| OLD | NEW |
| 1 | 1 |
| 2 (function(scope) { | 2 (function(scope) { |
| 3 var MoreRouting = scope.MoreRouting = scope.MoreRouting || {}; | 3 var MoreRouting = scope.MoreRouting = scope.MoreRouting || {}; |
| 4 MoreRouting.Route = Route; | 4 MoreRouting.Route = Route; |
| 5 | 5 |
| 6 // Note that this can differ from the part separator defined by the driver. The | 6 // Note that this can differ from the part separator defined by the driver. The |
| 7 // driver's separator is used when parsing/generating URLs given to the client, | 7 // driver's separator is used when parsing/generating URLs given to the client, |
| 8 // whereas this one is for route definitions. | 8 // whereas this one is for route definitions. |
| 9 var PART_SEPARATOR = '/'; | 9 var PART_SEPARATOR = '/'; |
| 10 var PARAM_SENTINEL = ':'; | 10 var PARAM_SENTINEL = ':'; |
| 11 var SEPARATOR_CLEANER = /\/\/+/g; | 11 var SEPARATOR_CLEANER = /\/\/+/g; |
| 12 | 12 |
| 13 /** | 13 /** |
| 14 * TODO(nevir): Docs. | 14 * TODO(nevir): Docs. |
| 15 */ | 15 */ |
| 16 function Route(path, parent) { | 16 function Route(path, parent) { |
| 17 // For `MoreRouting.Emitter`; Emits changes for `active`. |
| 18 this.__listeners = []; |
| 19 |
| 17 this.path = path; | 20 this.path = path; |
| 18 this.parent = parent; | 21 this.parent = parent; |
| 19 this.fullPath = path; | 22 this.fullPath = path; |
| 20 this.compiled = this._compile(this.path); | 23 this.compiled = this._compile(this.path); |
| 21 this.active = false; | 24 this.active = false; |
| 22 this.driver = null; | 25 this.driver = null; |
| 23 | 26 |
| 27 var params = MoreRouting.Params(namedParams(this.compiled), this.parent && thi
s.parent.params); |
| 28 params.__subscribe(this._navigateToParams.bind(this)); |
| 29 Object.defineProperty(this, 'params', { |
| 30 get: function() { return params; }, |
| 31 set: function() { throw new Error('Route#params cannot be overwritten'); }, |
| 32 }); |
| 33 |
| 24 this.parts = []; | 34 this.parts = []; |
| 25 this.children = []; | 35 this.children = []; |
| 26 | 36 |
| 27 // Param values matching the current URL, or an empty object if not `active`. | 37 // Param values matching the current URL, or an empty object if not `active`. |
| 28 // | 38 // |
| 29 // To make data "binding" easy, `Route` guarantees that `params` will always | 39 // To make data "binding" easy, `Route` guarantees that `params` will always |
| 30 // be the same object; just make a reference to it. | 40 // be the same object; just make a reference to it. |
| 31 if (this.parent) { | 41 if (this.parent) { |
| 32 this.params = Object.create(this.parent.params); | |
| 33 this.parent.children.push(this); | 42 this.parent.children.push(this); |
| 34 this.fullPath = this.parent.fullPath + this.fullPath; | 43 this.fullPath = this.parent.fullPath + this.fullPath; |
| 35 this.depth = this.parent.depth + this.compiled.length; | 44 this.depth = this.parent.depth + this.compiled.length; |
| 36 this.numParams = this.parent.numParams + countParams(this.compiled); | 45 this.numParams = this.parent.numParams + countParams(this.compiled); |
| 37 } else { | 46 } else { |
| 38 this.params = {}; | |
| 39 this.depth = this.compiled.length; | 47 this.depth = this.compiled.length; |
| 40 this.numParams = countParams(this.compiled); | 48 this.numParams = countParams(this.compiled); |
| 41 } | 49 } |
| 50 } |
| 51 Route.prototype = Object.create(MoreRouting.Emitter); |
| 42 | 52 |
| 43 this._paramObserver = new ObjectObserver(this.params); | 53 Object.defineProperty(Route.prototype, 'active', { |
| 44 this._paramObserver.open(this._navigateToParams.bind(this)); | 54 get: function() { |
| 45 } | 55 return this._active; |
| 56 }, |
| 57 set: function(value) { |
| 58 if (value !== this._active); |
| 59 this._active = value; |
| 60 this.__notify('active', value); |
| 61 }, |
| 62 }); |
| 46 | 63 |
| 47 Route.isPath = function isPath(pathOrName) { | 64 Route.isPath = function isPath(pathOrName) { |
| 48 return pathOrName.indexOf(PART_SEPARATOR) === 0; | 65 return pathOrName.indexOf(PART_SEPARATOR) === 0; |
| 49 }; | 66 }; |
| 50 | 67 |
| 51 Route.joinPath = function joinPath(paths) { | 68 Route.joinPath = function joinPath(paths) { |
| 52 var joined = Array.prototype.join.call(arguments, PART_SEPARATOR); | 69 var joined = Array.prototype.join.call(arguments, PART_SEPARATOR); |
| 53 joined = joined.replace(SEPARATOR_CLEANER, PART_SEPARATOR); | 70 joined = joined.replace(SEPARATOR_CLEANER, PART_SEPARATOR); |
| 54 | 71 |
| 55 var minLength = joined.length - PART_SEPARATOR.length; | 72 var minLength = joined.length - PART_SEPARATOR.length; |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 108 | 125 |
| 109 /** | 126 /** |
| 110 * Called by the driver whenever it has detected a change to the URL. | 127 * Called by the driver whenever it has detected a change to the URL. |
| 111 * | 128 * |
| 112 * @param {Array.<String>|null} parts The parts of the URL, or null if the | 129 * @param {Array.<String>|null} parts The parts of the URL, or null if the |
| 113 * route should be disabled. | 130 * route should be disabled. |
| 114 */ | 131 */ |
| 115 Route.prototype.processPathParts = function processPathParts(parts) { | 132 Route.prototype.processPathParts = function processPathParts(parts) { |
| 116 this.parts = parts; | 133 this.parts = parts; |
| 117 this.active = this.matchesPathParts(parts); | 134 this.active = this.matchesPathParts(parts); |
| 135 |
| 136 // We don't want to notify of these changes; they'd be no-op noise. |
| 137 this.params.__silent = true; |
| 138 |
| 118 if (this.active) { | 139 if (this.active) { |
| 119 var keys = Object.keys(this.params); | 140 var keys = Object.keys(this.params); |
| 120 for (var i = 0; i < keys.length; i++) { | 141 for (var i = 0; i < keys.length; i++) { |
| 121 delete this.params[keys[i]]; | 142 delete this.params[keys[i]]; |
| 122 } | 143 } |
| 123 for (var i = 0, config; config = this.compiled[i]; i++) { | 144 for (var i = 0, config; config = this.compiled[i]; i++) { |
| 124 if (config.type === 'param') { | 145 if (config.type === 'param') { |
| 125 this.params[config.name] = parts[i]; | 146 this.params[config.name] = parts[i]; |
| 126 } | 147 } |
| 127 } | 148 } |
| 128 } else { | 149 } else { |
| 129 for (key in this.params) { | 150 for (key in this.params) { |
| 130 this.params[key] = undefined; | 151 this.params[key] = undefined; |
| 131 } | 152 } |
| 132 } | 153 } |
| 133 this._paramObserver.discardChanges(); | 154 |
| 155 delete this.params.__silent; |
| 134 }; | 156 }; |
| 135 | 157 |
| 136 Route.prototype.matchesPathParts = function matchesPathParts(parts) { | 158 Route.prototype.matchesPathParts = function matchesPathParts(parts) { |
| 137 if (!parts) return false; | 159 if (!parts) return false; |
| 138 if (parts.length < this.compiled.length) return false; | 160 if (parts.length < this.compiled.length) return false; |
| 139 for (var i = 0, config; config = this.compiled[i]; i++) { | 161 for (var i = 0, config; config = this.compiled[i]; i++) { |
| 140 if (config.type === 'static' && parts[i] !== config.part) { | 162 if (config.type === 'static' && parts[i] !== config.part) { |
| 141 return false; | 163 return false; |
| 142 } | 164 } |
| 143 } | 165 } |
| (...skipping 29 matching lines...) Expand all Loading... |
| 173 if (!parts) return; | 195 if (!parts) return; |
| 174 this.driver.navigateToParts(parts); | 196 this.driver.navigateToParts(parts); |
| 175 }; | 197 }; |
| 176 | 198 |
| 177 function countParams(compiled) { | 199 function countParams(compiled) { |
| 178 return compiled.reduce(function(count, part) { | 200 return compiled.reduce(function(count, part) { |
| 179 return count + (part.type === 'param' ? 1 : 0); | 201 return count + (part.type === 'param' ? 1 : 0); |
| 180 }, 0); | 202 }, 0); |
| 181 } | 203 } |
| 182 | 204 |
| 205 function namedParams(compiled) { |
| 206 var result = []; |
| 207 compiled.forEach(function(part) { |
| 208 if (part.type === 'static') return; |
| 209 result.push(part.name); |
| 210 }); |
| 211 return result; |
| 212 } |
| 213 |
| 183 })(window); | 214 })(window); |
| OLD | NEW |