Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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 /** | |
| 6 * Specifies available routes in settings. | |
|
dpapad
2017/07/06 02:07:06
Nit: Can we clarify this comment a bit?
s/availabl
scottchen
2017/07/07 20:58:45
Done.
| |
| 7 * | |
| 8 * @typedef {{ | |
| 9 * ABOUT: (undefined|!settings.Route), | |
| 10 * ABOUT_ABOUT: (undefined|!settings.Route), | |
| 11 * ACCESSIBILITY: (undefined|!settings.Route), | |
| 12 * ACCOUNTS: (undefined|!settings.Route), | |
| 13 * ADVANCED: (undefined|!settings.Route), | |
| 14 * ANDROID_APPS: (undefined|!settings.Route), | |
| 15 * ANDROID_APPS_DETAILS: (undefined|!settings.Route), | |
| 16 * APPEARANCE: (undefined|!settings.Route), | |
| 17 * AUTOFILL: (undefined|!settings.Route), | |
| 18 * BASIC: (undefined|!settings.Route), | |
| 19 * BLUETOOTH: (undefined|!settings.Route), | |
| 20 * BLUETOOTH_DEVICES: (undefined|!settings.Route), | |
| 21 * CERTIFICATES: (undefined|!settings.Route), | |
| 22 * CHANGE_PICTURE: (undefined|!settings.Route), | |
| 23 * CLEAR_BROWSER_DATA: (undefined|!settings.Route), | |
| 24 * CLOUD_PRINTERS: (undefined|!settings.Route), | |
| 25 * CUPS_PRINTER_DETAIL: (undefined|!settings.Route), | |
| 26 * CUPS_PRINTERS: (undefined|!settings.Route), | |
| 27 * DATETIME: (undefined|!settings.Route), | |
| 28 * DEFAULT_BROWSER: (undefined|!settings.Route), | |
| 29 * DETAILED_BUILD_INFO: (undefined|!settings.Route), | |
| 30 * DEVICE: (undefined|!settings.Route), | |
| 31 * DISPLAY: (undefined|!settings.Route), | |
| 32 * DOWNLOADS: (undefined|!settings.Route), | |
| 33 * EDIT_DICTIONARY: (undefined|!settings.Route), | |
| 34 * FINGERPRINT: (undefined|!settings.Route), | |
| 35 * FONTS: (undefined|!settings.Route), | |
| 36 * IMPORT_DATA: (undefined|!settings.Route), | |
| 37 * INPUT_METHODS: (undefined|!settings.Route), | |
| 38 * INTERNET: (undefined|!settings.Route), | |
| 39 * INTERNET_NETWORKS: (undefined|!settings.Route), | |
| 40 * KEYBOARD: (undefined|!settings.Route), | |
| 41 * KNOWN_NETWORKS: (undefined|!settings.Route), | |
| 42 * LANGUAGES: (undefined|!settings.Route), | |
| 43 * LOCK_SCREEN: (undefined|!settings.Route), | |
| 44 * MANAGE_ACCESSIBILITY: (undefined|!settings.Route), | |
| 45 * MANAGE_PASSWORDS: (undefined|!settings.Route), | |
| 46 * MANAGE_PROFILE: (undefined|!settings.Route), | |
| 47 * NETWORK_CONFIG: (undefined|!settings.Route), | |
| 48 * NETWORK_DETAIL: (undefined|!settings.Route), | |
| 49 * ON_STARTUP: (undefined|!settings.Route), | |
| 50 * PASSWORDS: (undefined|!settings.Route), | |
| 51 * PEOPLE: (undefined|!settings.Route), | |
| 52 * POINTERS: (undefined|!settings.Route), | |
| 53 * POWER: (undefined|!settings.Route), | |
| 54 * PRINTING: (undefined|!settings.Route), | |
| 55 * PRIVACY: (undefined|!settings.Route), | |
| 56 * RESET: (undefined|!settings.Route), | |
| 57 * RESET_DIALOG: (undefined|!settings.Route), | |
| 58 * SEARCH: (undefined|!settings.Route), | |
| 59 * SEARCH_ENGINES: (undefined|!settings.Route), | |
| 60 * SIGN_OUT: (undefined|!settings.Route), | |
| 61 * SITE_SETTINGS: (undefined|!settings.Route), | |
| 62 * SITE_SETTINGS_ADS: (undefined|!settings.Route), | |
| 63 * SITE_SETTINGS_ALL: (undefined|!settings.Route), | |
| 64 * SITE_SETTINGS_AUTOMATIC_DOWNLOADS: (undefined|!settings.Route), | |
| 65 * SITE_SETTINGS_BACKGROUND_SYNC: (undefined|!settings.Route), | |
| 66 * SITE_SETTINGS_CAMERA: (undefined|!settings.Route), | |
| 67 * SITE_SETTINGS_COOKIES: (undefined|!settings.Route), | |
| 68 * SITE_SETTINGS_DATA_DETAILS: (undefined|!settings.Route), | |
| 69 * SITE_SETTINGS_FLASH: (undefined|!settings.Route), | |
| 70 * SITE_SETTINGS_HANDLERS: (undefined|!settings.Route), | |
| 71 * SITE_SETTINGS_IMAGES: (undefined|!settings.Route), | |
| 72 * SITE_SETTINGS_JAVASCRIPT: (undefined|!settings.Route), | |
| 73 * SITE_SETTINGS_LOCATION: (undefined|!settings.Route), | |
| 74 * SITE_SETTINGS_MICROPHONE: (undefined|!settings.Route), | |
| 75 * SITE_SETTINGS_MIDI_DEVICES: (undefined|!settings.Route), | |
| 76 * SITE_SETTINGS_NOTIFICATIONS: (undefined|!settings.Route), | |
| 77 * SITE_SETTINGS_PDF_DOCUMENTS: (undefined|!settings.Route), | |
| 78 * SITE_SETTINGS_POPUPS: (undefined|!settings.Route), | |
| 79 * SITE_SETTINGS_PROTECTED_CONTENT: (undefined|!settings.Route), | |
| 80 * SITE_SETTINGS_SITE_DETAILS: (undefined|!settings.Route), | |
| 81 * SITE_SETTINGS_UNSANDBOXED_PLUGINS: (undefined|!settings.Route), | |
| 82 * SITE_SETTINGS_USB_DEVICES: (undefined|!settings.Route), | |
| 83 * SITE_SETTINGS_ZOOM_LEVELS: (undefined|!settings.Route), | |
| 84 * STORAGE: (undefined|!settings.Route), | |
| 85 * STYLUS: (undefined|!settings.Route), | |
| 86 * SYNC: (undefined|!settings.Route), | |
| 87 * SYSTEM: (undefined|!settings.Route), | |
| 88 * TRIGGERED_RESET_DIALOG: (undefined|!settings.Route), | |
| 89 * }} | |
| 90 */ | |
| 91 var SettingsRoutes; | |
| 92 | |
| 5 cr.define('settings', function() { | 93 cr.define('settings', function() { |
| 94 | |
| 6 /** | 95 /** |
| 7 * Class for navigable routes. May only be instantiated within this file. | 96 * Class for navigable routes. May only be instantiated within this file. |
| 8 * @constructor | |
| 9 * @param {string} path | |
| 10 * @private | |
| 11 */ | 97 */ |
| 12 var Route = function(path) { | 98 class Route { |
| 13 this.path = path; | 99 /** |
| 100 * @param {string} path | |
|
dpapad
2017/07/06 02:07:06
Nit: Maybe shorten as follows:
/** @param {string
scottchen
2017/07/07 20:58:44
Done.
| |
| 101 */ | |
| 102 constructor(path) { | |
| 103 /** @type {string} */ | |
| 104 this.path = path; | |
| 14 | 105 |
| 15 /** @type {?settings.Route} */ | 106 /** @type {?settings.Route} */ |
| 16 this.parent = null; | 107 this.parent = null; |
| 17 | 108 |
| 18 /** @type {number} */ | 109 /** @type {number} */ |
| 19 this.depth = 0; | 110 this.depth = 0; |
| 20 | 111 |
| 21 /** | 112 /** |
| 22 * @type {boolean} Whether this route corresponds to a navigable | 113 * @type {boolean} Whether this route corresponds to a navigable |
| 23 * dialog. Those routes don't belong to a "section". | 114 * dialog. Those routes don't belong to a "section". |
| 24 */ | 115 */ |
| 25 this.isNavigableDialog = false; | 116 this.isNavigableDialog = false; |
| 26 | 117 |
| 27 // Below are all legacy properties to provide compatibility with the old | 118 // Below are all legacy properties to provide compatibility with the old |
| 28 // routing system. | 119 // routing system. |
| 29 | 120 |
| 30 /** @type {string} */ | 121 /** @type {string} */ |
| 31 this.section = ''; | 122 this.section = ''; |
| 32 }; | 123 } |
| 33 | 124 |
| 34 Route.prototype = { | |
| 35 /** | 125 /** |
| 36 * Returns a new Route instance that's a child of this route. | 126 * Returns a new Route instance that's a child of this route. |
| 37 * @param {string} path Extends this route's path if it doesn't contain a | 127 * @param {string} path Extends this route's path if it doesn't contain a |
| 38 * leading slash. | 128 * leading slash. |
| 39 * @return {!settings.Route} | 129 * @return {!settings.Route} |
| 40 * @private | 130 * @private |
| 41 */ | 131 */ |
| 42 createChild: function(path) { | 132 createChild(path) { |
| 43 assert(path); | 133 assert(path); |
| 44 | 134 |
| 45 // |path| extends this route's path if it doesn't have a leading slash. | 135 // |path| extends this route's path if it doesn't have a leading slash. |
| 46 // If it does have a leading slash, it's just set as the new route's URL. | 136 // If it does have a leading slash, it's just set as the new route's URL. |
| 47 var newUrl = path[0] == '/' ? path : this.path + '/' + path; | 137 var newUrl = path[0] == '/' ? path : this.path + '/' + path; |
| 48 | 138 |
| 49 var route = new Route(newUrl); | 139 var route = new Route(newUrl); |
| 50 route.parent = this; | 140 route.parent = this; |
| 51 route.section = this.section; | 141 route.section = this.section; |
| 52 route.depth = this.depth + 1; | 142 route.depth = this.depth + 1; |
| 53 | 143 |
| 54 return route; | 144 return route; |
| 55 }, | 145 } |
| 56 | 146 |
| 57 /** | 147 /** |
| 58 * Returns a new Route instance that's a child section of this route. | 148 * Returns a new Route instance that's a child section of this route. |
| 59 * TODO(tommycli): Remove once we've obsoleted the concept of sections. | 149 * TODO(tommycli): Remove once we've obsoleted the concept of sections. |
| 60 * @param {string} path | 150 * @param {string} path |
| 61 * @param {string} section | 151 * @param {string} section |
| 62 * @return {!settings.Route} | 152 * @return {!settings.Route} |
| 63 * @private | 153 * @private |
| 64 */ | 154 */ |
| 65 createSection: function(path, section) { | 155 createSection(path, section) { |
| 66 var route = this.createChild(path); | 156 var route = this.createChild(path); |
| 67 route.section = section; | 157 route.section = section; |
| 68 return route; | 158 return route; |
| 69 }, | 159 } |
| 70 | 160 |
| 71 /** | 161 /** |
| 72 * Returns true if this route matches or is an ancestor of the parameter. | 162 * Returns true if this route matches or is an ancestor of the parameter. |
| 73 * @param {!settings.Route} route | 163 * @param {!settings.Route} route |
| 74 * @return {boolean} | 164 * @return {boolean} |
| 75 */ | 165 */ |
| 76 contains: function(route) { | 166 contains(route) { |
| 77 for (var r = route; r != null; r = r.parent) { | 167 for (var r = route; r != null; r = r.parent) { |
| 78 if (this == r) | 168 if (this == r) |
| 79 return true; | 169 return true; |
| 80 } | 170 } |
| 81 return false; | 171 return false; |
| 82 }, | 172 } |
| 83 | 173 |
| 84 /** | 174 /** |
| 85 * Returns true if this route is a subpage of a section. | 175 * Returns true if this route is a subpage of a section. |
| 86 * @return {boolean} | 176 * @return {boolean} |
| 87 */ | 177 */ |
| 88 isSubpage: function() { | 178 isSubpage() { |
| 89 return !!this.parent && !!this.section && | 179 return !!this.parent && !!this.section && |
| 90 this.parent.section == this.section; | 180 this.parent.section == this.section; |
| 91 }, | 181 } |
| 182 } | |
| 183 | |
| 184 class Router { | |
| 185 constructor() { | |
| 186 /** @private {!SettingsRoutes} */ | |
|
dpapad
2017/07/06 02:07:06
Let's add a comment to differentiate between route
scottchen
2017/07/07 20:58:44
Done.
| |
| 187 this.routes_ = {}; | |
|
dpapad
2017/07/06 02:07:06
Not sure if this is worth doing, but mentioning it
scottchen
2017/07/07 20:58:44
I was going to use Router_.initializeRoutes() in t
dpapad
2017/07/07 21:19:30
SG. Consider this optional.
| |
| 188 this.initializeRoutes(); | |
| 189 | |
| 190 /** | |
| 191 * The current active route. This updated is only by settings.navigateTo | |
| 192 * or settings.initializeRouteFromUrl. | |
| 193 * @type {!settings.Route} | |
| 194 */ | |
| 195 this.currentRoute = this.routes_.BASIC; | |
| 196 | |
| 197 /** | |
| 198 * The current query parameters. This is updated only by | |
| 199 * settings.navigateTo or settings.initializeRouteFromUrl. | |
| 200 * @private {!URLSearchParams} | |
| 201 */ | |
| 202 this.currentQueryParameters_ = new URLSearchParams(); | |
| 203 | |
| 204 /** @private {boolean} */ | |
| 205 this.wasLastRouteChangePopstate_ = false; | |
| 206 | |
| 207 /** @private {boolean}*/ | |
| 208 this.initializeRouteFromUrlCalled_ = false; | |
| 209 } | |
| 210 | |
| 211 initializeRoutes() { | |
|
dpapad
2017/07/06 02:07:06
Can this be private?
scottchen
2017/07/07 20:58:44
Moving this method out of the class definition.
| |
| 212 var pageVisibility = settings.pageVisibility || {}; | |
| 213 | |
| 214 // Abbreviated variable for easier definitions. | |
| 215 var r = this.routes_; | |
| 216 | |
| 217 // Root pages. | |
| 218 r.BASIC = new Route('/'); | |
| 219 r.ABOUT = new Route('/help'); | |
| 220 | |
| 221 // Navigable dialogs. These are the only non-section children of root | |
| 222 // pages. These are disfavored. If we add anymore, we should add explicit | |
| 223 // support. | |
| 224 r.IMPORT_DATA = r.BASIC.createChild('/importData'); | |
| 225 r.IMPORT_DATA.isNavigableDialog = true; | |
| 226 r.SIGN_OUT = r.BASIC.createChild('/signOut'); | |
| 227 r.SIGN_OUT.isNavigableDialog = true; | |
| 228 | |
| 229 // <if expr="chromeos"> | |
| 230 r.INTERNET = r.BASIC.createSection('/internet', 'internet'); | |
| 231 r.INTERNET_NETWORKS = r.INTERNET.createChild('/networks'); | |
| 232 r.NETWORK_CONFIG = r.INTERNET.createChild('/networkConfig'); | |
| 233 r.NETWORK_DETAIL = r.INTERNET.createChild('/networkDetail'); | |
| 234 r.KNOWN_NETWORKS = r.INTERNET.createChild('/knownNetworks'); | |
| 235 r.BLUETOOTH = r.BASIC.createSection('/bluetooth', 'bluetooth'); | |
| 236 r.BLUETOOTH_DEVICES = r.BLUETOOTH.createChild('/bluetoothDevices'); | |
| 237 // </if> | |
| 238 | |
| 239 if (pageVisibility.appearance !== false) { | |
| 240 r.APPEARANCE = r.BASIC.createSection('/appearance', 'appearance'); | |
| 241 r.FONTS = r.APPEARANCE.createChild('/fonts'); | |
| 242 } | |
| 243 | |
| 244 if (pageVisibility.defaultBrowser !== false) { | |
| 245 r.DEFAULT_BROWSER = | |
| 246 r.BASIC.createSection('/defaultBrowser', 'defaultBrowser'); | |
| 247 } | |
| 248 | |
| 249 r.SEARCH = r.BASIC.createSection('/search', 'search'); | |
| 250 r.SEARCH_ENGINES = r.SEARCH.createChild('/searchEngines'); | |
| 251 | |
| 252 // <if expr="chromeos"> | |
| 253 r.ANDROID_APPS = r.BASIC.createSection('/androidApps', 'androidApps'); | |
| 254 r.ANDROID_APPS_DETAILS = | |
| 255 r.ANDROID_APPS.createChild('/androidApps/details'); | |
| 256 // </if> | |
| 257 | |
| 258 if (pageVisibility.onStartup !== false) { | |
| 259 r.ON_STARTUP = r.BASIC.createSection('/onStartup', 'onStartup'); | |
| 260 } | |
| 261 | |
| 262 if (pageVisibility.people !== false) { | |
| 263 r.PEOPLE = r.BASIC.createSection('/people', 'people'); | |
| 264 r.SYNC = r.PEOPLE.createChild('/syncSetup'); | |
| 265 // <if expr="not chromeos"> | |
| 266 r.MANAGE_PROFILE = r.PEOPLE.createChild('/manageProfile'); | |
| 267 // </if> | |
| 268 // <if expr="chromeos"> | |
| 269 r.CHANGE_PICTURE = r.PEOPLE.createChild('/changePicture'); | |
| 270 r.ACCOUNTS = r.PEOPLE.createChild('/accounts'); | |
| 271 r.LOCK_SCREEN = r.PEOPLE.createChild('/lockScreen'); | |
| 272 r.FINGERPRINT = r.LOCK_SCREEN.createChild('/lockScreen/fingerprint'); | |
| 273 // </if> | |
| 274 } | |
| 275 | |
| 276 // <if expr="chromeos"> | |
| 277 r.DEVICE = r.BASIC.createSection('/device', 'device'); | |
| 278 r.POINTERS = r.DEVICE.createChild('/pointer-overlay'); | |
| 279 r.KEYBOARD = r.DEVICE.createChild('/keyboard-overlay'); | |
| 280 r.STYLUS = r.DEVICE.createChild('/stylus'); | |
| 281 r.DISPLAY = r.DEVICE.createChild('/display'); | |
| 282 r.STORAGE = r.DEVICE.createChild('/storage'); | |
| 283 r.POWER = r.DEVICE.createChild('/power'); | |
| 284 // </if> | |
| 285 | |
| 286 // Advacned Routes | |
| 287 if (pageVisibility.advancedSettings !== false) { | |
| 288 r.ADVANCED = new Route('/advanced'); | |
| 289 | |
| 290 r.CLEAR_BROWSER_DATA = r.ADVANCED.createChild('/clearBrowserData'); | |
| 291 r.CLEAR_BROWSER_DATA.isNavigableDialog = true; | |
| 292 | |
| 293 if (pageVisibility.privacy !== false) { | |
| 294 r.PRIVACY = r.ADVANCED.createSection('/privacy', 'privacy'); | |
| 295 r.CERTIFICATES = r.PRIVACY.createChild('/certificates'); | |
| 296 r.SITE_SETTINGS = r.PRIVACY.createChild('/content'); | |
| 297 } | |
| 298 | |
| 299 if (loadTimeData.getBoolean('enableSiteSettings')) { | |
| 300 r.SITE_SETTINGS_ALL = r.SITE_SETTINGS.createChild('all'); | |
| 301 r.SITE_SETTINGS_SITE_DETAILS = | |
| 302 r.SITE_SETTINGS_ALL.createChild('/content/siteDetails'); | |
| 303 } else if (loadTimeData.getBoolean('enableSiteDetails')) { | |
| 304 // When there is no "All Sites", pressing 'back' from "Site Details" | |
| 305 // should return to "Content Settings". This should only occur when | |
| 306 // |kSiteSettings| is off and |kSiteDetails| is on. | |
| 307 r.SITE_SETTINGS_SITE_DETAILS = | |
| 308 r.SITE_SETTINGS.createChild('/content/siteDetails'); | |
| 309 } | |
| 310 | |
| 311 r.SITE_SETTINGS_HANDLERS = r.SITE_SETTINGS.createChild('/handlers'); | |
| 312 | |
| 313 // TODO(tommycli): Find a way to refactor these repetitive category | |
| 314 // routes. | |
| 315 r.SITE_SETTINGS_ADS = r.SITE_SETTINGS.createChild('ads'); | |
| 316 r.SITE_SETTINGS_AUTOMATIC_DOWNLOADS = | |
| 317 r.SITE_SETTINGS.createChild('automaticDownloads'); | |
| 318 r.SITE_SETTINGS_BACKGROUND_SYNC = | |
| 319 r.SITE_SETTINGS.createChild('backgroundSync'); | |
| 320 r.SITE_SETTINGS_CAMERA = r.SITE_SETTINGS.createChild('camera'); | |
| 321 r.SITE_SETTINGS_COOKIES = r.SITE_SETTINGS.createChild('cookies'); | |
| 322 r.SITE_SETTINGS_DATA_DETAILS = | |
| 323 r.SITE_SETTINGS_COOKIES.createChild('/cookies/detail'); | |
| 324 r.SITE_SETTINGS_IMAGES = r.SITE_SETTINGS.createChild('images'); | |
| 325 r.SITE_SETTINGS_JAVASCRIPT = r.SITE_SETTINGS.createChild('javascript'); | |
| 326 r.SITE_SETTINGS_LOCATION = r.SITE_SETTINGS.createChild('location'); | |
| 327 r.SITE_SETTINGS_MICROPHONE = r.SITE_SETTINGS.createChild('microphone'); | |
| 328 r.SITE_SETTINGS_NOTIFICATIONS = | |
| 329 r.SITE_SETTINGS.createChild('notifications'); | |
| 330 r.SITE_SETTINGS_FLASH = r.SITE_SETTINGS.createChild('flash'); | |
| 331 r.SITE_SETTINGS_POPUPS = r.SITE_SETTINGS.createChild('popups'); | |
| 332 r.SITE_SETTINGS_UNSANDBOXED_PLUGINS = | |
| 333 r.SITE_SETTINGS.createChild('unsandboxedPlugins'); | |
| 334 r.SITE_SETTINGS_MIDI_DEVICES = | |
| 335 r.SITE_SETTINGS.createChild('midiDevices'); | |
| 336 r.SITE_SETTINGS_USB_DEVICES = r.SITE_SETTINGS.createChild('usbDevices'); | |
| 337 r.SITE_SETTINGS_ZOOM_LEVELS = r.SITE_SETTINGS.createChild('zoomLevels'); | |
| 338 r.SITE_SETTINGS_PDF_DOCUMENTS = | |
| 339 r.SITE_SETTINGS.createChild('pdfDocuments'); | |
| 340 r.SITE_SETTINGS_PROTECTED_CONTENT = | |
| 341 r.SITE_SETTINGS.createChild('protectedContent'); | |
| 342 | |
| 343 // <if expr="chromeos"> | |
| 344 if (pageVisibility.dateTime !== false) { | |
| 345 r.DATETIME = r.ADVANCED.createSection('/dateTime', 'dateTime'); | |
| 346 } | |
| 347 // </if> | |
| 348 | |
| 349 if (pageVisibility.passwordsAndForms !== false) { | |
| 350 r.PASSWORDS = r.ADVANCED.createSection( | |
| 351 '/passwordsAndForms', 'passwordsAndForms'); | |
| 352 r.AUTOFILL = r.PASSWORDS.createChild('/autofill'); | |
| 353 r.MANAGE_PASSWORDS = r.PASSWORDS.createChild('/passwords'); | |
| 354 } | |
| 355 | |
| 356 r.LANGUAGES = r.ADVANCED.createSection('/languages', 'languages'); | |
| 357 // <if expr="chromeos"> | |
| 358 r.INPUT_METHODS = r.LANGUAGES.createChild('/inputMethods'); | |
| 359 // </if> | |
| 360 // <if expr="not is_macosx"> | |
| 361 r.EDIT_DICTIONARY = r.LANGUAGES.createChild('/editDictionary'); | |
| 362 // </if> | |
| 363 | |
| 364 if (pageVisibility.downloads !== false) { | |
| 365 r.DOWNLOADS = r.ADVANCED.createSection('/downloads', 'downloads'); | |
| 366 } | |
| 367 | |
| 368 r.PRINTING = r.ADVANCED.createSection('/printing', 'printing'); | |
| 369 r.CLOUD_PRINTERS = r.PRINTING.createChild('/cloudPrinters'); | |
| 370 // <if expr="chromeos"> | |
| 371 r.CUPS_PRINTERS = r.PRINTING.createChild('/cupsPrinters'); | |
| 372 r.CUPS_PRINTER_DETAIL = | |
| 373 r.CUPS_PRINTERS.createChild('/cupsPrinterDetails'); | |
| 374 // </if> | |
| 375 | |
| 376 r.ACCESSIBILITY = r.ADVANCED.createSection('/accessibility', 'a11y'); | |
| 377 // <if expr="chromeos"> | |
| 378 r.MANAGE_ACCESSIBILITY = | |
| 379 r.ACCESSIBILITY.createChild('/manageAccessibility'); | |
| 380 // </if> | |
| 381 | |
| 382 r.SYSTEM = r.ADVANCED.createSection('/system', 'system'); | |
| 383 | |
| 384 if (pageVisibility.reset !== false) { | |
| 385 r.RESET = r.ADVANCED.createSection('/reset', 'reset'); | |
| 386 r.RESET_DIALOG = r.ADVANCED.createChild('/resetProfileSettings'); | |
| 387 r.RESET_DIALOG.isNavigableDialog = true; | |
| 388 r.TRIGGERED_RESET_DIALOG = | |
| 389 r.ADVANCED.createChild('/triggeredResetProfileSettings'); | |
| 390 r.TRIGGERED_RESET_DIALOG.isNavigableDialog = true; | |
| 391 } | |
| 392 } | |
| 393 | |
| 394 // <if expr="chromeos"> | |
| 395 // "About" is the only section in About, but we still need to create the | |
| 396 // route in order to show the subpage on Chrome OS. | |
| 397 r.ABOUT_ABOUT = r.ABOUT.createSection('/help/about', 'about'); | |
| 398 r.DETAILED_BUILD_INFO = r.ABOUT_ABOUT.createChild('/help/details'); | |
| 399 // </if> | |
| 400 } | |
| 401 | |
| 402 /** @return {settings.Route} */ | |
| 403 getRoute(routeName) { | |
| 404 return this.routes_[routeName]; | |
| 405 } | |
| 406 | |
| 407 /** @return {!SettingsRoutes} */ | |
| 408 getRoutes() { | |
| 409 return this.routes_; | |
| 410 } | |
| 411 | |
| 412 /** | |
| 413 * Helper function to set the current route and notify all observers. | |
| 414 * @param {!settings.Route} route | |
| 415 * @param {!URLSearchParams} queryParameters | |
| 416 * @param {boolean} isPopstate | |
| 417 */ | |
| 418 setCurrentRoute(route, queryParameters, isPopstate) { | |
| 419 var oldRoute = this.currentRoute; | |
| 420 this.currentRoute = route; | |
| 421 this.currentQueryParameters_ = queryParameters; | |
| 422 this.wasLastRouteChangePopstate_ = isPopstate; | |
| 423 routeObservers.forEach(function(observer) { | |
| 424 observer.currentRouteChanged(this.currentRoute, oldRoute); | |
| 425 }.bind(this)); | |
| 426 } | |
| 427 | |
| 428 /** @return {!settings.Route} */ | |
| 429 getCurrentRoute() { | |
| 430 return this.currentRoute; | |
| 431 } | |
| 432 | |
| 433 /** @return {!URLSearchParams} */ | |
| 434 getQueryParameters() { | |
| 435 return new URLSearchParams( | |
| 436 this.currentQueryParameters_); // Defensive copy. | |
| 437 } | |
| 438 | |
| 439 /** @return {boolean} */ | |
| 440 lastRouteChangeWasPopstate() { | |
| 441 return this.wasLastRouteChangePopstate_; | |
| 442 } | |
| 443 | |
| 444 /** | |
| 445 * @param {string} path | |
| 446 * @return {?settings.Route} The matching canonical route, or null if none | |
| 447 * matches. | |
| 448 */ | |
| 449 getRouteForPath(path) { | |
| 450 // Allow trailing slash in paths. | |
| 451 var canonicalPath = path.replace(CANONICAL_PATH_REGEX, '$1$2'); | |
| 452 | |
| 453 // TODO(tommycli): Use Object.values once Closure compilation supports it. | |
| 454 var matchingKey = Object.keys(this.routes_).find(function(key) { | |
| 455 return this.routes_[key].path == canonicalPath; | |
| 456 }.bind(this)); | |
| 457 | |
| 458 return !!matchingKey ? this.routes_[matchingKey] : null; | |
| 459 } | |
| 460 | |
| 461 /** | |
| 462 * Navigates to a canonical route and pushes a new history entry. | |
| 463 * @param {!settings.Route} route | |
| 464 * @param {URLSearchParams=} opt_dynamicParameters Navigations to the same | |
| 465 * URL parameters in a different order will still push to history. | |
| 466 * @param {boolean=} opt_removeSearch Whether to strip the 'search' URL | |
| 467 * parameter during navigation. Defaults to false. | |
| 468 */ | |
| 469 navigateTo(route, opt_dynamicParameters, opt_removeSearch) { | |
| 470 // The ADVANCED route only serves as a parent of subpages, and should not | |
| 471 // be possible to navigate to it directly. | |
| 472 if (route == this.routes_.ADVANCED) | |
| 473 route = /** @type {!settings.Route} */ (this.routes_.BASIC); | |
| 474 | |
| 475 var params = opt_dynamicParameters || new URLSearchParams(); | |
| 476 var removeSearch = !!opt_removeSearch; | |
| 477 | |
| 478 var oldSearchParam = this.getQueryParameters().get('search') || ''; | |
| 479 var newSearchParam = params.get('search') || ''; | |
| 480 | |
| 481 if (!removeSearch && oldSearchParam && !newSearchParam) | |
| 482 params.append('search', oldSearchParam); | |
| 483 | |
| 484 var url = route.path; | |
| 485 var queryString = params.toString(); | |
| 486 if (queryString) | |
| 487 url += '?' + queryString; | |
| 488 | |
| 489 // History serializes the state, so we don't push the actual route object. | |
| 490 window.history.pushState(this.currentRoute.path, '', url); | |
| 491 this.setCurrentRoute(route, params, false); | |
| 492 } | |
| 493 | |
| 494 /** | |
| 495 * Navigates to the previous route if it has an equal or lesser depth. | |
| 496 * If there is no previous route in history meeting those requirements, | |
| 497 * this navigates to the immediate parent. This will never exit Settings. | |
| 498 */ | |
| 499 navigateToPreviousRoute() { | |
| 500 var previousRoute = window.history.state && | |
| 501 assert(this.getRouteForPath( | |
| 502 /** @type {string} */ (window.history.state))); | |
| 503 | |
| 504 if (previousRoute && previousRoute.depth <= this.currentRoute.depth) | |
| 505 window.history.back(); | |
| 506 else | |
| 507 this.navigateTo( | |
| 508 this.currentRoute.parent || | |
| 509 /** @type {!settings.Route} */ (this.routes_.BASIC)); | |
| 510 } | |
| 511 | |
| 512 /** | |
| 513 * Initialize the route and query params from the URL. | |
| 514 */ | |
| 515 initializeRouteFromUrl() { | |
| 516 assert(!this.initializeRouteFromUrlCalled_); | |
| 517 this.initializeRouteFromUrlCalled_ = true; | |
| 518 | |
| 519 var route = this.getRouteForPath(window.location.pathname); | |
| 520 // Never allow direct navigation to ADVANCED. | |
| 521 if (route && route != this.routes_.ADVANCED) { | |
| 522 this.currentRoute = route; | |
| 523 this.currentQueryParameters_ = | |
| 524 new URLSearchParams(window.location.search); | |
| 525 } else { | |
| 526 window.history.replaceState(undefined, '', this.routes_.BASIC.path); | |
| 527 } | |
| 528 } | |
| 529 | |
| 530 resetRouteForTesting() { | |
| 531 this.initializeRouteFromUrlCalled_ = false; | |
| 532 this.wasLastRouteChangePopstate_ = false; | |
| 533 this.currentRoute = /** @type {!settings.Route} */ (this.routes_.BASIC); | |
| 534 this.currentQueryParameters_ = new URLSearchParams(); | |
| 535 } | |
| 536 } | |
| 537 | |
| 538 var Router_ = new Router(); | |
|
dpapad
2017/07/06 02:07:06
Typically only class names start with a capital le
scottchen
2017/07/07 20:58:45
Done. Changed to routerInstance because apparently
| |
| 539 | |
| 540 var getRouter = function() { | |
|
dpapad
2017/07/06 02:07:06
Is this used anywhere?
scottchen
2017/07/07 20:58:45
not anymore, removing.
| |
| 541 return Router_; | |
| 92 }; | 542 }; |
| 93 | 543 |
| 94 // Abbreviated variable for easier definitions. | |
| 95 var r = Route; | |
| 96 | |
| 97 // Root pages. | |
| 98 r.BASIC = new Route('/'); | |
| 99 r.ADVANCED = new Route('/advanced'); | |
| 100 r.ABOUT = new Route('/help'); | |
| 101 | |
| 102 // Navigable dialogs. These are the only non-section children of root pages. | |
| 103 // These are disfavored. If we add anymore, we should add explicit support. | |
| 104 r.IMPORT_DATA = r.BASIC.createChild('/importData'); | |
| 105 r.IMPORT_DATA.isNavigableDialog = true; | |
| 106 r.SIGN_OUT = r.BASIC.createChild('/signOut'); | |
| 107 r.SIGN_OUT.isNavigableDialog = true; | |
| 108 r.CLEAR_BROWSER_DATA = r.ADVANCED.createChild('/clearBrowserData'); | |
| 109 r.CLEAR_BROWSER_DATA.isNavigableDialog = true; | |
| 110 r.RESET_DIALOG = r.ADVANCED.createChild('/resetProfileSettings'); | |
| 111 r.RESET_DIALOG.isNavigableDialog = true; | |
| 112 r.TRIGGERED_RESET_DIALOG = | |
| 113 r.ADVANCED.createChild('/triggeredResetProfileSettings'); | |
| 114 r.TRIGGERED_RESET_DIALOG.isNavigableDialog = true; | |
| 115 | |
| 116 // <if expr="chromeos"> | |
| 117 r.INTERNET = r.BASIC.createSection('/internet', 'internet'); | |
| 118 r.INTERNET_NETWORKS = r.INTERNET.createChild('/networks'); | |
| 119 r.NETWORK_CONFIG = r.INTERNET.createChild('/networkConfig'); | |
| 120 r.NETWORK_DETAIL = r.INTERNET.createChild('/networkDetail'); | |
| 121 r.KNOWN_NETWORKS = r.INTERNET.createChild('/knownNetworks'); | |
| 122 r.BLUETOOTH = r.BASIC.createSection('/bluetooth', 'bluetooth'); | |
| 123 r.BLUETOOTH_DEVICES = r.BLUETOOTH.createChild('/bluetoothDevices'); | |
| 124 // </if> | |
| 125 | |
| 126 r.APPEARANCE = r.BASIC.createSection('/appearance', 'appearance'); | |
| 127 r.FONTS = r.APPEARANCE.createChild('/fonts'); | |
| 128 | |
| 129 r.DEFAULT_BROWSER = | |
| 130 r.BASIC.createSection('/defaultBrowser', 'defaultBrowser'); | |
| 131 | |
| 132 r.SEARCH = r.BASIC.createSection('/search', 'search'); | |
| 133 r.SEARCH_ENGINES = r.SEARCH.createChild('/searchEngines'); | |
| 134 | |
| 135 // <if expr="chromeos"> | |
| 136 r.ANDROID_APPS = r.BASIC.createSection('/androidApps', 'androidApps'); | |
| 137 r.ANDROID_APPS_DETAILS = r.ANDROID_APPS.createChild('/androidApps/details'); | |
| 138 // </if> | |
| 139 | |
| 140 r.ON_STARTUP = r.BASIC.createSection('/onStartup', 'onStartup'); | |
| 141 | |
| 142 r.PEOPLE = r.BASIC.createSection('/people', 'people'); | |
| 143 r.SYNC = r.PEOPLE.createChild('/syncSetup'); | |
| 144 // <if expr="not chromeos"> | |
| 145 r.MANAGE_PROFILE = r.PEOPLE.createChild('/manageProfile'); | |
| 146 // </if> | |
| 147 // <if expr="chromeos"> | |
| 148 r.CHANGE_PICTURE = r.PEOPLE.createChild('/changePicture'); | |
| 149 r.ACCOUNTS = r.PEOPLE.createChild('/accounts'); | |
| 150 r.LOCK_SCREEN = r.PEOPLE.createChild('/lockScreen'); | |
| 151 r.FINGERPRINT = r.LOCK_SCREEN.createChild('/lockScreen/fingerprint'); | |
| 152 | |
| 153 r.DEVICE = r.BASIC.createSection('/device', 'device'); | |
| 154 r.POINTERS = r.DEVICE.createChild('/pointer-overlay'); | |
| 155 r.KEYBOARD = r.DEVICE.createChild('/keyboard-overlay'); | |
| 156 r.STYLUS = r.DEVICE.createChild('/stylus'); | |
| 157 r.DISPLAY = r.DEVICE.createChild('/display'); | |
| 158 r.STORAGE = r.DEVICE.createChild('/storage'); | |
| 159 r.POWER = r.DEVICE.createChild('/power'); | |
| 160 // </if> | |
| 161 | |
| 162 r.PRIVACY = r.ADVANCED.createSection('/privacy', 'privacy'); | |
| 163 r.CERTIFICATES = r.PRIVACY.createChild('/certificates'); | |
| 164 | |
| 165 r.SITE_SETTINGS = r.PRIVACY.createChild('/content'); | |
| 166 | |
| 167 if (loadTimeData.getBoolean('enableSiteSettings')) { | |
| 168 r.SITE_SETTINGS_ALL = r.SITE_SETTINGS.createChild('all'); | |
| 169 r.SITE_SETTINGS_SITE_DETAILS = | |
| 170 r.SITE_SETTINGS_ALL.createChild('/content/siteDetails'); | |
| 171 } else if (loadTimeData.getBoolean('enableSiteDetails')) { | |
| 172 // When there is no "All Sites", pressing 'back' from "Site Details" should | |
| 173 // return to "Content Settings". This should only occur when |kSiteSettings| | |
| 174 // is off and |kSiteDetails| is on. | |
| 175 r.SITE_SETTINGS_SITE_DETAILS = | |
| 176 r.SITE_SETTINGS.createChild('/content/siteDetails'); | |
| 177 } | |
| 178 | |
| 179 r.SITE_SETTINGS_HANDLERS = r.SITE_SETTINGS.createChild('/handlers'); | |
| 180 | |
| 181 // TODO(tommycli): Find a way to refactor these repetitive category routes. | |
| 182 r.SITE_SETTINGS_ADS = r.SITE_SETTINGS.createChild('ads'); | |
| 183 r.SITE_SETTINGS_AUTOMATIC_DOWNLOADS = | |
| 184 r.SITE_SETTINGS.createChild('automaticDownloads'); | |
| 185 r.SITE_SETTINGS_BACKGROUND_SYNC = | |
| 186 r.SITE_SETTINGS.createChild('backgroundSync'); | |
| 187 r.SITE_SETTINGS_CAMERA = r.SITE_SETTINGS.createChild('camera'); | |
| 188 r.SITE_SETTINGS_COOKIES = r.SITE_SETTINGS.createChild('cookies'); | |
| 189 r.SITE_SETTINGS_DATA_DETAILS = | |
| 190 r.SITE_SETTINGS_COOKIES.createChild('/cookies/detail'); | |
| 191 r.SITE_SETTINGS_IMAGES = r.SITE_SETTINGS.createChild('images'); | |
| 192 r.SITE_SETTINGS_JAVASCRIPT = r.SITE_SETTINGS.createChild('javascript'); | |
| 193 r.SITE_SETTINGS_LOCATION = r.SITE_SETTINGS.createChild('location'); | |
| 194 r.SITE_SETTINGS_MICROPHONE = r.SITE_SETTINGS.createChild('microphone'); | |
| 195 r.SITE_SETTINGS_NOTIFICATIONS = r.SITE_SETTINGS.createChild('notifications'); | |
| 196 r.SITE_SETTINGS_FLASH = r.SITE_SETTINGS.createChild('flash'); | |
| 197 r.SITE_SETTINGS_POPUPS = r.SITE_SETTINGS.createChild('popups'); | |
| 198 r.SITE_SETTINGS_UNSANDBOXED_PLUGINS = | |
| 199 r.SITE_SETTINGS.createChild('unsandboxedPlugins'); | |
| 200 r.SITE_SETTINGS_MIDI_DEVICES = r.SITE_SETTINGS.createChild('midiDevices'); | |
| 201 r.SITE_SETTINGS_USB_DEVICES = r.SITE_SETTINGS.createChild('usbDevices'); | |
| 202 r.SITE_SETTINGS_ZOOM_LEVELS = r.SITE_SETTINGS.createChild('zoomLevels'); | |
| 203 r.SITE_SETTINGS_PDF_DOCUMENTS = r.SITE_SETTINGS.createChild('pdfDocuments'); | |
| 204 r.SITE_SETTINGS_PROTECTED_CONTENT = | |
| 205 r.SITE_SETTINGS.createChild('protectedContent'); | |
| 206 | |
| 207 // <if expr="chromeos"> | |
| 208 r.DATETIME = r.ADVANCED.createSection('/dateTime', 'dateTime'); | |
| 209 // </if> | |
| 210 | |
| 211 r.PASSWORDS = | |
| 212 r.ADVANCED.createSection('/passwordsAndForms', 'passwordsAndForms'); | |
| 213 r.AUTOFILL = r.PASSWORDS.createChild('/autofill'); | |
| 214 r.MANAGE_PASSWORDS = r.PASSWORDS.createChild('/passwords'); | |
| 215 | |
| 216 r.LANGUAGES = r.ADVANCED.createSection('/languages', 'languages'); | |
| 217 // <if expr="chromeos"> | |
| 218 r.INPUT_METHODS = r.LANGUAGES.createChild('/inputMethods'); | |
| 219 // </if> | |
| 220 // <if expr="not is_macosx"> | |
| 221 r.EDIT_DICTIONARY = r.LANGUAGES.createChild('/editDictionary'); | |
| 222 // </if> | |
| 223 | |
| 224 r.DOWNLOADS = r.ADVANCED.createSection('/downloads', 'downloads'); | |
| 225 | |
| 226 r.PRINTING = r.ADVANCED.createSection('/printing', 'printing'); | |
| 227 r.CLOUD_PRINTERS = r.PRINTING.createChild('/cloudPrinters'); | |
| 228 // <if expr="chromeos"> | |
| 229 r.CUPS_PRINTERS = r.PRINTING.createChild('/cupsPrinters'); | |
| 230 r.CUPS_PRINTER_DETAIL = r.CUPS_PRINTERS.createChild('/cupsPrinterDetails'); | |
| 231 // </if> | |
| 232 | |
| 233 r.ACCESSIBILITY = r.ADVANCED.createSection('/accessibility', 'a11y'); | |
| 234 // <if expr="chromeos"> | |
| 235 r.MANAGE_ACCESSIBILITY = r.ACCESSIBILITY.createChild('/manageAccessibility'); | |
| 236 // </if> | |
| 237 | |
| 238 r.SYSTEM = r.ADVANCED.createSection('/system', 'system'); | |
| 239 r.RESET = r.ADVANCED.createSection('/reset', 'reset'); | |
| 240 | |
| 241 // <if expr="chromeos"> | |
| 242 // "About" is the only section in About, but we still need to create the route | |
| 243 // in order to show the subpage on Chrome OS. | |
| 244 r.ABOUT_ABOUT = r.ABOUT.createSection('/help/about', 'about'); | |
| 245 r.DETAILED_BUILD_INFO = r.ABOUT_ABOUT.createChild('/help/details'); | |
| 246 // </if> | |
| 247 | |
| 248 var routeObservers = new Set(); | 544 var routeObservers = new Set(); |
| 249 | 545 |
| 250 /** @polymerBehavior */ | 546 /** @polymerBehavior */ |
| 251 var RouteObserverBehavior = { | 547 var RouteObserverBehavior = { |
| 252 /** @override */ | 548 /** @override */ |
| 253 attached: function() { | 549 attached: function() { |
| 254 assert(!routeObservers.has(this)); | 550 assert(!routeObservers.has(this)); |
| 255 routeObservers.add(this); | 551 routeObservers.add(this); |
| 256 | 552 |
| 257 // Emulating Polymer data bindings, the observer is called when the | 553 // Emulating Polymer data bindings, the observer is called when the |
| 258 // element starts observing the route. | 554 // element starts observing the route. |
| 259 this.currentRouteChanged(currentRoute, undefined); | 555 this.currentRouteChanged(Router_.currentRoute, undefined); |
| 260 }, | 556 }, |
| 261 | 557 |
| 262 /** @override */ | 558 /** @override */ |
| 263 detached: function() { | 559 detached: function() { |
| 264 assert(routeObservers.delete(this)); | 560 assert(routeObservers.delete(this)); |
| 265 }, | 561 }, |
| 266 | 562 |
| 267 /** | 563 /** |
| 268 * @param {!settings.Route|undefined} opt_newRoute | 564 * @param {!settings.Route|undefined} opt_newRoute |
| 269 * @param {!settings.Route|undefined} opt_oldRoute | 565 * @param {!settings.Route|undefined} opt_oldRoute |
| 270 * @abstract | 566 * @abstract |
| 271 */ | 567 */ |
| 272 currentRouteChanged: function(opt_newRoute, opt_oldRoute) { | 568 currentRouteChanged: function(opt_newRoute, opt_oldRoute) { |
| 273 assertNotReached(); | 569 assertNotReached(); |
| 274 }, | 570 }, |
| 275 }; | 571 }; |
| 276 | 572 |
| 277 /** | 573 /** |
| 278 * Regular expression that captures the leading slash, the content and the | 574 * Regular expression that captures the leading slash, the content and the |
| 279 * trailing slash in three different groups. | 575 * trailing slash in three different groups. |
| 280 * @const {!RegExp} | 576 * @const {!RegExp} |
| 281 */ | 577 */ |
| 282 var CANONICAL_PATH_REGEX = /(^\/)([\/-\w]+)(\/$)/; | 578 var CANONICAL_PATH_REGEX = /(^\/)([\/-\w]+)(\/$)/; |
| 283 | 579 |
| 284 /** | |
| 285 * @param {string} path | |
| 286 * @return {?settings.Route} The matching canonical route, or null if none | |
| 287 * matches. | |
| 288 */ | |
| 289 var getRouteForPath = function(path) { | |
| 290 // Allow trailing slash in paths. | |
| 291 var canonicalPath = path.replace(CANONICAL_PATH_REGEX, '$1$2'); | |
| 292 | |
| 293 // TODO(tommycli): Use Object.values once Closure compilation supports it. | |
| 294 var matchingKey = Object.keys(Route).find(function(key) { | |
| 295 return Route[key].path == canonicalPath; | |
| 296 }); | |
| 297 | |
| 298 return !!matchingKey ? Route[matchingKey] : null; | |
| 299 }; | |
| 300 | |
| 301 /** | |
| 302 * The current active route. This updated is only by settings.navigateTo or | |
| 303 * settings.initializeRouteFromUrl. | |
| 304 * @private {!settings.Route} | |
| 305 */ | |
| 306 var currentRoute = Route.BASIC; | |
| 307 | |
| 308 /** | |
| 309 * The current query parameters. This is updated only by settings.navigateTo | |
| 310 * or settings.initializeRouteFromUrl. | |
| 311 * @private {!URLSearchParams} | |
| 312 */ | |
| 313 var currentQueryParameters = new URLSearchParams(); | |
| 314 | |
| 315 /** @private {boolean} */ | |
| 316 var wasLastRouteChangePopstate = false; | |
| 317 | |
| 318 /** @private */ | |
| 319 var initializeRouteFromUrlCalled = false; | |
| 320 | |
| 321 /** | |
| 322 * Initialize the route and query params from the URL. | |
| 323 */ | |
| 324 var initializeRouteFromUrl = function() { | |
| 325 assert(!initializeRouteFromUrlCalled); | |
| 326 initializeRouteFromUrlCalled = true; | |
| 327 | |
| 328 var route = getRouteForPath(window.location.pathname); | |
| 329 // Never allow direct navigation to ADVANCED. | |
| 330 if (route && route != Route.ADVANCED) { | |
| 331 currentRoute = route; | |
| 332 currentQueryParameters = new URLSearchParams(window.location.search); | |
| 333 } else { | |
| 334 window.history.replaceState(undefined, '', Route.BASIC.path); | |
| 335 } | |
| 336 }; | |
| 337 | |
| 338 function resetRouteForTesting() { | |
| 339 initializeRouteFromUrlCalled = false; | |
| 340 wasLastRouteChangePopstate = false; | |
| 341 currentRoute = Route.BASIC; | |
| 342 currentQueryParameters = new URLSearchParams(); | |
| 343 } | |
| 344 | |
| 345 /** | |
| 346 * Helper function to set the current route and notify all observers. | |
| 347 * @param {!settings.Route} route | |
| 348 * @param {!URLSearchParams} queryParameters | |
| 349 * @param {boolean} isPopstate | |
| 350 */ | |
| 351 var setCurrentRoute = function(route, queryParameters, isPopstate) { | |
| 352 var oldRoute = currentRoute; | |
| 353 currentRoute = route; | |
| 354 currentQueryParameters = queryParameters; | |
| 355 wasLastRouteChangePopstate = isPopstate; | |
| 356 routeObservers.forEach(function(observer) { | |
| 357 observer.currentRouteChanged(currentRoute, oldRoute); | |
| 358 }); | |
| 359 }; | |
| 360 | |
| 361 /** @return {!settings.Route} */ | |
| 362 var getCurrentRoute = function() { | |
| 363 return currentRoute; | |
| 364 }; | |
| 365 | |
| 366 /** @return {!URLSearchParams} */ | |
| 367 var getQueryParameters = function() { | |
| 368 return new URLSearchParams(currentQueryParameters); // Defensive copy. | |
| 369 }; | |
| 370 | |
| 371 /** @return {boolean} */ | |
| 372 var lastRouteChangeWasPopstate = function() { | |
| 373 return wasLastRouteChangePopstate; | |
| 374 }; | |
| 375 | |
| 376 /** | |
| 377 * Navigates to a canonical route and pushes a new history entry. | |
| 378 * @param {!settings.Route} route | |
| 379 * @param {URLSearchParams=} opt_dynamicParameters Navigations to the same | |
| 380 * URL parameters in a different order will still push to history. | |
| 381 * @param {boolean=} opt_removeSearch Whether to strip the 'search' URL | |
| 382 * parameter during navigation. Defaults to false. | |
| 383 */ | |
| 384 var navigateTo = function(route, opt_dynamicParameters, opt_removeSearch) { | |
| 385 // The ADVANCED route only serves as a parent of subpages, and should not | |
| 386 // be possible to navigate to it directly. | |
| 387 if (route == settings.Route.ADVANCED) | |
| 388 route = settings.Route.BASIC; | |
| 389 | |
| 390 var params = opt_dynamicParameters || new URLSearchParams(); | |
| 391 var removeSearch = !!opt_removeSearch; | |
| 392 | |
| 393 var oldSearchParam = getQueryParameters().get('search') || ''; | |
| 394 var newSearchParam = params.get('search') || ''; | |
| 395 | |
| 396 if (!removeSearch && oldSearchParam && !newSearchParam) | |
| 397 params.append('search', oldSearchParam); | |
| 398 | |
| 399 var url = route.path; | |
| 400 var queryString = params.toString(); | |
| 401 if (queryString) | |
| 402 url += '?' + queryString; | |
| 403 | |
| 404 // History serializes the state, so we don't push the actual route object. | |
| 405 window.history.pushState(currentRoute.path, '', url); | |
| 406 setCurrentRoute(route, params, false); | |
| 407 }; | |
| 408 | |
| 409 /** | |
| 410 * Navigates to the previous route if it has an equal or lesser depth. | |
| 411 * If there is no previous route in history meeting those requirements, | |
| 412 * this navigates to the immediate parent. This will never exit Settings. | |
| 413 */ | |
| 414 var navigateToPreviousRoute = function() { | |
| 415 var previousRoute = window.history.state && | |
| 416 assert(getRouteForPath(/** @type {string} */ (window.history.state))); | |
| 417 | |
| 418 if (previousRoute && previousRoute.depth <= currentRoute.depth) | |
| 419 window.history.back(); | |
| 420 else | |
| 421 navigateTo(currentRoute.parent || Route.BASIC); | |
| 422 }; | |
| 423 | |
| 424 window.addEventListener('popstate', function(event) { | 580 window.addEventListener('popstate', function(event) { |
| 425 // On pop state, do not push the state onto the window.history again. | 581 // On pop state, do not push the state onto the window.history again. |
| 426 setCurrentRoute( | 582 Router_.setCurrentRoute( |
| 427 getRouteForPath(window.location.pathname) || Route.BASIC, | 583 /** @type {!settings.Route} */ ( |
| 584 Router_.getRouteForPath(window.location.pathname) || | |
| 585 Router_.getRoutes().BASIC), | |
| 428 new URLSearchParams(window.location.search), true); | 586 new URLSearchParams(window.location.search), true); |
| 429 }); | 587 }); |
| 430 | 588 |
| 589 // TODO(scottchen): Change to 'get routes() {}' in export when we figure | |
| 590 // out why doing so causes a closure compiler error. | |
|
dpapad
2017/07/06 02:07:06
Maybe update the comment to mention our newest fin
scottchen
2017/07/07 20:58:45
Done.
| |
| 591 var routes = Router_.getRoutes(); | |
| 592 | |
|
dpapad
2017/07/06 02:07:06
Maybe add a TODO as follows?
"Stop exposing all t
scottchen
2017/07/07 20:58:45
Done.
| |
| 593 var getCurrentRoute = Router_.getCurrentRoute.bind(Router_); | |
| 594 var getRouteForPath = Router_.getRouteForPath.bind(Router_); | |
| 595 var initializeRouteFromUrl = Router_.initializeRouteFromUrl.bind(Router_); | |
| 596 var resetRouteForTesting = Router_.resetRouteForTesting.bind(Router_); | |
| 597 var getQueryParameters = Router_.getQueryParameters.bind(Router_); | |
| 598 var lastRouteChangeWasPopstate = | |
| 599 Router_.lastRouteChangeWasPopstate.bind(Router_); | |
| 600 var navigateTo = Router_.navigateTo.bind(Router_); | |
| 601 var navigateToPreviousRoute = Router_.navigateToPreviousRoute.bind(Router_); | |
| 602 | |
| 431 return { | 603 return { |
| 432 Route: Route, | 604 Route: Route, // The class definition. |
| 605 Router: Router, // The class definition. | |
| 606 router: Router_, // the singleton. | |
| 607 routes: routes, | |
| 433 RouteObserverBehavior: RouteObserverBehavior, | 608 RouteObserverBehavior: RouteObserverBehavior, |
| 434 getRouteForPath: getRouteForPath, | 609 getRouteForPath: getRouteForPath, |
| 435 initializeRouteFromUrl: initializeRouteFromUrl, | 610 initializeRouteFromUrl: initializeRouteFromUrl, |
| 436 resetRouteForTesting: resetRouteForTesting, | 611 resetRouteForTesting: resetRouteForTesting, |
| 437 getCurrentRoute: getCurrentRoute, | 612 getCurrentRoute: getCurrentRoute, |
| 438 getQueryParameters: getQueryParameters, | 613 getQueryParameters: getQueryParameters, |
| 439 lastRouteChangeWasPopstate: lastRouteChangeWasPopstate, | 614 lastRouteChangeWasPopstate: lastRouteChangeWasPopstate, |
| 440 navigateTo: navigateTo, | 615 navigateTo: navigateTo, |
| 441 navigateToPreviousRoute: navigateToPreviousRoute, | 616 navigateToPreviousRoute: navigateToPreviousRoute, |
| 442 }; | 617 }; |
| 443 }); | 618 }); |
| OLD | NEW |