| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * @typedef {{ | 6 * @typedef {{ |
| 7 * dialog: (string|undefined), | 7 * dialog: (string|undefined), |
| 8 * page: string, | 8 * page: string, |
| 9 * section: string, | 9 * section: string, |
| 10 * subpage: !Array<string>, | 10 * subpage: !Array<string>, |
| 11 * url: (string|undefined), | |
| 12 * }} | 11 * }} |
| 13 */ | 12 */ |
| 14 var SettingsRoute; | 13 var SettingsRoute; |
| 15 | 14 |
| 15 /** @typedef {SettingsRoute|{url: string}} */ |
| 16 var CanonicalRoute; |
| 17 |
| 18 /** @typedef {SettingsRoute|{inHistory: boolean}} */ |
| 19 var HistoricRoute; |
| 20 |
| 16 /** | 21 /** |
| 17 * @fileoverview | 22 * @fileoverview |
| 18 * 'settings-router' is a simple router for settings. Its responsibilities: | 23 * 'settings-router' is a simple router for settings. Its responsibilities: |
| 19 * - Update the URL when the routing state changes. | 24 * - Update the URL when the routing state changes. |
| 20 * - Initialize the routing state with the initial URL. | 25 * - Initialize the routing state with the initial URL. |
| 21 * - Process and validate all routing state changes. | 26 * - Process and validate all routing state changes. |
| 22 * | 27 * |
| 23 * Example: | 28 * Example: |
| 24 * | 29 * |
| 25 * <settings-router current-route="{{currentRoute}}"> | 30 * <settings-router current-route="{{currentRoute}}"> |
| (...skipping 16 matching lines...) Expand all Loading... |
| 42 * the user is on. The previous elements are the ancestor subpages. This | 47 * the user is on. The previous elements are the ancestor subpages. This |
| 43 * enables support for multiple paths to the same subpage. This is used by | 48 * enables support for multiple paths to the same subpage. This is used by |
| 44 * both the Back button and the Breadcrumb to determine ancestor subpages. | 49 * both the Back button and the Breadcrumb to determine ancestor subpages. |
| 45 * @type {SettingsRoute} | 50 * @type {SettingsRoute} |
| 46 */ | 51 */ |
| 47 currentRoute: { | 52 currentRoute: { |
| 48 notify: true, | 53 notify: true, |
| 49 observer: 'currentRouteChanged_', | 54 observer: 'currentRouteChanged_', |
| 50 type: Object, | 55 type: Object, |
| 51 value: function() { | 56 value: function() { |
| 57 var initialRoute = this.canonicalRoutes_[0]; |
| 58 |
| 52 // Take the current URL, find a matching pre-defined route, and | 59 // Take the current URL, find a matching pre-defined route, and |
| 53 // initialize the currentRoute to that pre-defined route. | 60 // initialize the currentRoute to that pre-defined route. |
| 54 for (var i = 0; i < this.routes_.length; ++i) { | 61 for (var i = 0; i < this.canonicalRoutes_.length; ++i) { |
| 55 var route = this.routes_[i]; | 62 var canonicalRoute = this.canonicalRoutes_[i]; |
| 56 if (route.url == window.location.pathname) { | 63 if (canonicalRoute.url == window.location.pathname) { |
| 57 return { | 64 initialRoute = canonicalRoute; |
| 58 url: route.url, | 65 break; |
| 59 page: route.page, | |
| 60 section: route.section, | |
| 61 subpage: route.subpage, | |
| 62 dialog: route.dialog, | |
| 63 }; | |
| 64 } | 66 } |
| 65 } | 67 } |
| 66 | 68 |
| 67 // As a fallback return the default route. | 69 return { |
| 68 return this.routes_[0]; | 70 page: initialRoute.page, |
| 71 section: initialRoute.section, |
| 72 subpage: initialRoute.subpage, |
| 73 dialog: initialRoute.dialog, |
| 74 }; |
| 69 }, | 75 }, |
| 70 }, | 76 }, |
| 71 | 77 |
| 72 /** | 78 /** |
| 73 * Page titles for the currently active route. Updated by the currentRoute | 79 * Page titles for the currently active route. Updated by the currentRoute |
| 74 * property observer. | 80 * property observer. |
| 75 * @type {{pageTitle: string}} | 81 * @type {{pageTitle: string}} |
| 76 */ | 82 */ |
| 77 currentRouteTitles: { | 83 currentRouteTitles: { |
| 78 notify: true, | 84 notify: true, |
| 79 type: Object, | 85 type: Object, |
| 80 value: function() { | 86 value: function() { |
| 81 return { | 87 return { |
| 82 pageTitle: '', | 88 pageTitle: '', |
| 83 }; | 89 }; |
| 84 }, | 90 }, |
| 85 }, | 91 }, |
| 86 }, | 92 }, |
| 87 | 93 |
| 88 | 94 |
| 89 /** | 95 /** |
| 90 * @private {!Array<!SettingsRoute>} | 96 * @private {!Array<!CanonicalRoute>} |
| 91 * The 'url' property is not accessible to other elements. | 97 * The 'url' property is not accessible to other elements. |
| 92 */ | 98 */ |
| 93 routes_: [ | 99 canonicalRoutes_: [ |
| 94 { | 100 { |
| 95 url: '/', | 101 url: '/', |
| 96 page: 'basic', | 102 page: 'basic', |
| 97 section: '', | 103 section: '', |
| 98 subpage: [], | 104 subpage: [], |
| 99 }, | 105 }, |
| 100 { | 106 { |
| 101 url: '/help', | 107 url: '/help', |
| 102 page: 'about', | 108 page: 'about', |
| 103 section: '', | 109 section: '', |
| (...skipping 480 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 584 | 590 |
| 585 /** | 591 /** |
| 586 * Is called when another element modifies the route. This observer validates | 592 * Is called when another element modifies the route. This observer validates |
| 587 * the route change against the pre-defined list of routes, and updates the | 593 * the route change against the pre-defined list of routes, and updates the |
| 588 * URL appropriately. | 594 * URL appropriately. |
| 589 * @param {!SettingsRoute} newRoute Where we're headed. | 595 * @param {!SettingsRoute} newRoute Where we're headed. |
| 590 * @param {!SettingsRoute|undefined} oldRoute Where we've been. | 596 * @param {!SettingsRoute|undefined} oldRoute Where we've been. |
| 591 * @private | 597 * @private |
| 592 */ | 598 */ |
| 593 currentRouteChanged_: function(newRoute, oldRoute) { | 599 currentRouteChanged_: function(newRoute, oldRoute) { |
| 594 for (var i = 0; i < this.routes_.length; ++i) { | 600 for (var i = 0; i < this.canonicalRoutes_.length; ++i) { |
| 595 var route = this.routes_[i]; | 601 var canonicalRoute = this.canonicalRoutes_[i]; |
| 596 if (route.page == newRoute.page && | 602 if (canonicalRoute.page == newRoute.page && |
| 597 route.section == newRoute.section && | 603 canonicalRoute.section == newRoute.section && |
| 598 route.dialog == newRoute.dialog && | 604 canonicalRoute.dialog == newRoute.dialog && |
| 599 route.subpage.length == newRoute.subpage.length && | 605 canonicalRoute.subpage.length == newRoute.subpage.length && |
| 600 newRoute.subpage.every(function(value, index) { | 606 canonicalRoute.subpage.every(function(value, index) { |
| 601 return value == route.subpage[index]; | 607 return value == newRoute.subpage[index]; |
| 602 })) { | 608 })) { |
| 603 // Update the property containing the titles for the current route. | 609 // Update the property containing the titles for the current route. |
| 604 this.currentRouteTitles = { | 610 this.currentRouteTitles = { |
| 605 pageTitle: loadTimeData.getString(route.page + 'PageTitle'), | 611 pageTitle: loadTimeData.getString(canonicalRoute.page + 'PageTitle'), |
| 606 }; | 612 }; |
| 607 | 613 |
| 608 // If we are restoring a state from history, don't push it again. | 614 // If we are restoring a state from history, don't push it again. |
| 609 if (newRoute.inHistory) | 615 if (/** @type {HistoricRoute} */(newRoute).inHistory) |
| 610 return; | 616 return; |
| 611 | 617 |
| 612 // Mark routes persisted in history as already stored in history. | 618 // Mark routes persisted in history as already stored in history. |
| 613 var historicState = { | 619 var historicRoute = /** @type {HistoricRoute} */({ |
| 614 inHistory: true, | 620 inHistory: true, |
| 615 page: newRoute.page, | 621 page: newRoute.page, |
| 616 section: newRoute.section, | 622 section: newRoute.section, |
| 617 subpage: newRoute.subpage, | 623 subpage: newRoute.subpage, |
| 618 dialog: newRoute.dialog, | 624 dialog: newRoute.dialog, |
| 619 }; | 625 }); |
| 620 | 626 |
| 621 // Push the current route to the history state, so when the user | 627 // Push the current route to the history state, so when the user |
| 622 // navigates with the browser back button, we can recall the route. | 628 // navigates with the browser back button, we can recall the route. |
| 623 if (oldRoute) { | 629 if (oldRoute) { |
| 624 window.history.pushState(historicState, document.title, route.url); | 630 window.history.pushState(historicRoute, document.title, |
| 631 canonicalRoute.url); |
| 625 } else { | 632 } else { |
| 626 // For the very first route (oldRoute will be undefined), we replace | 633 // For the very first route (oldRoute will be undefined), we replace |
| 627 // the existing state instead of pushing a new one. This is to allow | 634 // the existing state instead of pushing a new one. This is to allow |
| 628 // the user to use the browser back button to exit Settings entirely. | 635 // the user to use the browser back button to exit Settings entirely. |
| 629 window.history.replaceState(historicState, document.title); | 636 window.history.replaceState(historicRoute, document.title); |
| 630 } | 637 } |
| 631 | 638 |
| 632 return; | 639 return; |
| 633 } | 640 } |
| 634 } | 641 } |
| 635 | 642 |
| 636 assertNotReached('Route not found: ' + JSON.stringify(newRoute)); | 643 assertNotReached('Route not found: ' + JSON.stringify(newRoute)); |
| 637 }, | 644 }, |
| 638 }); | 645 }); |
| OLD | NEW |