OLD | NEW |
| (Empty) |
1 'use strict'; | |
2 | |
3 Polymer({ | |
4 is: 'app-route', | |
5 | |
6 properties: { | |
7 /** | |
8 * The URL component managed by this element. | |
9 */ | |
10 route: { | |
11 type: Object, | |
12 notify: true | |
13 }, | |
14 | |
15 /** | |
16 * The pattern of slash-separated segments to match `path` against. | |
17 * | |
18 * For example the pattern "/foo" will match "/foo" or "/foo/bar" | |
19 * but not "/foobar". | |
20 * | |
21 * Path segments like `/:named` are mapped to properties on the `data` obj
ect. | |
22 */ | |
23 pattern: { | |
24 type: String | |
25 }, | |
26 | |
27 /** | |
28 * The parameterized values that are extracted from the route as | |
29 * described by `pattern`. | |
30 */ | |
31 data: { | |
32 type: Object, | |
33 value: function() {return {};}, | |
34 notify: true | |
35 }, | |
36 | |
37 /** | |
38 * @type {?Object} | |
39 */ | |
40 queryParams: { | |
41 type: Object, | |
42 value: function() { | |
43 return {}; | |
44 }, | |
45 notify: true | |
46 }, | |
47 | |
48 /** | |
49 * The part of `path` NOT consumed by `pattern`. | |
50 */ | |
51 tail: { | |
52 type: Object, | |
53 value: function() {return {path: null, prefix: null, __queryParams: null
};}, | |
54 notify: true | |
55 }, | |
56 | |
57 active: { | |
58 type: Boolean, | |
59 notify: true, | |
60 readOnly: true | |
61 }, | |
62 | |
63 _queryParamsUpdating: { | |
64 type: Boolean, | |
65 value: false | |
66 }, | |
67 /** | |
68 * @type {?string} | |
69 */ | |
70 _matched: { | |
71 type: String, | |
72 value: '' | |
73 } | |
74 }, | |
75 | |
76 observers: [ | |
77 '__tryToMatch(route.path, pattern)', | |
78 '__updatePathOnDataChange(data.*)', | |
79 '__tailPathChanged(tail.path)', | |
80 '__routeQueryParamsChanged(route.__queryParams)', | |
81 '__tailQueryParamsChanged(tail.__queryParams)', | |
82 '__queryParamsChanged(queryParams.*)' | |
83 ], | |
84 | |
85 created: function() { | |
86 this.linkPaths('route.__queryParams', 'tail.__queryParams'); | |
87 this.linkPaths('tail.__queryParams', 'route.__queryParams'); | |
88 }, | |
89 | |
90 /** | |
91 * Deal with the query params object being assigned to wholesale. | |
92 * @export | |
93 */ | |
94 __routeQueryParamsChanged: function(queryParams) { | |
95 if (queryParams && this.tail) { | |
96 this.set('tail.__queryParams', queryParams); | |
97 | |
98 if (!this.active || this._queryParamsUpdating) { | |
99 return; | |
100 } | |
101 | |
102 // Copy queryParams and track whether there are any differences compared | |
103 // to the existing query params. | |
104 var copyOfQueryParams = {}; | |
105 var anythingChanged = false; | |
106 for (var key in queryParams) { | |
107 copyOfQueryParams[key] = queryParams[key]; | |
108 if (anythingChanged || | |
109 !this.queryParams || | |
110 queryParams[key] !== this.queryParams[key]) { | |
111 anythingChanged = true; | |
112 } | |
113 } | |
114 // Need to check whether any keys were deleted | |
115 for (var key in this.queryParams) { | |
116 if (anythingChanged || !(key in queryParams)) { | |
117 anythingChanged = true; | |
118 break; | |
119 } | |
120 } | |
121 | |
122 if (!anythingChanged) { | |
123 return; | |
124 } | |
125 this._queryParamsUpdating = true; | |
126 this.set('queryParams', copyOfQueryParams); | |
127 this._queryParamsUpdating = false; | |
128 } | |
129 }, | |
130 | |
131 /** | |
132 * @export | |
133 */ | |
134 __tailQueryParamsChanged: function(queryParams) { | |
135 if (queryParams && this.route) { | |
136 this.set('route.__queryParams', queryParams); | |
137 } | |
138 }, | |
139 | |
140 /** | |
141 * @export | |
142 */ | |
143 __queryParamsChanged: function(changes) { | |
144 if (!this.active || this._queryParamsUpdating) { | |
145 return; | |
146 } | |
147 | |
148 this.set('route.__' + changes.path, changes.value); | |
149 }, | |
150 | |
151 __resetProperties: function() { | |
152 this._setActive(false); | |
153 this._matched = null; | |
154 //this.tail = { path: null, prefix: null, queryParams: null }; | |
155 //this.data = {}; | |
156 }, | |
157 | |
158 /** | |
159 * @export | |
160 */ | |
161 __tryToMatch: function() { | |
162 if (!this.route) { | |
163 return; | |
164 } | |
165 var path = this.route.path; | |
166 var pattern = this.pattern; | |
167 if (!pattern) { | |
168 return; | |
169 } | |
170 | |
171 if (!path) { | |
172 this.__resetProperties(); | |
173 return; | |
174 } | |
175 | |
176 var remainingPieces = path.split('/'); | |
177 var patternPieces = pattern.split('/'); | |
178 | |
179 var matched = []; | |
180 var namedMatches = {}; | |
181 | |
182 for (var i=0; i < patternPieces.length; i++) { | |
183 var patternPiece = patternPieces[i]; | |
184 if (!patternPiece && patternPiece !== '') { | |
185 break; | |
186 } | |
187 var pathPiece = remainingPieces.shift(); | |
188 | |
189 // We don't match this path. | |
190 if (!pathPiece && pathPiece !== '') { | |
191 this.__resetProperties(); | |
192 return; | |
193 } | |
194 matched.push(pathPiece); | |
195 | |
196 if (patternPiece.charAt(0) == ':') { | |
197 namedMatches[patternPiece.slice(1)] = pathPiece; | |
198 } else if (patternPiece !== pathPiece) { | |
199 this.__resetProperties(); | |
200 return; | |
201 } | |
202 } | |
203 | |
204 this._matched = matched.join('/'); | |
205 | |
206 // Properties that must be updated atomically. | |
207 var propertyUpdates = {}; | |
208 | |
209 //this.active | |
210 if (!this.active) { | |
211 propertyUpdates.active = true; | |
212 } | |
213 | |
214 // this.tail | |
215 var tailPrefix = this.route.prefix + this._matched; | |
216 var tailPath = remainingPieces.join('/'); | |
217 if (remainingPieces.length > 0) { | |
218 tailPath = '/' + tailPath; | |
219 } | |
220 if (!this.tail || | |
221 this.tail.prefix !== tailPrefix || | |
222 this.tail.path !== tailPath) { | |
223 propertyUpdates.tail = { | |
224 prefix: tailPrefix, | |
225 path: tailPath, | |
226 __queryParams: this.route.__queryParams | |
227 }; | |
228 } | |
229 | |
230 // this.data | |
231 propertyUpdates.data = namedMatches; | |
232 this._dataInUrl = {}; | |
233 for (var key in namedMatches) { | |
234 this._dataInUrl[key] = namedMatches[key]; | |
235 } | |
236 | |
237 this.__setMulti(propertyUpdates); | |
238 }, | |
239 | |
240 /** | |
241 * @export | |
242 */ | |
243 __tailPathChanged: function() { | |
244 if (!this.active) { | |
245 return; | |
246 } | |
247 var tailPath = this.tail.path; | |
248 var newPath = this._matched; | |
249 if (tailPath) { | |
250 if (tailPath.charAt(0) !== '/') { | |
251 tailPath = '/' + tailPath; | |
252 } | |
253 newPath += tailPath; | |
254 } | |
255 this.set('route.path', newPath); | |
256 }, | |
257 | |
258 /** | |
259 * @export | |
260 */ | |
261 __updatePathOnDataChange: function() { | |
262 if (!this.route || !this.active) { | |
263 return; | |
264 } | |
265 var newPath = this.__getLink({}); | |
266 var oldPath = this.__getLink(this._dataInUrl); | |
267 if (newPath === oldPath) { | |
268 return; | |
269 } | |
270 this.set('route.path', newPath); | |
271 }, | |
272 | |
273 __getLink: function(overrideValues) { | |
274 var values = {tail: null}; | |
275 for (var key in this.data) { | |
276 values[key] = this.data[key]; | |
277 } | |
278 for (var key in overrideValues) { | |
279 values[key] = overrideValues[key]; | |
280 } | |
281 var patternPieces = this.pattern.split('/'); | |
282 var interp = patternPieces.map(function(value) { | |
283 if (value[0] == ':') { | |
284 value = values[value.slice(1)]; | |
285 } | |
286 return value; | |
287 }, this); | |
288 if (values.tail && values.tail.path) { | |
289 if (interp.length > 0 && values.tail.path.charAt(0) === '/') { | |
290 interp.push(values.tail.path.slice(1)); | |
291 } else { | |
292 interp.push(values.tail.path); | |
293 } | |
294 } | |
295 return interp.join('/'); | |
296 }, | |
297 | |
298 __setMulti: function(setObj) { | |
299 // HACK(rictic): skirting around 1.0's lack of a setMulti by poking at | |
300 // internal data structures. I would not advise that you copy this | |
301 // example. | |
302 // | |
303 // In the future this will be a feature of Polymer itself. | |
304 // See: https://github.com/Polymer/polymer/issues/3640 | |
305 // | |
306 // Hacking around with private methods like this is juggling footguns, | |
307 // and is likely to have unexpected and unsupported rough edges. | |
308 // | |
309 // Be ye so warned. | |
310 for (var property in setObj) { | |
311 this._propertySetter(property, setObj[property]); | |
312 } | |
313 | |
314 for (var property in setObj) { | |
315 this._pathEffector(property, this[property]); | |
316 this._notifyPathUp(property, this[property]); | |
317 } | |
318 } | |
319 }); | |
OLD | NEW |