| OLD | NEW |
| (Empty) |
| 1 <!-- | |
| 2 Copyright (c) 2015 The Polymer Project Authors. All rights reserved. | |
| 3 This code may only be used under the BSD style license found at http://polymer.g
ithub.io/LICENSE.txt | |
| 4 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt | |
| 5 The complete set of contributors may be found at http://polymer.github.io/CONTRI
BUTORS.txt | |
| 6 Code distributed by Google as part of the polymer project is also | |
| 7 subject to an additional IP rights grant found at http://polymer.github.io/PATEN
TS.txt | |
| 8 --> | |
| 9 <link rel="import" href="emitter.html"> | |
| 10 <link rel="import" href="params.html"> | |
| 11 | |
| 12 <script> | |
| 13 (function(scope) { | |
| 14 var MoreRouting = scope.MoreRouting = scope.MoreRouting || {}; | |
| 15 MoreRouting.Route = Route; | |
| 16 | |
| 17 // Note that this can differ from the part separator defined by the driver. The | |
| 18 // driver's separator is used when parsing/generating URLs given to the client, | |
| 19 // whereas this one is for route definitions. | |
| 20 var PART_SEPARATOR = '/'; | |
| 21 var PARAM_SENTINEL = ':'; | |
| 22 var SEPARATOR_CLEANER = /\/\/+/g; | |
| 23 | |
| 24 /** | |
| 25 * TODO(nevir): Docs. | |
| 26 */ | |
| 27 function Route(path, parent) { | |
| 28 // For `MoreRouting.Emitter`; Emits changes for `active`. | |
| 29 this.__listeners = []; | |
| 30 | |
| 31 this.path = path; | |
| 32 this.parent = parent; | |
| 33 this.fullPath = path; | |
| 34 this.compiled = this._compile(this.path); | |
| 35 this.active = false; | |
| 36 this.driver = null; | |
| 37 | |
| 38 var params = MoreRouting.Params(namedParams(this.compiled), this.parent && thi
s.parent.params); | |
| 39 params.__subscribe(this._navigateToParams.bind(this)); | |
| 40 Object.defineProperty(this, 'params', { | |
| 41 get: function() { return params; }, | |
| 42 set: function() { throw new Error('Route#params cannot be overwritten'); }, | |
| 43 }); | |
| 44 | |
| 45 this.parts = []; | |
| 46 this.children = []; | |
| 47 | |
| 48 // Param values matching the current URL, or an empty object if not `active`. | |
| 49 // | |
| 50 // To make data "binding" easy, `Route` guarantees that `params` will always | |
| 51 // be the same object; just make a reference to it. | |
| 52 if (this.parent) { | |
| 53 this.parent.children.push(this); | |
| 54 this.fullPath = this.parent.fullPath + this.fullPath; | |
| 55 this.depth = this.parent.depth + this.compiled.length; | |
| 56 this.numParams = this.parent.numParams + countParams(this.compiled); | |
| 57 } else { | |
| 58 this.depth = this.compiled.length; | |
| 59 this.numParams = countParams(this.compiled); | |
| 60 } | |
| 61 } | |
| 62 Route.prototype = Object.create(MoreRouting.Emitter); | |
| 63 | |
| 64 Object.defineProperty(Route.prototype, 'active', { | |
| 65 get: function() { | |
| 66 return this._active; | |
| 67 }, | |
| 68 set: function(value) { | |
| 69 if (value !== this._active); | |
| 70 this._active = value; | |
| 71 this.__notify('active', value); | |
| 72 }, | |
| 73 }); | |
| 74 | |
| 75 Route.isPath = function isPath(pathOrName) { | |
| 76 return pathOrName.indexOf(PART_SEPARATOR) === 0; | |
| 77 }; | |
| 78 | |
| 79 Route.joinPath = function joinPath(paths) { | |
| 80 var joined = Array.prototype.join.call(arguments, PART_SEPARATOR); | |
| 81 joined = joined.replace(SEPARATOR_CLEANER, PART_SEPARATOR); | |
| 82 | |
| 83 var minLength = joined.length - PART_SEPARATOR.length; | |
| 84 if (joined.substr(minLength) === PART_SEPARATOR) { | |
| 85 joined = joined.substr(0, minLength); | |
| 86 } | |
| 87 | |
| 88 return joined; | |
| 89 }; | |
| 90 | |
| 91 Route.prototype.urlFor = function urlFor(params) { | |
| 92 return this.driver.urlForParts(this.partsForParams(params)); | |
| 93 }; | |
| 94 | |
| 95 Route.prototype.navigateTo = function navigateTo(params) { | |
| 96 return this.driver.navigateToParts(this.partsForParams(params)); | |
| 97 } | |
| 98 | |
| 99 Route.prototype.isCurrentUrl = function isCurrentUrl(params) { | |
| 100 if (!this.active) return false; | |
| 101 var currentKeys = Object.keys(this.params); | |
| 102 for (var i = 0, key; key = currentKeys[i]; i++) { | |
| 103 if (this.params[key] !== String(params[key])) { | |
| 104 return false; | |
| 105 } | |
| 106 } | |
| 107 return true; | |
| 108 }; | |
| 109 | |
| 110 // Driver Interface | |
| 111 | |
| 112 Route.prototype.partsForParams = function partsForParams(params, silent) { | |
| 113 var parts = this.parent && this.parent.partsForParams(params, silent) || []; | |
| 114 for (var i = 0, config; config = this.compiled[i]; i++) { | |
| 115 if (config.type === 'static') { | |
| 116 parts.push(config.part); | |
| 117 } else if (config.type === 'param') { | |
| 118 var value | |
| 119 if (params && config.name in params) { | |
| 120 value = params[config.name]; | |
| 121 } else { | |
| 122 value = this.params[config.name]; | |
| 123 } | |
| 124 if (value === undefined) { | |
| 125 if (silent) { | |
| 126 return null; | |
| 127 } else { | |
| 128 throw new Error('Missing param "' + config.name + '" for route ' + thi
s); | |
| 129 } | |
| 130 } | |
| 131 parts.push(value); | |
| 132 } | |
| 133 } | |
| 134 return parts; | |
| 135 }; | |
| 136 | |
| 137 /** | |
| 138 * Called by the driver whenever it has detected a change to the URL. | |
| 139 * | |
| 140 * @param {Array.<String>|null} parts The parts of the URL, or null if the | |
| 141 * route should be disabled. | |
| 142 */ | |
| 143 Route.prototype.processPathParts = function processPathParts(parts) { | |
| 144 this.parts = parts; | |
| 145 this.active = this.matchesPathParts(parts); | |
| 146 | |
| 147 // We don't want to notify of these changes; they'd be no-op noise. | |
| 148 this.params.__silent = true; | |
| 149 | |
| 150 if (this.active) { | |
| 151 var keys = Object.keys(this.params); | |
| 152 for (var i = 0; i < keys.length; i++) { | |
| 153 delete this.params[keys[i]]; | |
| 154 } | |
| 155 for (var i = 0, config; config = this.compiled[i]; i++) { | |
| 156 if (config.type === 'param') { | |
| 157 this.params[config.name] = parts[i]; | |
| 158 } | |
| 159 } | |
| 160 } else { | |
| 161 for (key in this.params) { | |
| 162 this.params[key] = undefined; | |
| 163 } | |
| 164 } | |
| 165 | |
| 166 delete this.params.__silent; | |
| 167 }; | |
| 168 | |
| 169 Route.prototype.matchesPathParts = function matchesPathParts(parts) { | |
| 170 if (!parts) return false; | |
| 171 if (parts.length < this.compiled.length) return false; | |
| 172 for (var i = 0, config; config = this.compiled[i]; i++) { | |
| 173 if (config.type === 'static' && parts[i] !== config.part) { | |
| 174 return false; | |
| 175 } | |
| 176 } | |
| 177 return true; | |
| 178 }; | |
| 179 | |
| 180 Route.prototype.toString = function toString() { | |
| 181 return this.path; | |
| 182 }; | |
| 183 | |
| 184 // Internal Implementation | |
| 185 | |
| 186 Route.prototype._compile = function _compile(rawPath) { | |
| 187 // Not strictly required, but helps us stay consistent w/ `getRoute`, etc. | |
| 188 if (rawPath.indexOf(PART_SEPARATOR) !== 0) { | |
| 189 throw new Error('Route paths must begin with a path separator; got: "' + raw
Path + '"'); | |
| 190 } | |
| 191 var path = rawPath.substr(PART_SEPARATOR.length); | |
| 192 if (path === '') return []; | |
| 193 | |
| 194 return path.split(PART_SEPARATOR).map(function(part) { | |
| 195 // raw fragment. | |
| 196 if (part.substr(0, 1) == PARAM_SENTINEL) { | |
| 197 return {type: 'param', name: part.substr(1)}; | |
| 198 } else { | |
| 199 return {type: 'static', part: part}; | |
| 200 } | |
| 201 }); | |
| 202 }; | |
| 203 | |
| 204 Route.prototype._navigateToParams = function _navigateToParams() { | |
| 205 var parts = this.partsForParams(this.params, true); | |
| 206 if (!parts) return; | |
| 207 this.driver.navigateToParts(parts); | |
| 208 }; | |
| 209 | |
| 210 function countParams(compiled) { | |
| 211 return compiled.reduce(function(count, part) { | |
| 212 return count + (part.type === 'param' ? 1 : 0); | |
| 213 }, 0); | |
| 214 } | |
| 215 | |
| 216 function namedParams(compiled) { | |
| 217 var result = []; | |
| 218 compiled.forEach(function(part) { | |
| 219 if (part.type === 'static') return; | |
| 220 result.push(part.name); | |
| 221 }); | |
| 222 return result; | |
| 223 } | |
| 224 | |
| 225 })(window); | |
| 226 </script> | |
| OLD | NEW |