Service workers: Allow HTTPS pages arrived at via HTTP redirect to use SW
[chromium-blink-merge.git] / third_party / polymer / v1_0 / components-chromium / more-routing / route-extracted.js
blob839fb72a0809b974fb950ef2088693fb5e1d37e4
1 (function(scope) {
2 var MoreRouting = scope.MoreRouting = scope.MoreRouting || {};
3 MoreRouting.Route = Route;
5 // Note that this can differ from the part separator defined by the driver. The
6 // driver's separator is used when parsing/generating URLs given to the client,
7 // whereas this one is for route definitions.
8 var PART_SEPARATOR    = '/';
9 var PARAM_SENTINEL    = ':';
10 var SEPARATOR_CLEANER = /\/\/+/g;
12 /**
13  * TODO(nevir): Docs.
14  */
15 function Route(path, parent) {
16   // For `MoreRouting.Emitter`; Emits changes for `active`.
17   this.__listeners = [];
19   this.path     = path;
20   this.parent   = parent;
21   this.fullPath = path;
22   this.compiled = this._compile(this.path);
23   this.active   = false;
24   this.driver   = null;
26   var params = MoreRouting.Params(namedParams(this.compiled), this.parent && this.parent.params);
27   params.__subscribe(this._navigateToParams.bind(this));
28   Object.defineProperty(this, 'params', {
29     get: function() { return params; },
30     set: function() { throw new Error('Route#params cannot be overwritten'); },
31   });
33   this.parts    = [];
34   this.children = [];
36   // Param values matching the current URL, or an empty object if not `active`.
37   //
38   // To make data "binding" easy, `Route` guarantees that `params` will always
39   // be the same object; just make a reference to it.
40   if (this.parent) {
41     this.parent.children.push(this);
42     this.fullPath  = this.parent.fullPath + this.fullPath;
43     this.depth     = this.parent.depth + this.compiled.length;
44     this.numParams = this.parent.numParams + countParams(this.compiled);
45   } else {
46     this.depth     = this.compiled.length;
47     this.numParams = countParams(this.compiled);
48   }
50 Route.prototype = Object.create(MoreRouting.Emitter);
52 Object.defineProperty(Route.prototype, 'active', {
53   get: function() {
54     return this._active;
55   },
56   set: function(value) {
57     if (value !== this._active);
58     this._active = value;
59     this.__notify('active', value);
60   },
61 });
63 Route.isPath = function isPath(pathOrName) {
64   return pathOrName.indexOf(PART_SEPARATOR) === 0;
67 Route.joinPath = function joinPath(paths) {
68   var joined = Array.prototype.join.call(arguments, PART_SEPARATOR);
69   joined = joined.replace(SEPARATOR_CLEANER, PART_SEPARATOR);
71   var minLength = joined.length - PART_SEPARATOR.length;
72   if (joined.substr(minLength) === PART_SEPARATOR) {
73     joined = joined.substr(0, minLength);
74   }
76   return joined;
79 Route.prototype.urlFor = function urlFor(params) {
80   return this.driver.urlForParts(this.partsForParams(params));
83 Route.prototype.navigateTo = function navigateTo(params) {
84   return this.driver.navigateToParts(this.partsForParams(params));
87 Route.prototype.isCurrentUrl = function isCurrentUrl(params) {
88   if (!this.active) return false;
89   var currentKeys = Object.keys(this.params);
90   for (var i = 0, key; key = currentKeys[i]; i++) {
91     if (this.params[key] !== String(params[key])) {
92       return false;
93     }
94   }
95   return true;
98 // Driver Interface
100 Route.prototype.partsForParams = function partsForParams(params, silent) {
101   var parts = this.parent && this.parent.partsForParams(params, silent) || [];
102   for (var i = 0, config; config = this.compiled[i]; i++) {
103     if (config.type === 'static') {
104       parts.push(config.part);
105     } else if (config.type === 'param') {
106       var value
107       if (params && config.name in params) {
108         value = params[config.name];
109       } else {
110         value = this.params[config.name];
111       }
112       if (value === undefined) {
113         if (silent) {
114           return null;
115         } else {
116           throw new Error('Missing param "' + config.name + '" for route ' + this);
117         }
118       }
119       parts.push(value);
120     }
121   }
122   return parts;
126  * Called by the driver whenever it has detected a change to the URL.
128  * @param {Array.<String>|null} parts The parts of the URL, or null if the
129  *     route should be disabled.
130  */
131 Route.prototype.processPathParts = function processPathParts(parts) {
132   this.parts  = parts;
133   this.active = this.matchesPathParts(parts);
135   // We don't want to notify of these changes; they'd be no-op noise.
136   this.params.__silent = true;
138   if (this.active) {
139     var keys = Object.keys(this.params);
140     for (var i = 0; i < keys.length; i++) {
141       delete this.params[keys[i]];
142     }
143     for (var i = 0, config; config = this.compiled[i]; i++) {
144       if (config.type === 'param') {
145         this.params[config.name] = parts[i];
146       }
147     }
148   } else {
149     for (key in this.params) {
150       this.params[key] = undefined;
151     }
152   }
154   delete this.params.__silent;
157 Route.prototype.matchesPathParts = function matchesPathParts(parts) {
158   if (!parts) return false;
159   if (parts.length < this.compiled.length) return false;
160   for (var i = 0, config; config = this.compiled[i]; i++) {
161     if (config.type === 'static' && parts[i] !== config.part) {
162       return false;
163     }
164   }
165   return true;
168 Route.prototype.toString = function toString() {
169   return this.path;
172 // Internal Implementation
174 Route.prototype._compile = function _compile(rawPath) {
175   // Not strictly required, but helps us stay consistent w/ `getRoute`, etc.
176   if (rawPath.indexOf(PART_SEPARATOR) !== 0) {
177     throw new Error('Route paths must begin with a path separator; got: "' + rawPath + '"');
178   }
179   var path = rawPath.substr(PART_SEPARATOR.length);
180   if (path === '') return [];
182   return path.split(PART_SEPARATOR).map(function(part) {
183     // raw fragment.
184     if (part.substr(0, 1) == PARAM_SENTINEL) {
185       return {type: 'param', name: part.substr(1)};
186     } else {
187       return {type: 'static', part: part};
188     }
189   });
192 Route.prototype._navigateToParams = function _navigateToParams() {
193   var parts = this.partsForParams(this.params, true);
194   if (!parts) return;
195   this.driver.navigateToParts(parts);
198 function countParams(compiled) {
199   return compiled.reduce(function(count, part) {
200     return count + (part.type === 'param' ? 1 : 0);
201   }, 0);
204 function namedParams(compiled) {
205   var result = [];
206   compiled.forEach(function(part) {
207     if (part.type === 'static') return;
208     result.push(part.name);
209   });
210   return result;
213 })(window);