vueonrails 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +70 -0
- data/app/controllers/vue_controller.rb +2 -0
- data/app/helpers/syntax_helper.rb +28 -0
- data/app/views/vue/index.html.erb +1 -0
- data/config/routes.rb +5 -0
- data/lib/generators/generator_templates/packs/index.css +4 -0
- data/lib/generators/generator_templates/packs/index.js +30 -0
- data/lib/generators/generator_templates/packs/index.vue +13 -0
- data/lib/generators/generator_templates/packs/pack.js.erb +18 -0
- data/lib/generators/generator_templates/sfc/single-file-component.vue +45 -0
- data/lib/generators/generator_templates/tests/unit.test.js.erb +17 -0
- data/lib/generators/generator_templates/turbolinks/turbolinks-pack.js.erb +23 -0
- data/lib/generators/options/click.rb +10 -0
- data/lib/generators/options/form.rb +19 -0
- data/lib/generators/options/list.rb +32 -0
- data/lib/generators/options/modal.rb +26 -0
- data/lib/generators/options/seperate.rb +5 -0
- data/lib/generators/options/single.rb +3 -0
- data/lib/generators/options/table.rb +10 -0
- data/lib/generators/options/test.rb +2 -0
- data/lib/generators/options/turbolinks-seperate.rb +5 -0
- data/lib/generators/options/turbolinks-single.rb +3 -0
- data/lib/generators/options/vuex.rb +10 -0
- data/lib/generators/vue/USAGE +17 -0
- data/lib/generators/vue/vue_generator.rb +60 -0
- data/lib/install/Procfile +2 -0
- data/lib/install/config/alias.js +9 -0
- data/lib/install/setup.rb +78 -0
- data/lib/install/spv.rb +20 -0
- data/lib/install/test.rb +46 -0
- data/lib/install/turbolinks.rb +3 -0
- data/lib/install/ui.rb +4 -0
- data/lib/install/vuex.rb +12 -0
- data/lib/tasks/assets.rake +12 -0
- data/lib/tasks/info.rake +21 -0
- data/lib/tasks/vue.rake +27 -0
- data/lib/vueonrails.rb +13 -0
- data/lib/vueonrails/post_message.rb +4 -0
- data/lib/vueonrails/version.rb +3 -0
- data/vendor/assets/javascripts/axios.js +1545 -0
- data/vendor/assets/javascripts/axios.map +1 -0
- data/vendor/assets/javascripts/element-ui.js +12 -0
- data/vendor/assets/javascripts/vue-resource.js +1531 -0
- data/vendor/assets/javascripts/vue-router.js +2709 -0
- data/vendor/assets/javascripts/vue-router2.js +2284 -0
- data/vendor/assets/javascripts/vue-validator.js +910 -0
- data/vendor/assets/javascripts/vue-validator2.js +2615 -0
- data/vendor/assets/javascripts/vue-validator3.js +2054 -0
- data/vendor/assets/javascripts/vue.js +10237 -0
- data/vendor/assets/javascripts/vue.min.js +9 -0
- data/vendor/assets/javascripts/vue2.js +8568 -0
- data/vendor/assets/javascripts/vue2.min.js +8 -0
- data/vendor/assets/javascripts/vueonrails.js +39 -0
- data/vendor/assets/javascripts/vuex.js +722 -0
- data/vendor/assets/javascripts/vuex2.js +805 -0
- data/vendor/assets/stylesheets/element-ui.css +1 -0
- metadata +128 -0
@@ -0,0 +1,2709 @@
|
|
1
|
+
/*!
|
2
|
+
* vue-router v0.7.13
|
3
|
+
* (c) 2016 Evan You
|
4
|
+
* Released under the MIT License.
|
5
|
+
*/
|
6
|
+
(function (global, factory) {
|
7
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
8
|
+
typeof define === 'function' && define.amd ? define(factory) :
|
9
|
+
global.VueRouter = factory();
|
10
|
+
}(this, function () { 'use strict';
|
11
|
+
|
12
|
+
var babelHelpers = {};
|
13
|
+
|
14
|
+
babelHelpers.classCallCheck = function (instance, Constructor) {
|
15
|
+
if (!(instance instanceof Constructor)) {
|
16
|
+
throw new TypeError("Cannot call a class as a function");
|
17
|
+
}
|
18
|
+
};
|
19
|
+
function Target(path, matcher, delegate) {
|
20
|
+
this.path = path;
|
21
|
+
this.matcher = matcher;
|
22
|
+
this.delegate = delegate;
|
23
|
+
}
|
24
|
+
|
25
|
+
Target.prototype = {
|
26
|
+
to: function to(target, callback) {
|
27
|
+
var delegate = this.delegate;
|
28
|
+
|
29
|
+
if (delegate && delegate.willAddRoute) {
|
30
|
+
target = delegate.willAddRoute(this.matcher.target, target);
|
31
|
+
}
|
32
|
+
|
33
|
+
this.matcher.add(this.path, target);
|
34
|
+
|
35
|
+
if (callback) {
|
36
|
+
if (callback.length === 0) {
|
37
|
+
throw new Error("You must have an argument in the function passed to `to`");
|
38
|
+
}
|
39
|
+
this.matcher.addChild(this.path, target, callback, this.delegate);
|
40
|
+
}
|
41
|
+
return this;
|
42
|
+
}
|
43
|
+
};
|
44
|
+
|
45
|
+
function Matcher(target) {
|
46
|
+
this.routes = {};
|
47
|
+
this.children = {};
|
48
|
+
this.target = target;
|
49
|
+
}
|
50
|
+
|
51
|
+
Matcher.prototype = {
|
52
|
+
add: function add(path, handler) {
|
53
|
+
this.routes[path] = handler;
|
54
|
+
},
|
55
|
+
|
56
|
+
addChild: function addChild(path, target, callback, delegate) {
|
57
|
+
var matcher = new Matcher(target);
|
58
|
+
this.children[path] = matcher;
|
59
|
+
|
60
|
+
var match = generateMatch(path, matcher, delegate);
|
61
|
+
|
62
|
+
if (delegate && delegate.contextEntered) {
|
63
|
+
delegate.contextEntered(target, match);
|
64
|
+
}
|
65
|
+
|
66
|
+
callback(match);
|
67
|
+
}
|
68
|
+
};
|
69
|
+
|
70
|
+
function generateMatch(startingPath, matcher, delegate) {
|
71
|
+
return function (path, nestedCallback) {
|
72
|
+
var fullPath = startingPath + path;
|
73
|
+
|
74
|
+
if (nestedCallback) {
|
75
|
+
nestedCallback(generateMatch(fullPath, matcher, delegate));
|
76
|
+
} else {
|
77
|
+
return new Target(startingPath + path, matcher, delegate);
|
78
|
+
}
|
79
|
+
};
|
80
|
+
}
|
81
|
+
|
82
|
+
function addRoute(routeArray, path, handler) {
|
83
|
+
var len = 0;
|
84
|
+
for (var i = 0, l = routeArray.length; i < l; i++) {
|
85
|
+
len += routeArray[i].path.length;
|
86
|
+
}
|
87
|
+
|
88
|
+
path = path.substr(len);
|
89
|
+
var route = { path: path, handler: handler };
|
90
|
+
routeArray.push(route);
|
91
|
+
}
|
92
|
+
|
93
|
+
function eachRoute(baseRoute, matcher, callback, binding) {
|
94
|
+
var routes = matcher.routes;
|
95
|
+
|
96
|
+
for (var path in routes) {
|
97
|
+
if (routes.hasOwnProperty(path)) {
|
98
|
+
var routeArray = baseRoute.slice();
|
99
|
+
addRoute(routeArray, path, routes[path]);
|
100
|
+
|
101
|
+
if (matcher.children[path]) {
|
102
|
+
eachRoute(routeArray, matcher.children[path], callback, binding);
|
103
|
+
} else {
|
104
|
+
callback.call(binding, routeArray);
|
105
|
+
}
|
106
|
+
}
|
107
|
+
}
|
108
|
+
}
|
109
|
+
|
110
|
+
function map (callback, addRouteCallback) {
|
111
|
+
var matcher = new Matcher();
|
112
|
+
|
113
|
+
callback(generateMatch("", matcher, this.delegate));
|
114
|
+
|
115
|
+
eachRoute([], matcher, function (route) {
|
116
|
+
if (addRouteCallback) {
|
117
|
+
addRouteCallback(this, route);
|
118
|
+
} else {
|
119
|
+
this.add(route);
|
120
|
+
}
|
121
|
+
}, this);
|
122
|
+
}
|
123
|
+
|
124
|
+
var specials = ['/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\'];
|
125
|
+
|
126
|
+
var escapeRegex = new RegExp('(\\' + specials.join('|\\') + ')', 'g');
|
127
|
+
|
128
|
+
var noWarning = false;
|
129
|
+
function warn(msg) {
|
130
|
+
if (!noWarning && typeof console !== 'undefined') {
|
131
|
+
console.error('[vue-router] ' + msg);
|
132
|
+
}
|
133
|
+
}
|
134
|
+
|
135
|
+
function tryDecode(uri, asComponent) {
|
136
|
+
try {
|
137
|
+
return asComponent ? decodeURIComponent(uri) : decodeURI(uri);
|
138
|
+
} catch (e) {
|
139
|
+
warn('malformed URI' + (asComponent ? ' component: ' : ': ') + uri);
|
140
|
+
}
|
141
|
+
}
|
142
|
+
|
143
|
+
function isArray(test) {
|
144
|
+
return Object.prototype.toString.call(test) === "[object Array]";
|
145
|
+
}
|
146
|
+
|
147
|
+
// A Segment represents a segment in the original route description.
|
148
|
+
// Each Segment type provides an `eachChar` and `regex` method.
|
149
|
+
//
|
150
|
+
// The `eachChar` method invokes the callback with one or more character
|
151
|
+
// specifications. A character specification consumes one or more input
|
152
|
+
// characters.
|
153
|
+
//
|
154
|
+
// The `regex` method returns a regex fragment for the segment. If the
|
155
|
+
// segment is a dynamic of star segment, the regex fragment also includes
|
156
|
+
// a capture.
|
157
|
+
//
|
158
|
+
// A character specification contains:
|
159
|
+
//
|
160
|
+
// * `validChars`: a String with a list of all valid characters, or
|
161
|
+
// * `invalidChars`: a String with a list of all invalid characters
|
162
|
+
// * `repeat`: true if the character specification can repeat
|
163
|
+
|
164
|
+
function StaticSegment(string) {
|
165
|
+
this.string = string;
|
166
|
+
}
|
167
|
+
StaticSegment.prototype = {
|
168
|
+
eachChar: function eachChar(callback) {
|
169
|
+
var string = this.string,
|
170
|
+
ch;
|
171
|
+
|
172
|
+
for (var i = 0, l = string.length; i < l; i++) {
|
173
|
+
ch = string.charAt(i);
|
174
|
+
callback({ validChars: ch });
|
175
|
+
}
|
176
|
+
},
|
177
|
+
|
178
|
+
regex: function regex() {
|
179
|
+
return this.string.replace(escapeRegex, '\\$1');
|
180
|
+
},
|
181
|
+
|
182
|
+
generate: function generate() {
|
183
|
+
return this.string;
|
184
|
+
}
|
185
|
+
};
|
186
|
+
|
187
|
+
function DynamicSegment(name) {
|
188
|
+
this.name = name;
|
189
|
+
}
|
190
|
+
DynamicSegment.prototype = {
|
191
|
+
eachChar: function eachChar(callback) {
|
192
|
+
callback({ invalidChars: "/", repeat: true });
|
193
|
+
},
|
194
|
+
|
195
|
+
regex: function regex() {
|
196
|
+
return "([^/]+)";
|
197
|
+
},
|
198
|
+
|
199
|
+
generate: function generate(params) {
|
200
|
+
var val = params[this.name];
|
201
|
+
return val == null ? ":" + this.name : val;
|
202
|
+
}
|
203
|
+
};
|
204
|
+
|
205
|
+
function StarSegment(name) {
|
206
|
+
this.name = name;
|
207
|
+
}
|
208
|
+
StarSegment.prototype = {
|
209
|
+
eachChar: function eachChar(callback) {
|
210
|
+
callback({ invalidChars: "", repeat: true });
|
211
|
+
},
|
212
|
+
|
213
|
+
regex: function regex() {
|
214
|
+
return "(.+)";
|
215
|
+
},
|
216
|
+
|
217
|
+
generate: function generate(params) {
|
218
|
+
var val = params[this.name];
|
219
|
+
return val == null ? ":" + this.name : val;
|
220
|
+
}
|
221
|
+
};
|
222
|
+
|
223
|
+
function EpsilonSegment() {}
|
224
|
+
EpsilonSegment.prototype = {
|
225
|
+
eachChar: function eachChar() {},
|
226
|
+
regex: function regex() {
|
227
|
+
return "";
|
228
|
+
},
|
229
|
+
generate: function generate() {
|
230
|
+
return "";
|
231
|
+
}
|
232
|
+
};
|
233
|
+
|
234
|
+
function parse(route, names, specificity) {
|
235
|
+
// normalize route as not starting with a "/". Recognition will
|
236
|
+
// also normalize.
|
237
|
+
if (route.charAt(0) === "/") {
|
238
|
+
route = route.substr(1);
|
239
|
+
}
|
240
|
+
|
241
|
+
var segments = route.split("/"),
|
242
|
+
results = [];
|
243
|
+
|
244
|
+
// A routes has specificity determined by the order that its different segments
|
245
|
+
// appear in. This system mirrors how the magnitude of numbers written as strings
|
246
|
+
// works.
|
247
|
+
// Consider a number written as: "abc". An example would be "200". Any other number written
|
248
|
+
// "xyz" will be smaller than "abc" so long as `a > z`. For instance, "199" is smaller
|
249
|
+
// then "200", even though "y" and "z" (which are both 9) are larger than "0" (the value
|
250
|
+
// of (`b` and `c`). This is because the leading symbol, "2", is larger than the other
|
251
|
+
// leading symbol, "1".
|
252
|
+
// The rule is that symbols to the left carry more weight than symbols to the right
|
253
|
+
// when a number is written out as a string. In the above strings, the leading digit
|
254
|
+
// represents how many 100's are in the number, and it carries more weight than the middle
|
255
|
+
// number which represents how many 10's are in the number.
|
256
|
+
// This system of number magnitude works well for route specificity, too. A route written as
|
257
|
+
// `a/b/c` will be more specific than `x/y/z` as long as `a` is more specific than
|
258
|
+
// `x`, irrespective of the other parts.
|
259
|
+
// Because of this similarity, we assign each type of segment a number value written as a
|
260
|
+
// string. We can find the specificity of compound routes by concatenating these strings
|
261
|
+
// together, from left to right. After we have looped through all of the segments,
|
262
|
+
// we convert the string to a number.
|
263
|
+
specificity.val = '';
|
264
|
+
|
265
|
+
for (var i = 0, l = segments.length; i < l; i++) {
|
266
|
+
var segment = segments[i],
|
267
|
+
match;
|
268
|
+
|
269
|
+
if (match = segment.match(/^:([^\/]+)$/)) {
|
270
|
+
results.push(new DynamicSegment(match[1]));
|
271
|
+
names.push(match[1]);
|
272
|
+
specificity.val += '3';
|
273
|
+
} else if (match = segment.match(/^\*([^\/]+)$/)) {
|
274
|
+
results.push(new StarSegment(match[1]));
|
275
|
+
specificity.val += '2';
|
276
|
+
names.push(match[1]);
|
277
|
+
} else if (segment === "") {
|
278
|
+
results.push(new EpsilonSegment());
|
279
|
+
specificity.val += '1';
|
280
|
+
} else {
|
281
|
+
results.push(new StaticSegment(segment));
|
282
|
+
specificity.val += '4';
|
283
|
+
}
|
284
|
+
}
|
285
|
+
|
286
|
+
specificity.val = +specificity.val;
|
287
|
+
|
288
|
+
return results;
|
289
|
+
}
|
290
|
+
|
291
|
+
// A State has a character specification and (`charSpec`) and a list of possible
|
292
|
+
// subsequent states (`nextStates`).
|
293
|
+
//
|
294
|
+
// If a State is an accepting state, it will also have several additional
|
295
|
+
// properties:
|
296
|
+
//
|
297
|
+
// * `regex`: A regular expression that is used to extract parameters from paths
|
298
|
+
// that reached this accepting state.
|
299
|
+
// * `handlers`: Information on how to convert the list of captures into calls
|
300
|
+
// to registered handlers with the specified parameters
|
301
|
+
// * `types`: How many static, dynamic or star segments in this route. Used to
|
302
|
+
// decide which route to use if multiple registered routes match a path.
|
303
|
+
//
|
304
|
+
// Currently, State is implemented naively by looping over `nextStates` and
|
305
|
+
// comparing a character specification against a character. A more efficient
|
306
|
+
// implementation would use a hash of keys pointing at one or more next states.
|
307
|
+
|
308
|
+
function State(charSpec) {
|
309
|
+
this.charSpec = charSpec;
|
310
|
+
this.nextStates = [];
|
311
|
+
}
|
312
|
+
|
313
|
+
State.prototype = {
|
314
|
+
get: function get(charSpec) {
|
315
|
+
var nextStates = this.nextStates;
|
316
|
+
|
317
|
+
for (var i = 0, l = nextStates.length; i < l; i++) {
|
318
|
+
var child = nextStates[i];
|
319
|
+
|
320
|
+
var isEqual = child.charSpec.validChars === charSpec.validChars;
|
321
|
+
isEqual = isEqual && child.charSpec.invalidChars === charSpec.invalidChars;
|
322
|
+
|
323
|
+
if (isEqual) {
|
324
|
+
return child;
|
325
|
+
}
|
326
|
+
}
|
327
|
+
},
|
328
|
+
|
329
|
+
put: function put(charSpec) {
|
330
|
+
var state;
|
331
|
+
|
332
|
+
// If the character specification already exists in a child of the current
|
333
|
+
// state, just return that state.
|
334
|
+
if (state = this.get(charSpec)) {
|
335
|
+
return state;
|
336
|
+
}
|
337
|
+
|
338
|
+
// Make a new state for the character spec
|
339
|
+
state = new State(charSpec);
|
340
|
+
|
341
|
+
// Insert the new state as a child of the current state
|
342
|
+
this.nextStates.push(state);
|
343
|
+
|
344
|
+
// If this character specification repeats, insert the new state as a child
|
345
|
+
// of itself. Note that this will not trigger an infinite loop because each
|
346
|
+
// transition during recognition consumes a character.
|
347
|
+
if (charSpec.repeat) {
|
348
|
+
state.nextStates.push(state);
|
349
|
+
}
|
350
|
+
|
351
|
+
// Return the new state
|
352
|
+
return state;
|
353
|
+
},
|
354
|
+
|
355
|
+
// Find a list of child states matching the next character
|
356
|
+
match: function match(ch) {
|
357
|
+
// DEBUG "Processing `" + ch + "`:"
|
358
|
+
var nextStates = this.nextStates,
|
359
|
+
child,
|
360
|
+
charSpec,
|
361
|
+
chars;
|
362
|
+
|
363
|
+
// DEBUG " " + debugState(this)
|
364
|
+
var returned = [];
|
365
|
+
|
366
|
+
for (var i = 0, l = nextStates.length; i < l; i++) {
|
367
|
+
child = nextStates[i];
|
368
|
+
|
369
|
+
charSpec = child.charSpec;
|
370
|
+
|
371
|
+
if (typeof (chars = charSpec.validChars) !== 'undefined') {
|
372
|
+
if (chars.indexOf(ch) !== -1) {
|
373
|
+
returned.push(child);
|
374
|
+
}
|
375
|
+
} else if (typeof (chars = charSpec.invalidChars) !== 'undefined') {
|
376
|
+
if (chars.indexOf(ch) === -1) {
|
377
|
+
returned.push(child);
|
378
|
+
}
|
379
|
+
}
|
380
|
+
}
|
381
|
+
|
382
|
+
return returned;
|
383
|
+
}
|
384
|
+
|
385
|
+
/** IF DEBUG
|
386
|
+
, debug: function() {
|
387
|
+
var charSpec = this.charSpec,
|
388
|
+
debug = "[",
|
389
|
+
chars = charSpec.validChars || charSpec.invalidChars;
|
390
|
+
if (charSpec.invalidChars) { debug += "^"; }
|
391
|
+
debug += chars;
|
392
|
+
debug += "]";
|
393
|
+
if (charSpec.repeat) { debug += "+"; }
|
394
|
+
return debug;
|
395
|
+
}
|
396
|
+
END IF **/
|
397
|
+
};
|
398
|
+
|
399
|
+
/** IF DEBUG
|
400
|
+
function debug(log) {
|
401
|
+
console.log(log);
|
402
|
+
}
|
403
|
+
|
404
|
+
function debugState(state) {
|
405
|
+
return state.nextStates.map(function(n) {
|
406
|
+
if (n.nextStates.length === 0) { return "( " + n.debug() + " [accepting] )"; }
|
407
|
+
return "( " + n.debug() + " <then> " + n.nextStates.map(function(s) { return s.debug() }).join(" or ") + " )";
|
408
|
+
}).join(", ")
|
409
|
+
}
|
410
|
+
END IF **/
|
411
|
+
|
412
|
+
// Sort the routes by specificity
|
413
|
+
function sortSolutions(states) {
|
414
|
+
return states.sort(function (a, b) {
|
415
|
+
return b.specificity.val - a.specificity.val;
|
416
|
+
});
|
417
|
+
}
|
418
|
+
|
419
|
+
function recognizeChar(states, ch) {
|
420
|
+
var nextStates = [];
|
421
|
+
|
422
|
+
for (var i = 0, l = states.length; i < l; i++) {
|
423
|
+
var state = states[i];
|
424
|
+
|
425
|
+
nextStates = nextStates.concat(state.match(ch));
|
426
|
+
}
|
427
|
+
|
428
|
+
return nextStates;
|
429
|
+
}
|
430
|
+
|
431
|
+
var oCreate = Object.create || function (proto) {
|
432
|
+
function F() {}
|
433
|
+
F.prototype = proto;
|
434
|
+
return new F();
|
435
|
+
};
|
436
|
+
|
437
|
+
function RecognizeResults(queryParams) {
|
438
|
+
this.queryParams = queryParams || {};
|
439
|
+
}
|
440
|
+
RecognizeResults.prototype = oCreate({
|
441
|
+
splice: Array.prototype.splice,
|
442
|
+
slice: Array.prototype.slice,
|
443
|
+
push: Array.prototype.push,
|
444
|
+
length: 0,
|
445
|
+
queryParams: null
|
446
|
+
});
|
447
|
+
|
448
|
+
function findHandler(state, path, queryParams) {
|
449
|
+
var handlers = state.handlers,
|
450
|
+
regex = state.regex;
|
451
|
+
var captures = path.match(regex),
|
452
|
+
currentCapture = 1;
|
453
|
+
var result = new RecognizeResults(queryParams);
|
454
|
+
|
455
|
+
for (var i = 0, l = handlers.length; i < l; i++) {
|
456
|
+
var handler = handlers[i],
|
457
|
+
names = handler.names,
|
458
|
+
params = {};
|
459
|
+
|
460
|
+
for (var j = 0, m = names.length; j < m; j++) {
|
461
|
+
params[names[j]] = captures[currentCapture++];
|
462
|
+
}
|
463
|
+
|
464
|
+
result.push({ handler: handler.handler, params: params, isDynamic: !!names.length });
|
465
|
+
}
|
466
|
+
|
467
|
+
return result;
|
468
|
+
}
|
469
|
+
|
470
|
+
function addSegment(currentState, segment) {
|
471
|
+
segment.eachChar(function (ch) {
|
472
|
+
var state;
|
473
|
+
|
474
|
+
currentState = currentState.put(ch);
|
475
|
+
});
|
476
|
+
|
477
|
+
return currentState;
|
478
|
+
}
|
479
|
+
|
480
|
+
function decodeQueryParamPart(part) {
|
481
|
+
// http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1
|
482
|
+
part = part.replace(/\+/gm, '%20');
|
483
|
+
return tryDecode(part, true);
|
484
|
+
}
|
485
|
+
|
486
|
+
// The main interface
|
487
|
+
|
488
|
+
var RouteRecognizer = function RouteRecognizer() {
|
489
|
+
this.rootState = new State();
|
490
|
+
this.names = {};
|
491
|
+
};
|
492
|
+
|
493
|
+
RouteRecognizer.prototype = {
|
494
|
+
add: function add(routes, options) {
|
495
|
+
var currentState = this.rootState,
|
496
|
+
regex = "^",
|
497
|
+
specificity = {},
|
498
|
+
handlers = [],
|
499
|
+
allSegments = [],
|
500
|
+
name;
|
501
|
+
|
502
|
+
var isEmpty = true;
|
503
|
+
|
504
|
+
for (var i = 0, l = routes.length; i < l; i++) {
|
505
|
+
var route = routes[i],
|
506
|
+
names = [];
|
507
|
+
|
508
|
+
var segments = parse(route.path, names, specificity);
|
509
|
+
|
510
|
+
allSegments = allSegments.concat(segments);
|
511
|
+
|
512
|
+
for (var j = 0, m = segments.length; j < m; j++) {
|
513
|
+
var segment = segments[j];
|
514
|
+
|
515
|
+
if (segment instanceof EpsilonSegment) {
|
516
|
+
continue;
|
517
|
+
}
|
518
|
+
|
519
|
+
isEmpty = false;
|
520
|
+
|
521
|
+
// Add a "/" for the new segment
|
522
|
+
currentState = currentState.put({ validChars: "/" });
|
523
|
+
regex += "/";
|
524
|
+
|
525
|
+
// Add a representation of the segment to the NFA and regex
|
526
|
+
currentState = addSegment(currentState, segment);
|
527
|
+
regex += segment.regex();
|
528
|
+
}
|
529
|
+
|
530
|
+
var handler = { handler: route.handler, names: names };
|
531
|
+
handlers.push(handler);
|
532
|
+
}
|
533
|
+
|
534
|
+
if (isEmpty) {
|
535
|
+
currentState = currentState.put({ validChars: "/" });
|
536
|
+
regex += "/";
|
537
|
+
}
|
538
|
+
|
539
|
+
currentState.handlers = handlers;
|
540
|
+
currentState.regex = new RegExp(regex + "$");
|
541
|
+
currentState.specificity = specificity;
|
542
|
+
|
543
|
+
if (name = options && options.as) {
|
544
|
+
this.names[name] = {
|
545
|
+
segments: allSegments,
|
546
|
+
handlers: handlers
|
547
|
+
};
|
548
|
+
}
|
549
|
+
},
|
550
|
+
|
551
|
+
handlersFor: function handlersFor(name) {
|
552
|
+
var route = this.names[name],
|
553
|
+
result = [];
|
554
|
+
if (!route) {
|
555
|
+
throw new Error("There is no route named " + name);
|
556
|
+
}
|
557
|
+
|
558
|
+
for (var i = 0, l = route.handlers.length; i < l; i++) {
|
559
|
+
result.push(route.handlers[i]);
|
560
|
+
}
|
561
|
+
|
562
|
+
return result;
|
563
|
+
},
|
564
|
+
|
565
|
+
hasRoute: function hasRoute(name) {
|
566
|
+
return !!this.names[name];
|
567
|
+
},
|
568
|
+
|
569
|
+
generate: function generate(name, params) {
|
570
|
+
var route = this.names[name],
|
571
|
+
output = "";
|
572
|
+
if (!route) {
|
573
|
+
throw new Error("There is no route named " + name);
|
574
|
+
}
|
575
|
+
|
576
|
+
var segments = route.segments;
|
577
|
+
|
578
|
+
for (var i = 0, l = segments.length; i < l; i++) {
|
579
|
+
var segment = segments[i];
|
580
|
+
|
581
|
+
if (segment instanceof EpsilonSegment) {
|
582
|
+
continue;
|
583
|
+
}
|
584
|
+
|
585
|
+
output += "/";
|
586
|
+
output += segment.generate(params);
|
587
|
+
}
|
588
|
+
|
589
|
+
if (output.charAt(0) !== '/') {
|
590
|
+
output = '/' + output;
|
591
|
+
}
|
592
|
+
|
593
|
+
if (params && params.queryParams) {
|
594
|
+
output += this.generateQueryString(params.queryParams);
|
595
|
+
}
|
596
|
+
|
597
|
+
return output;
|
598
|
+
},
|
599
|
+
|
600
|
+
generateQueryString: function generateQueryString(params) {
|
601
|
+
var pairs = [];
|
602
|
+
var keys = [];
|
603
|
+
for (var key in params) {
|
604
|
+
if (params.hasOwnProperty(key)) {
|
605
|
+
keys.push(key);
|
606
|
+
}
|
607
|
+
}
|
608
|
+
keys.sort();
|
609
|
+
for (var i = 0, len = keys.length; i < len; i++) {
|
610
|
+
key = keys[i];
|
611
|
+
var value = params[key];
|
612
|
+
if (value == null) {
|
613
|
+
continue;
|
614
|
+
}
|
615
|
+
var pair = encodeURIComponent(key);
|
616
|
+
if (isArray(value)) {
|
617
|
+
for (var j = 0, l = value.length; j < l; j++) {
|
618
|
+
var arrayPair = key + '[]' + '=' + encodeURIComponent(value[j]);
|
619
|
+
pairs.push(arrayPair);
|
620
|
+
}
|
621
|
+
} else {
|
622
|
+
pair += "=" + encodeURIComponent(value);
|
623
|
+
pairs.push(pair);
|
624
|
+
}
|
625
|
+
}
|
626
|
+
|
627
|
+
if (pairs.length === 0) {
|
628
|
+
return '';
|
629
|
+
}
|
630
|
+
|
631
|
+
return "?" + pairs.join("&");
|
632
|
+
},
|
633
|
+
|
634
|
+
parseQueryString: function parseQueryString(queryString) {
|
635
|
+
var pairs = queryString.split("&"),
|
636
|
+
queryParams = {};
|
637
|
+
for (var i = 0; i < pairs.length; i++) {
|
638
|
+
var pair = pairs[i].split('='),
|
639
|
+
key = decodeQueryParamPart(pair[0]),
|
640
|
+
keyLength = key.length,
|
641
|
+
isArray = false,
|
642
|
+
value;
|
643
|
+
if (pair.length === 1) {
|
644
|
+
value = 'true';
|
645
|
+
} else {
|
646
|
+
//Handle arrays
|
647
|
+
if (keyLength > 2 && key.slice(keyLength - 2) === '[]') {
|
648
|
+
isArray = true;
|
649
|
+
key = key.slice(0, keyLength - 2);
|
650
|
+
if (!queryParams[key]) {
|
651
|
+
queryParams[key] = [];
|
652
|
+
}
|
653
|
+
}
|
654
|
+
value = pair[1] ? decodeQueryParamPart(pair[1]) : '';
|
655
|
+
}
|
656
|
+
if (isArray) {
|
657
|
+
queryParams[key].push(value);
|
658
|
+
} else {
|
659
|
+
queryParams[key] = value;
|
660
|
+
}
|
661
|
+
}
|
662
|
+
return queryParams;
|
663
|
+
},
|
664
|
+
|
665
|
+
recognize: function recognize(path, silent) {
|
666
|
+
noWarning = silent;
|
667
|
+
var states = [this.rootState],
|
668
|
+
pathLen,
|
669
|
+
i,
|
670
|
+
l,
|
671
|
+
queryStart,
|
672
|
+
queryParams = {},
|
673
|
+
isSlashDropped = false;
|
674
|
+
|
675
|
+
queryStart = path.indexOf('?');
|
676
|
+
if (queryStart !== -1) {
|
677
|
+
var queryString = path.substr(queryStart + 1, path.length);
|
678
|
+
path = path.substr(0, queryStart);
|
679
|
+
if (queryString) {
|
680
|
+
queryParams = this.parseQueryString(queryString);
|
681
|
+
}
|
682
|
+
}
|
683
|
+
|
684
|
+
path = tryDecode(path);
|
685
|
+
if (!path) return;
|
686
|
+
|
687
|
+
// DEBUG GROUP path
|
688
|
+
|
689
|
+
if (path.charAt(0) !== "/") {
|
690
|
+
path = "/" + path;
|
691
|
+
}
|
692
|
+
|
693
|
+
pathLen = path.length;
|
694
|
+
if (pathLen > 1 && path.charAt(pathLen - 1) === "/") {
|
695
|
+
path = path.substr(0, pathLen - 1);
|
696
|
+
isSlashDropped = true;
|
697
|
+
}
|
698
|
+
|
699
|
+
for (i = 0, l = path.length; i < l; i++) {
|
700
|
+
states = recognizeChar(states, path.charAt(i));
|
701
|
+
if (!states.length) {
|
702
|
+
break;
|
703
|
+
}
|
704
|
+
}
|
705
|
+
|
706
|
+
// END DEBUG GROUP
|
707
|
+
|
708
|
+
var solutions = [];
|
709
|
+
for (i = 0, l = states.length; i < l; i++) {
|
710
|
+
if (states[i].handlers) {
|
711
|
+
solutions.push(states[i]);
|
712
|
+
}
|
713
|
+
}
|
714
|
+
|
715
|
+
states = sortSolutions(solutions);
|
716
|
+
|
717
|
+
var state = solutions[0];
|
718
|
+
|
719
|
+
if (state && state.handlers) {
|
720
|
+
// if a trailing slash was dropped and a star segment is the last segment
|
721
|
+
// specified, put the trailing slash back
|
722
|
+
if (isSlashDropped && state.regex.source.slice(-5) === "(.+)$") {
|
723
|
+
path = path + "/";
|
724
|
+
}
|
725
|
+
return findHandler(state, path, queryParams);
|
726
|
+
}
|
727
|
+
}
|
728
|
+
};
|
729
|
+
|
730
|
+
RouteRecognizer.prototype.map = map;
|
731
|
+
|
732
|
+
var genQuery = RouteRecognizer.prototype.generateQueryString;
|
733
|
+
|
734
|
+
// export default for holding the Vue reference
|
735
|
+
var exports$1 = {};
|
736
|
+
/**
|
737
|
+
* Warn stuff.
|
738
|
+
*
|
739
|
+
* @param {String} msg
|
740
|
+
*/
|
741
|
+
|
742
|
+
function warn$1(msg) {
|
743
|
+
/* istanbul ignore next */
|
744
|
+
if (typeof console !== 'undefined') {
|
745
|
+
console.error('[vue-router] ' + msg);
|
746
|
+
}
|
747
|
+
}
|
748
|
+
|
749
|
+
/**
|
750
|
+
* Resolve a relative path.
|
751
|
+
*
|
752
|
+
* @param {String} base
|
753
|
+
* @param {String} relative
|
754
|
+
* @param {Boolean} append
|
755
|
+
* @return {String}
|
756
|
+
*/
|
757
|
+
|
758
|
+
function resolvePath(base, relative, append) {
|
759
|
+
var query = base.match(/(\?.*)$/);
|
760
|
+
if (query) {
|
761
|
+
query = query[1];
|
762
|
+
base = base.slice(0, -query.length);
|
763
|
+
}
|
764
|
+
// a query!
|
765
|
+
if (relative.charAt(0) === '?') {
|
766
|
+
return base + relative;
|
767
|
+
}
|
768
|
+
var stack = base.split('/');
|
769
|
+
// remove trailing segment if:
|
770
|
+
// - not appending
|
771
|
+
// - appending to trailing slash (last segment is empty)
|
772
|
+
if (!append || !stack[stack.length - 1]) {
|
773
|
+
stack.pop();
|
774
|
+
}
|
775
|
+
// resolve relative path
|
776
|
+
var segments = relative.replace(/^\//, '').split('/');
|
777
|
+
for (var i = 0; i < segments.length; i++) {
|
778
|
+
var segment = segments[i];
|
779
|
+
if (segment === '.') {
|
780
|
+
continue;
|
781
|
+
} else if (segment === '..') {
|
782
|
+
stack.pop();
|
783
|
+
} else {
|
784
|
+
stack.push(segment);
|
785
|
+
}
|
786
|
+
}
|
787
|
+
// ensure leading slash
|
788
|
+
if (stack[0] !== '') {
|
789
|
+
stack.unshift('');
|
790
|
+
}
|
791
|
+
return stack.join('/');
|
792
|
+
}
|
793
|
+
|
794
|
+
/**
|
795
|
+
* Forgiving check for a promise
|
796
|
+
*
|
797
|
+
* @param {Object} p
|
798
|
+
* @return {Boolean}
|
799
|
+
*/
|
800
|
+
|
801
|
+
function isPromise(p) {
|
802
|
+
return p && typeof p.then === 'function';
|
803
|
+
}
|
804
|
+
|
805
|
+
/**
|
806
|
+
* Retrive a route config field from a component instance
|
807
|
+
* OR a component contructor.
|
808
|
+
*
|
809
|
+
* @param {Function|Vue} component
|
810
|
+
* @param {String} name
|
811
|
+
* @return {*}
|
812
|
+
*/
|
813
|
+
|
814
|
+
function getRouteConfig(component, name) {
|
815
|
+
var options = component && (component.$options || component.options);
|
816
|
+
return options && options.route && options.route[name];
|
817
|
+
}
|
818
|
+
|
819
|
+
/**
|
820
|
+
* Resolve an async component factory. Have to do a dirty
|
821
|
+
* mock here because of Vue core's internal API depends on
|
822
|
+
* an ID check.
|
823
|
+
*
|
824
|
+
* @param {Object} handler
|
825
|
+
* @param {Function} cb
|
826
|
+
*/
|
827
|
+
|
828
|
+
var resolver = undefined;
|
829
|
+
|
830
|
+
function resolveAsyncComponent(handler, cb) {
|
831
|
+
if (!resolver) {
|
832
|
+
resolver = {
|
833
|
+
resolve: exports$1.Vue.prototype._resolveComponent,
|
834
|
+
$options: {
|
835
|
+
components: {
|
836
|
+
_: handler.component
|
837
|
+
}
|
838
|
+
}
|
839
|
+
};
|
840
|
+
} else {
|
841
|
+
resolver.$options.components._ = handler.component;
|
842
|
+
}
|
843
|
+
resolver.resolve('_', function (Component) {
|
844
|
+
handler.component = Component;
|
845
|
+
cb(Component);
|
846
|
+
});
|
847
|
+
}
|
848
|
+
|
849
|
+
/**
|
850
|
+
* Map the dynamic segments in a path to params.
|
851
|
+
*
|
852
|
+
* @param {String} path
|
853
|
+
* @param {Object} params
|
854
|
+
* @param {Object} query
|
855
|
+
*/
|
856
|
+
|
857
|
+
function mapParams(path, params, query) {
|
858
|
+
if (params === undefined) params = {};
|
859
|
+
|
860
|
+
path = path.replace(/:([^\/]+)/g, function (_, key) {
|
861
|
+
var val = params[key];
|
862
|
+
/* istanbul ignore if */
|
863
|
+
if (!val) {
|
864
|
+
warn$1('param "' + key + '" not found when generating ' + 'path for "' + path + '" with params ' + JSON.stringify(params));
|
865
|
+
}
|
866
|
+
return val || '';
|
867
|
+
});
|
868
|
+
if (query) {
|
869
|
+
path += genQuery(query);
|
870
|
+
}
|
871
|
+
return path;
|
872
|
+
}
|
873
|
+
|
874
|
+
var hashRE = /#.*$/;
|
875
|
+
|
876
|
+
var HTML5History = (function () {
|
877
|
+
function HTML5History(_ref) {
|
878
|
+
var root = _ref.root;
|
879
|
+
var onChange = _ref.onChange;
|
880
|
+
babelHelpers.classCallCheck(this, HTML5History);
|
881
|
+
|
882
|
+
if (root && root !== '/') {
|
883
|
+
// make sure there's the starting slash
|
884
|
+
if (root.charAt(0) !== '/') {
|
885
|
+
root = '/' + root;
|
886
|
+
}
|
887
|
+
// remove trailing slash
|
888
|
+
this.root = root.replace(/\/$/, '');
|
889
|
+
this.rootRE = new RegExp('^\\' + this.root);
|
890
|
+
} else {
|
891
|
+
this.root = null;
|
892
|
+
}
|
893
|
+
this.onChange = onChange;
|
894
|
+
// check base tag
|
895
|
+
var baseEl = document.querySelector('base');
|
896
|
+
this.base = baseEl && baseEl.getAttribute('href');
|
897
|
+
}
|
898
|
+
|
899
|
+
HTML5History.prototype.start = function start() {
|
900
|
+
var _this = this;
|
901
|
+
|
902
|
+
this.listener = function (e) {
|
903
|
+
var url = location.pathname + location.search;
|
904
|
+
if (_this.root) {
|
905
|
+
url = url.replace(_this.rootRE, '');
|
906
|
+
}
|
907
|
+
_this.onChange(url, e && e.state, location.hash);
|
908
|
+
};
|
909
|
+
window.addEventListener('popstate', this.listener);
|
910
|
+
this.listener();
|
911
|
+
};
|
912
|
+
|
913
|
+
HTML5History.prototype.stop = function stop() {
|
914
|
+
window.removeEventListener('popstate', this.listener);
|
915
|
+
};
|
916
|
+
|
917
|
+
HTML5History.prototype.go = function go(path, replace, append) {
|
918
|
+
var url = this.formatPath(path, append);
|
919
|
+
if (replace) {
|
920
|
+
history.replaceState({}, '', url);
|
921
|
+
} else {
|
922
|
+
// record scroll position by replacing current state
|
923
|
+
history.replaceState({
|
924
|
+
pos: {
|
925
|
+
x: window.pageXOffset,
|
926
|
+
y: window.pageYOffset
|
927
|
+
}
|
928
|
+
}, '', location.href);
|
929
|
+
// then push new state
|
930
|
+
history.pushState({}, '', url);
|
931
|
+
}
|
932
|
+
var hashMatch = path.match(hashRE);
|
933
|
+
var hash = hashMatch && hashMatch[0];
|
934
|
+
path = url
|
935
|
+
// strip hash so it doesn't mess up params
|
936
|
+
.replace(hashRE, '')
|
937
|
+
// remove root before matching
|
938
|
+
.replace(this.rootRE, '');
|
939
|
+
this.onChange(path, null, hash);
|
940
|
+
};
|
941
|
+
|
942
|
+
HTML5History.prototype.formatPath = function formatPath(path, append) {
|
943
|
+
return path.charAt(0) === '/'
|
944
|
+
// absolute path
|
945
|
+
? this.root ? this.root + '/' + path.replace(/^\//, '') : path : resolvePath(this.base || location.pathname, path, append);
|
946
|
+
};
|
947
|
+
|
948
|
+
return HTML5History;
|
949
|
+
})();
|
950
|
+
|
951
|
+
var HashHistory = (function () {
|
952
|
+
function HashHistory(_ref) {
|
953
|
+
var hashbang = _ref.hashbang;
|
954
|
+
var onChange = _ref.onChange;
|
955
|
+
babelHelpers.classCallCheck(this, HashHistory);
|
956
|
+
|
957
|
+
this.hashbang = hashbang;
|
958
|
+
this.onChange = onChange;
|
959
|
+
}
|
960
|
+
|
961
|
+
HashHistory.prototype.start = function start() {
|
962
|
+
var self = this;
|
963
|
+
this.listener = function () {
|
964
|
+
var path = location.hash;
|
965
|
+
var raw = path.replace(/^#!?/, '');
|
966
|
+
// always
|
967
|
+
if (raw.charAt(0) !== '/') {
|
968
|
+
raw = '/' + raw;
|
969
|
+
}
|
970
|
+
var formattedPath = self.formatPath(raw);
|
971
|
+
if (formattedPath !== path) {
|
972
|
+
location.replace(formattedPath);
|
973
|
+
return;
|
974
|
+
}
|
975
|
+
// determine query
|
976
|
+
// note it's possible to have queries in both the actual URL
|
977
|
+
// and the hash fragment itself.
|
978
|
+
var query = location.search && path.indexOf('?') > -1 ? '&' + location.search.slice(1) : location.search;
|
979
|
+
self.onChange(path.replace(/^#!?/, '') + query);
|
980
|
+
};
|
981
|
+
window.addEventListener('hashchange', this.listener);
|
982
|
+
this.listener();
|
983
|
+
};
|
984
|
+
|
985
|
+
HashHistory.prototype.stop = function stop() {
|
986
|
+
window.removeEventListener('hashchange', this.listener);
|
987
|
+
};
|
988
|
+
|
989
|
+
HashHistory.prototype.go = function go(path, replace, append) {
|
990
|
+
path = this.formatPath(path, append);
|
991
|
+
if (replace) {
|
992
|
+
location.replace(path);
|
993
|
+
} else {
|
994
|
+
location.hash = path;
|
995
|
+
}
|
996
|
+
};
|
997
|
+
|
998
|
+
HashHistory.prototype.formatPath = function formatPath(path, append) {
|
999
|
+
var isAbsoloute = path.charAt(0) === '/';
|
1000
|
+
var prefix = '#' + (this.hashbang ? '!' : '');
|
1001
|
+
return isAbsoloute ? prefix + path : prefix + resolvePath(location.hash.replace(/^#!?/, ''), path, append);
|
1002
|
+
};
|
1003
|
+
|
1004
|
+
return HashHistory;
|
1005
|
+
})();
|
1006
|
+
|
1007
|
+
var AbstractHistory = (function () {
|
1008
|
+
function AbstractHistory(_ref) {
|
1009
|
+
var onChange = _ref.onChange;
|
1010
|
+
babelHelpers.classCallCheck(this, AbstractHistory);
|
1011
|
+
|
1012
|
+
this.onChange = onChange;
|
1013
|
+
this.currentPath = '/';
|
1014
|
+
}
|
1015
|
+
|
1016
|
+
AbstractHistory.prototype.start = function start() {
|
1017
|
+
this.onChange('/');
|
1018
|
+
};
|
1019
|
+
|
1020
|
+
AbstractHistory.prototype.stop = function stop() {
|
1021
|
+
// noop
|
1022
|
+
};
|
1023
|
+
|
1024
|
+
AbstractHistory.prototype.go = function go(path, replace, append) {
|
1025
|
+
path = this.currentPath = this.formatPath(path, append);
|
1026
|
+
this.onChange(path);
|
1027
|
+
};
|
1028
|
+
|
1029
|
+
AbstractHistory.prototype.formatPath = function formatPath(path, append) {
|
1030
|
+
return path.charAt(0) === '/' ? path : resolvePath(this.currentPath, path, append);
|
1031
|
+
};
|
1032
|
+
|
1033
|
+
return AbstractHistory;
|
1034
|
+
})();
|
1035
|
+
|
1036
|
+
/**
|
1037
|
+
* Determine the reusability of an existing router view.
|
1038
|
+
*
|
1039
|
+
* @param {Directive} view
|
1040
|
+
* @param {Object} handler
|
1041
|
+
* @param {Transition} transition
|
1042
|
+
*/
|
1043
|
+
|
1044
|
+
function canReuse(view, handler, transition) {
|
1045
|
+
var component = view.childVM;
|
1046
|
+
if (!component || !handler) {
|
1047
|
+
return false;
|
1048
|
+
}
|
1049
|
+
// important: check view.Component here because it may
|
1050
|
+
// have been changed in activate hook
|
1051
|
+
if (view.Component !== handler.component) {
|
1052
|
+
return false;
|
1053
|
+
}
|
1054
|
+
var canReuseFn = getRouteConfig(component, 'canReuse');
|
1055
|
+
return typeof canReuseFn === 'boolean' ? canReuseFn : canReuseFn ? canReuseFn.call(component, {
|
1056
|
+
to: transition.to,
|
1057
|
+
from: transition.from
|
1058
|
+
}) : true; // defaults to true
|
1059
|
+
}
|
1060
|
+
|
1061
|
+
/**
|
1062
|
+
* Check if a component can deactivate.
|
1063
|
+
*
|
1064
|
+
* @param {Directive} view
|
1065
|
+
* @param {Transition} transition
|
1066
|
+
* @param {Function} next
|
1067
|
+
*/
|
1068
|
+
|
1069
|
+
function canDeactivate(view, transition, next) {
|
1070
|
+
var fromComponent = view.childVM;
|
1071
|
+
var hook = getRouteConfig(fromComponent, 'canDeactivate');
|
1072
|
+
if (!hook) {
|
1073
|
+
next();
|
1074
|
+
} else {
|
1075
|
+
transition.callHook(hook, fromComponent, next, {
|
1076
|
+
expectBoolean: true
|
1077
|
+
});
|
1078
|
+
}
|
1079
|
+
}
|
1080
|
+
|
1081
|
+
/**
|
1082
|
+
* Check if a component can activate.
|
1083
|
+
*
|
1084
|
+
* @param {Object} handler
|
1085
|
+
* @param {Transition} transition
|
1086
|
+
* @param {Function} next
|
1087
|
+
*/
|
1088
|
+
|
1089
|
+
function canActivate(handler, transition, next) {
|
1090
|
+
resolveAsyncComponent(handler, function (Component) {
|
1091
|
+
// have to check due to async-ness
|
1092
|
+
if (transition.aborted) {
|
1093
|
+
return;
|
1094
|
+
}
|
1095
|
+
// determine if this component can be activated
|
1096
|
+
var hook = getRouteConfig(Component, 'canActivate');
|
1097
|
+
if (!hook) {
|
1098
|
+
next();
|
1099
|
+
} else {
|
1100
|
+
transition.callHook(hook, null, next, {
|
1101
|
+
expectBoolean: true
|
1102
|
+
});
|
1103
|
+
}
|
1104
|
+
});
|
1105
|
+
}
|
1106
|
+
|
1107
|
+
/**
|
1108
|
+
* Call deactivate hooks for existing router-views.
|
1109
|
+
*
|
1110
|
+
* @param {Directive} view
|
1111
|
+
* @param {Transition} transition
|
1112
|
+
* @param {Function} next
|
1113
|
+
*/
|
1114
|
+
|
1115
|
+
function deactivate(view, transition, next) {
|
1116
|
+
var component = view.childVM;
|
1117
|
+
var hook = getRouteConfig(component, 'deactivate');
|
1118
|
+
if (!hook) {
|
1119
|
+
next();
|
1120
|
+
} else {
|
1121
|
+
transition.callHooks(hook, component, next);
|
1122
|
+
}
|
1123
|
+
}
|
1124
|
+
|
1125
|
+
/**
|
1126
|
+
* Activate / switch component for a router-view.
|
1127
|
+
*
|
1128
|
+
* @param {Directive} view
|
1129
|
+
* @param {Transition} transition
|
1130
|
+
* @param {Number} depth
|
1131
|
+
* @param {Function} [cb]
|
1132
|
+
*/
|
1133
|
+
|
1134
|
+
function activate(view, transition, depth, cb, reuse) {
|
1135
|
+
var handler = transition.activateQueue[depth];
|
1136
|
+
if (!handler) {
|
1137
|
+
saveChildView(view);
|
1138
|
+
if (view._bound) {
|
1139
|
+
view.setComponent(null);
|
1140
|
+
}
|
1141
|
+
cb && cb();
|
1142
|
+
return;
|
1143
|
+
}
|
1144
|
+
|
1145
|
+
var Component = view.Component = handler.component;
|
1146
|
+
var activateHook = getRouteConfig(Component, 'activate');
|
1147
|
+
var dataHook = getRouteConfig(Component, 'data');
|
1148
|
+
var waitForData = getRouteConfig(Component, 'waitForData');
|
1149
|
+
|
1150
|
+
view.depth = depth;
|
1151
|
+
view.activated = false;
|
1152
|
+
|
1153
|
+
var component = undefined;
|
1154
|
+
var loading = !!(dataHook && !waitForData);
|
1155
|
+
|
1156
|
+
// "reuse" is a flag passed down when the parent view is
|
1157
|
+
// either reused via keep-alive or as a child of a kept-alive view.
|
1158
|
+
// of course we can only reuse if the current kept-alive instance
|
1159
|
+
// is of the correct type.
|
1160
|
+
reuse = reuse && view.childVM && view.childVM.constructor === Component;
|
1161
|
+
|
1162
|
+
if (reuse) {
|
1163
|
+
// just reuse
|
1164
|
+
component = view.childVM;
|
1165
|
+
component.$loadingRouteData = loading;
|
1166
|
+
} else {
|
1167
|
+
saveChildView(view);
|
1168
|
+
|
1169
|
+
// unbuild current component. this step also destroys
|
1170
|
+
// and removes all nested child views.
|
1171
|
+
view.unbuild(true);
|
1172
|
+
|
1173
|
+
// build the new component. this will also create the
|
1174
|
+
// direct child view of the current one. it will register
|
1175
|
+
// itself as view.childView.
|
1176
|
+
component = view.build({
|
1177
|
+
_meta: {
|
1178
|
+
$loadingRouteData: loading
|
1179
|
+
},
|
1180
|
+
created: function created() {
|
1181
|
+
this._routerView = view;
|
1182
|
+
}
|
1183
|
+
});
|
1184
|
+
|
1185
|
+
// handle keep-alive.
|
1186
|
+
// when a kept-alive child vm is restored, we need to
|
1187
|
+
// add its cached child views into the router's view list,
|
1188
|
+
// and also properly update current view's child view.
|
1189
|
+
if (view.keepAlive) {
|
1190
|
+
component.$loadingRouteData = loading;
|
1191
|
+
var cachedChildView = component._keepAliveRouterView;
|
1192
|
+
if (cachedChildView) {
|
1193
|
+
view.childView = cachedChildView;
|
1194
|
+
component._keepAliveRouterView = null;
|
1195
|
+
}
|
1196
|
+
}
|
1197
|
+
}
|
1198
|
+
|
1199
|
+
// cleanup the component in case the transition is aborted
|
1200
|
+
// before the component is ever inserted.
|
1201
|
+
var cleanup = function cleanup() {
|
1202
|
+
component.$destroy();
|
1203
|
+
};
|
1204
|
+
|
1205
|
+
// actually insert the component and trigger transition
|
1206
|
+
var insert = function insert() {
|
1207
|
+
if (reuse) {
|
1208
|
+
cb && cb();
|
1209
|
+
return;
|
1210
|
+
}
|
1211
|
+
var router = transition.router;
|
1212
|
+
if (router._rendered || router._transitionOnLoad) {
|
1213
|
+
view.transition(component);
|
1214
|
+
} else {
|
1215
|
+
// no transition on first render, manual transition
|
1216
|
+
/* istanbul ignore if */
|
1217
|
+
if (view.setCurrent) {
|
1218
|
+
// 0.12 compat
|
1219
|
+
view.setCurrent(component);
|
1220
|
+
} else {
|
1221
|
+
// 1.0
|
1222
|
+
view.childVM = component;
|
1223
|
+
}
|
1224
|
+
component.$before(view.anchor, null, false);
|
1225
|
+
}
|
1226
|
+
cb && cb();
|
1227
|
+
};
|
1228
|
+
|
1229
|
+
var afterData = function afterData() {
|
1230
|
+
// activate the child view
|
1231
|
+
if (view.childView) {
|
1232
|
+
activate(view.childView, transition, depth + 1, null, reuse || view.keepAlive);
|
1233
|
+
}
|
1234
|
+
insert();
|
1235
|
+
};
|
1236
|
+
|
1237
|
+
// called after activation hook is resolved
|
1238
|
+
var afterActivate = function afterActivate() {
|
1239
|
+
view.activated = true;
|
1240
|
+
if (dataHook && waitForData) {
|
1241
|
+
// wait until data loaded to insert
|
1242
|
+
loadData(component, transition, dataHook, afterData, cleanup);
|
1243
|
+
} else {
|
1244
|
+
// load data and insert at the same time
|
1245
|
+
if (dataHook) {
|
1246
|
+
loadData(component, transition, dataHook);
|
1247
|
+
}
|
1248
|
+
afterData();
|
1249
|
+
}
|
1250
|
+
};
|
1251
|
+
|
1252
|
+
if (activateHook) {
|
1253
|
+
transition.callHooks(activateHook, component, afterActivate, {
|
1254
|
+
cleanup: cleanup,
|
1255
|
+
postActivate: true
|
1256
|
+
});
|
1257
|
+
} else {
|
1258
|
+
afterActivate();
|
1259
|
+
}
|
1260
|
+
}
|
1261
|
+
|
1262
|
+
/**
|
1263
|
+
* Reuse a view, just reload data if necessary.
|
1264
|
+
*
|
1265
|
+
* @param {Directive} view
|
1266
|
+
* @param {Transition} transition
|
1267
|
+
*/
|
1268
|
+
|
1269
|
+
function reuse(view, transition) {
|
1270
|
+
var component = view.childVM;
|
1271
|
+
var dataHook = getRouteConfig(component, 'data');
|
1272
|
+
if (dataHook) {
|
1273
|
+
loadData(component, transition, dataHook);
|
1274
|
+
}
|
1275
|
+
}
|
1276
|
+
|
1277
|
+
/**
|
1278
|
+
* Asynchronously load and apply data to component.
|
1279
|
+
*
|
1280
|
+
* @param {Vue} component
|
1281
|
+
* @param {Transition} transition
|
1282
|
+
* @param {Function} hook
|
1283
|
+
* @param {Function} cb
|
1284
|
+
* @param {Function} cleanup
|
1285
|
+
*/
|
1286
|
+
|
1287
|
+
function loadData(component, transition, hook, cb, cleanup) {
|
1288
|
+
component.$loadingRouteData = true;
|
1289
|
+
transition.callHooks(hook, component, function () {
|
1290
|
+
component.$loadingRouteData = false;
|
1291
|
+
component.$emit('route-data-loaded', component);
|
1292
|
+
cb && cb();
|
1293
|
+
}, {
|
1294
|
+
cleanup: cleanup,
|
1295
|
+
postActivate: true,
|
1296
|
+
processData: function processData(data) {
|
1297
|
+
// handle promise sugar syntax
|
1298
|
+
var promises = [];
|
1299
|
+
if (isPlainObject(data)) {
|
1300
|
+
Object.keys(data).forEach(function (key) {
|
1301
|
+
var val = data[key];
|
1302
|
+
if (isPromise(val)) {
|
1303
|
+
promises.push(val.then(function (resolvedVal) {
|
1304
|
+
component.$set(key, resolvedVal);
|
1305
|
+
}));
|
1306
|
+
} else {
|
1307
|
+
component.$set(key, val);
|
1308
|
+
}
|
1309
|
+
});
|
1310
|
+
}
|
1311
|
+
if (promises.length) {
|
1312
|
+
return promises[0].constructor.all(promises);
|
1313
|
+
}
|
1314
|
+
}
|
1315
|
+
});
|
1316
|
+
}
|
1317
|
+
|
1318
|
+
/**
|
1319
|
+
* Save the child view for a kept-alive view so that
|
1320
|
+
* we can restore it when it is switched back to.
|
1321
|
+
*
|
1322
|
+
* @param {Directive} view
|
1323
|
+
*/
|
1324
|
+
|
1325
|
+
function saveChildView(view) {
|
1326
|
+
if (view.keepAlive && view.childVM && view.childView) {
|
1327
|
+
view.childVM._keepAliveRouterView = view.childView;
|
1328
|
+
}
|
1329
|
+
view.childView = null;
|
1330
|
+
}
|
1331
|
+
|
1332
|
+
/**
|
1333
|
+
* Check plain object.
|
1334
|
+
*
|
1335
|
+
* @param {*} val
|
1336
|
+
*/
|
1337
|
+
|
1338
|
+
function isPlainObject(val) {
|
1339
|
+
return Object.prototype.toString.call(val) === '[object Object]';
|
1340
|
+
}
|
1341
|
+
|
1342
|
+
/**
|
1343
|
+
* A RouteTransition object manages the pipeline of a
|
1344
|
+
* router-view switching process. This is also the object
|
1345
|
+
* passed into user route hooks.
|
1346
|
+
*
|
1347
|
+
* @param {Router} router
|
1348
|
+
* @param {Route} to
|
1349
|
+
* @param {Route} from
|
1350
|
+
*/
|
1351
|
+
|
1352
|
+
var RouteTransition = (function () {
|
1353
|
+
function RouteTransition(router, to, from) {
|
1354
|
+
babelHelpers.classCallCheck(this, RouteTransition);
|
1355
|
+
|
1356
|
+
this.router = router;
|
1357
|
+
this.to = to;
|
1358
|
+
this.from = from;
|
1359
|
+
this.next = null;
|
1360
|
+
this.aborted = false;
|
1361
|
+
this.done = false;
|
1362
|
+
}
|
1363
|
+
|
1364
|
+
/**
|
1365
|
+
* Abort current transition and return to previous location.
|
1366
|
+
*/
|
1367
|
+
|
1368
|
+
RouteTransition.prototype.abort = function abort() {
|
1369
|
+
if (!this.aborted) {
|
1370
|
+
this.aborted = true;
|
1371
|
+
// if the root path throws an error during validation
|
1372
|
+
// on initial load, it gets caught in an infinite loop.
|
1373
|
+
var abortingOnLoad = !this.from.path && this.to.path === '/';
|
1374
|
+
if (!abortingOnLoad) {
|
1375
|
+
this.router.replace(this.from.path || '/');
|
1376
|
+
}
|
1377
|
+
}
|
1378
|
+
};
|
1379
|
+
|
1380
|
+
/**
|
1381
|
+
* Abort current transition and redirect to a new location.
|
1382
|
+
*
|
1383
|
+
* @param {String} path
|
1384
|
+
*/
|
1385
|
+
|
1386
|
+
RouteTransition.prototype.redirect = function redirect(path) {
|
1387
|
+
if (!this.aborted) {
|
1388
|
+
this.aborted = true;
|
1389
|
+
if (typeof path === 'string') {
|
1390
|
+
path = mapParams(path, this.to.params, this.to.query);
|
1391
|
+
} else {
|
1392
|
+
path.params = path.params || this.to.params;
|
1393
|
+
path.query = path.query || this.to.query;
|
1394
|
+
}
|
1395
|
+
this.router.replace(path);
|
1396
|
+
}
|
1397
|
+
};
|
1398
|
+
|
1399
|
+
/**
|
1400
|
+
* A router view transition's pipeline can be described as
|
1401
|
+
* follows, assuming we are transitioning from an existing
|
1402
|
+
* <router-view> chain [Component A, Component B] to a new
|
1403
|
+
* chain [Component A, Component C]:
|
1404
|
+
*
|
1405
|
+
* A A
|
1406
|
+
* | => |
|
1407
|
+
* B C
|
1408
|
+
*
|
1409
|
+
* 1. Reusablity phase:
|
1410
|
+
* -> canReuse(A, A)
|
1411
|
+
* -> canReuse(B, C)
|
1412
|
+
* -> determine new queues:
|
1413
|
+
* - deactivation: [B]
|
1414
|
+
* - activation: [C]
|
1415
|
+
*
|
1416
|
+
* 2. Validation phase:
|
1417
|
+
* -> canDeactivate(B)
|
1418
|
+
* -> canActivate(C)
|
1419
|
+
*
|
1420
|
+
* 3. Activation phase:
|
1421
|
+
* -> deactivate(B)
|
1422
|
+
* -> activate(C)
|
1423
|
+
*
|
1424
|
+
* Each of these steps can be asynchronous, and any
|
1425
|
+
* step can potentially abort the transition.
|
1426
|
+
*
|
1427
|
+
* @param {Function} cb
|
1428
|
+
*/
|
1429
|
+
|
1430
|
+
RouteTransition.prototype.start = function start(cb) {
|
1431
|
+
var transition = this;
|
1432
|
+
|
1433
|
+
// determine the queue of views to deactivate
|
1434
|
+
var deactivateQueue = [];
|
1435
|
+
var view = this.router._rootView;
|
1436
|
+
while (view) {
|
1437
|
+
deactivateQueue.unshift(view);
|
1438
|
+
view = view.childView;
|
1439
|
+
}
|
1440
|
+
var reverseDeactivateQueue = deactivateQueue.slice().reverse();
|
1441
|
+
|
1442
|
+
// determine the queue of route handlers to activate
|
1443
|
+
var activateQueue = this.activateQueue = toArray(this.to.matched).map(function (match) {
|
1444
|
+
return match.handler;
|
1445
|
+
});
|
1446
|
+
|
1447
|
+
// 1. Reusability phase
|
1448
|
+
var i = undefined,
|
1449
|
+
reuseQueue = undefined;
|
1450
|
+
for (i = 0; i < reverseDeactivateQueue.length; i++) {
|
1451
|
+
if (!canReuse(reverseDeactivateQueue[i], activateQueue[i], transition)) {
|
1452
|
+
break;
|
1453
|
+
}
|
1454
|
+
}
|
1455
|
+
if (i > 0) {
|
1456
|
+
reuseQueue = reverseDeactivateQueue.slice(0, i);
|
1457
|
+
deactivateQueue = reverseDeactivateQueue.slice(i).reverse();
|
1458
|
+
activateQueue = activateQueue.slice(i);
|
1459
|
+
}
|
1460
|
+
|
1461
|
+
// 2. Validation phase
|
1462
|
+
transition.runQueue(deactivateQueue, canDeactivate, function () {
|
1463
|
+
transition.runQueue(activateQueue, canActivate, function () {
|
1464
|
+
transition.runQueue(deactivateQueue, deactivate, function () {
|
1465
|
+
// 3. Activation phase
|
1466
|
+
|
1467
|
+
// Update router current route
|
1468
|
+
transition.router._onTransitionValidated(transition);
|
1469
|
+
|
1470
|
+
// trigger reuse for all reused views
|
1471
|
+
reuseQueue && reuseQueue.forEach(function (view) {
|
1472
|
+
return reuse(view, transition);
|
1473
|
+
});
|
1474
|
+
|
1475
|
+
// the root of the chain that needs to be replaced
|
1476
|
+
// is the top-most non-reusable view.
|
1477
|
+
if (deactivateQueue.length) {
|
1478
|
+
var _view = deactivateQueue[deactivateQueue.length - 1];
|
1479
|
+
var depth = reuseQueue ? reuseQueue.length : 0;
|
1480
|
+
activate(_view, transition, depth, cb);
|
1481
|
+
} else {
|
1482
|
+
cb();
|
1483
|
+
}
|
1484
|
+
});
|
1485
|
+
});
|
1486
|
+
});
|
1487
|
+
};
|
1488
|
+
|
1489
|
+
/**
|
1490
|
+
* Asynchronously and sequentially apply a function to a
|
1491
|
+
* queue.
|
1492
|
+
*
|
1493
|
+
* @param {Array} queue
|
1494
|
+
* @param {Function} fn
|
1495
|
+
* @param {Function} cb
|
1496
|
+
*/
|
1497
|
+
|
1498
|
+
RouteTransition.prototype.runQueue = function runQueue(queue, fn, cb) {
|
1499
|
+
var transition = this;
|
1500
|
+
step(0);
|
1501
|
+
function step(index) {
|
1502
|
+
if (index >= queue.length) {
|
1503
|
+
cb();
|
1504
|
+
} else {
|
1505
|
+
fn(queue[index], transition, function () {
|
1506
|
+
step(index + 1);
|
1507
|
+
});
|
1508
|
+
}
|
1509
|
+
}
|
1510
|
+
};
|
1511
|
+
|
1512
|
+
/**
|
1513
|
+
* Call a user provided route transition hook and handle
|
1514
|
+
* the response (e.g. if the user returns a promise).
|
1515
|
+
*
|
1516
|
+
* If the user neither expects an argument nor returns a
|
1517
|
+
* promise, the hook is assumed to be synchronous.
|
1518
|
+
*
|
1519
|
+
* @param {Function} hook
|
1520
|
+
* @param {*} [context]
|
1521
|
+
* @param {Function} [cb]
|
1522
|
+
* @param {Object} [options]
|
1523
|
+
* - {Boolean} expectBoolean
|
1524
|
+
* - {Boolean} postActive
|
1525
|
+
* - {Function} processData
|
1526
|
+
* - {Function} cleanup
|
1527
|
+
*/
|
1528
|
+
|
1529
|
+
RouteTransition.prototype.callHook = function callHook(hook, context, cb) {
|
1530
|
+
var _ref = arguments.length <= 3 || arguments[3] === undefined ? {} : arguments[3];
|
1531
|
+
|
1532
|
+
var _ref$expectBoolean = _ref.expectBoolean;
|
1533
|
+
var expectBoolean = _ref$expectBoolean === undefined ? false : _ref$expectBoolean;
|
1534
|
+
var _ref$postActivate = _ref.postActivate;
|
1535
|
+
var postActivate = _ref$postActivate === undefined ? false : _ref$postActivate;
|
1536
|
+
var processData = _ref.processData;
|
1537
|
+
var cleanup = _ref.cleanup;
|
1538
|
+
|
1539
|
+
var transition = this;
|
1540
|
+
var nextCalled = false;
|
1541
|
+
|
1542
|
+
// abort the transition
|
1543
|
+
var abort = function abort() {
|
1544
|
+
cleanup && cleanup();
|
1545
|
+
transition.abort();
|
1546
|
+
};
|
1547
|
+
|
1548
|
+
// handle errors
|
1549
|
+
var onError = function onError(err) {
|
1550
|
+
postActivate ? next() : abort();
|
1551
|
+
if (err && !transition.router._suppress) {
|
1552
|
+
warn$1('Uncaught error during transition: ');
|
1553
|
+
throw err instanceof Error ? err : new Error(err);
|
1554
|
+
}
|
1555
|
+
};
|
1556
|
+
|
1557
|
+
// since promise swallows errors, we have to
|
1558
|
+
// throw it in the next tick...
|
1559
|
+
var onPromiseError = function onPromiseError(err) {
|
1560
|
+
try {
|
1561
|
+
onError(err);
|
1562
|
+
} catch (e) {
|
1563
|
+
setTimeout(function () {
|
1564
|
+
throw e;
|
1565
|
+
}, 0);
|
1566
|
+
}
|
1567
|
+
};
|
1568
|
+
|
1569
|
+
// advance the transition to the next step
|
1570
|
+
var next = function next() {
|
1571
|
+
if (nextCalled) {
|
1572
|
+
warn$1('transition.next() should be called only once.');
|
1573
|
+
return;
|
1574
|
+
}
|
1575
|
+
nextCalled = true;
|
1576
|
+
if (transition.aborted) {
|
1577
|
+
cleanup && cleanup();
|
1578
|
+
return;
|
1579
|
+
}
|
1580
|
+
cb && cb();
|
1581
|
+
};
|
1582
|
+
|
1583
|
+
var nextWithBoolean = function nextWithBoolean(res) {
|
1584
|
+
if (typeof res === 'boolean') {
|
1585
|
+
res ? next() : abort();
|
1586
|
+
} else if (isPromise(res)) {
|
1587
|
+
res.then(function (ok) {
|
1588
|
+
ok ? next() : abort();
|
1589
|
+
}, onPromiseError);
|
1590
|
+
} else if (!hook.length) {
|
1591
|
+
next();
|
1592
|
+
}
|
1593
|
+
};
|
1594
|
+
|
1595
|
+
var nextWithData = function nextWithData(data) {
|
1596
|
+
var res = undefined;
|
1597
|
+
try {
|
1598
|
+
res = processData(data);
|
1599
|
+
} catch (err) {
|
1600
|
+
return onError(err);
|
1601
|
+
}
|
1602
|
+
if (isPromise(res)) {
|
1603
|
+
res.then(next, onPromiseError);
|
1604
|
+
} else {
|
1605
|
+
next();
|
1606
|
+
}
|
1607
|
+
};
|
1608
|
+
|
1609
|
+
// expose a clone of the transition object, so that each
|
1610
|
+
// hook gets a clean copy and prevent the user from
|
1611
|
+
// messing with the internals.
|
1612
|
+
var exposed = {
|
1613
|
+
to: transition.to,
|
1614
|
+
from: transition.from,
|
1615
|
+
abort: abort,
|
1616
|
+
next: processData ? nextWithData : next,
|
1617
|
+
redirect: function redirect() {
|
1618
|
+
transition.redirect.apply(transition, arguments);
|
1619
|
+
}
|
1620
|
+
};
|
1621
|
+
|
1622
|
+
// actually call the hook
|
1623
|
+
var res = undefined;
|
1624
|
+
try {
|
1625
|
+
res = hook.call(context, exposed);
|
1626
|
+
} catch (err) {
|
1627
|
+
return onError(err);
|
1628
|
+
}
|
1629
|
+
|
1630
|
+
if (expectBoolean) {
|
1631
|
+
// boolean hooks
|
1632
|
+
nextWithBoolean(res);
|
1633
|
+
} else if (isPromise(res)) {
|
1634
|
+
// promise
|
1635
|
+
if (processData) {
|
1636
|
+
res.then(nextWithData, onPromiseError);
|
1637
|
+
} else {
|
1638
|
+
res.then(next, onPromiseError);
|
1639
|
+
}
|
1640
|
+
} else if (processData && isPlainOjbect(res)) {
|
1641
|
+
// data promise sugar
|
1642
|
+
nextWithData(res);
|
1643
|
+
} else if (!hook.length) {
|
1644
|
+
next();
|
1645
|
+
}
|
1646
|
+
};
|
1647
|
+
|
1648
|
+
/**
|
1649
|
+
* Call a single hook or an array of async hooks in series.
|
1650
|
+
*
|
1651
|
+
* @param {Array} hooks
|
1652
|
+
* @param {*} context
|
1653
|
+
* @param {Function} cb
|
1654
|
+
* @param {Object} [options]
|
1655
|
+
*/
|
1656
|
+
|
1657
|
+
RouteTransition.prototype.callHooks = function callHooks(hooks, context, cb, options) {
|
1658
|
+
var _this = this;
|
1659
|
+
|
1660
|
+
if (Array.isArray(hooks)) {
|
1661
|
+
this.runQueue(hooks, function (hook, _, next) {
|
1662
|
+
if (!_this.aborted) {
|
1663
|
+
_this.callHook(hook, context, next, options);
|
1664
|
+
}
|
1665
|
+
}, cb);
|
1666
|
+
} else {
|
1667
|
+
this.callHook(hooks, context, cb, options);
|
1668
|
+
}
|
1669
|
+
};
|
1670
|
+
|
1671
|
+
return RouteTransition;
|
1672
|
+
})();
|
1673
|
+
|
1674
|
+
function isPlainOjbect(val) {
|
1675
|
+
return Object.prototype.toString.call(val) === '[object Object]';
|
1676
|
+
}
|
1677
|
+
|
1678
|
+
function toArray(val) {
|
1679
|
+
return val ? Array.prototype.slice.call(val) : [];
|
1680
|
+
}
|
1681
|
+
|
1682
|
+
var internalKeysRE = /^(component|subRoutes|fullPath)$/;
|
1683
|
+
|
1684
|
+
/**
|
1685
|
+
* Route Context Object
|
1686
|
+
*
|
1687
|
+
* @param {String} path
|
1688
|
+
* @param {Router} router
|
1689
|
+
*/
|
1690
|
+
|
1691
|
+
var Route = function Route(path, router) {
|
1692
|
+
var _this = this;
|
1693
|
+
|
1694
|
+
babelHelpers.classCallCheck(this, Route);
|
1695
|
+
|
1696
|
+
var matched = router._recognizer.recognize(path);
|
1697
|
+
if (matched) {
|
1698
|
+
// copy all custom fields from route configs
|
1699
|
+
[].forEach.call(matched, function (match) {
|
1700
|
+
for (var key in match.handler) {
|
1701
|
+
if (!internalKeysRE.test(key)) {
|
1702
|
+
_this[key] = match.handler[key];
|
1703
|
+
}
|
1704
|
+
}
|
1705
|
+
});
|
1706
|
+
// set query and params
|
1707
|
+
this.query = matched.queryParams;
|
1708
|
+
this.params = [].reduce.call(matched, function (prev, cur) {
|
1709
|
+
if (cur.params) {
|
1710
|
+
for (var key in cur.params) {
|
1711
|
+
prev[key] = cur.params[key];
|
1712
|
+
}
|
1713
|
+
}
|
1714
|
+
return prev;
|
1715
|
+
}, {});
|
1716
|
+
}
|
1717
|
+
// expose path and router
|
1718
|
+
this.path = path;
|
1719
|
+
// for internal use
|
1720
|
+
this.matched = matched || router._notFoundHandler;
|
1721
|
+
// internal reference to router
|
1722
|
+
Object.defineProperty(this, 'router', {
|
1723
|
+
enumerable: false,
|
1724
|
+
value: router
|
1725
|
+
});
|
1726
|
+
// Important: freeze self to prevent observation
|
1727
|
+
Object.freeze(this);
|
1728
|
+
};
|
1729
|
+
|
1730
|
+
function applyOverride (Vue) {
|
1731
|
+
var _Vue$util = Vue.util;
|
1732
|
+
var extend = _Vue$util.extend;
|
1733
|
+
var isArray = _Vue$util.isArray;
|
1734
|
+
var defineReactive = _Vue$util.defineReactive;
|
1735
|
+
|
1736
|
+
// override Vue's init and destroy process to keep track of router instances
|
1737
|
+
var init = Vue.prototype._init;
|
1738
|
+
Vue.prototype._init = function (options) {
|
1739
|
+
options = options || {};
|
1740
|
+
var root = options._parent || options.parent || this;
|
1741
|
+
var router = root.$router;
|
1742
|
+
var route = root.$route;
|
1743
|
+
if (router) {
|
1744
|
+
// expose router
|
1745
|
+
this.$router = router;
|
1746
|
+
router._children.push(this);
|
1747
|
+
/* istanbul ignore if */
|
1748
|
+
if (this._defineMeta) {
|
1749
|
+
// 0.12
|
1750
|
+
this._defineMeta('$route', route);
|
1751
|
+
} else {
|
1752
|
+
// 1.0
|
1753
|
+
defineReactive(this, '$route', route);
|
1754
|
+
}
|
1755
|
+
}
|
1756
|
+
init.call(this, options);
|
1757
|
+
};
|
1758
|
+
|
1759
|
+
var destroy = Vue.prototype._destroy;
|
1760
|
+
Vue.prototype._destroy = function () {
|
1761
|
+
if (!this._isBeingDestroyed && this.$router) {
|
1762
|
+
this.$router._children.$remove(this);
|
1763
|
+
}
|
1764
|
+
destroy.apply(this, arguments);
|
1765
|
+
};
|
1766
|
+
|
1767
|
+
// 1.0 only: enable route mixins
|
1768
|
+
var strats = Vue.config.optionMergeStrategies;
|
1769
|
+
var hooksToMergeRE = /^(data|activate|deactivate)$/;
|
1770
|
+
|
1771
|
+
if (strats) {
|
1772
|
+
strats.route = function (parentVal, childVal) {
|
1773
|
+
if (!childVal) return parentVal;
|
1774
|
+
if (!parentVal) return childVal;
|
1775
|
+
var ret = {};
|
1776
|
+
extend(ret, parentVal);
|
1777
|
+
for (var key in childVal) {
|
1778
|
+
var a = ret[key];
|
1779
|
+
var b = childVal[key];
|
1780
|
+
// for data, activate and deactivate, we need to merge them into
|
1781
|
+
// arrays similar to lifecycle hooks.
|
1782
|
+
if (a && hooksToMergeRE.test(key)) {
|
1783
|
+
ret[key] = (isArray(a) ? a : [a]).concat(b);
|
1784
|
+
} else {
|
1785
|
+
ret[key] = b;
|
1786
|
+
}
|
1787
|
+
}
|
1788
|
+
return ret;
|
1789
|
+
};
|
1790
|
+
}
|
1791
|
+
}
|
1792
|
+
|
1793
|
+
function View (Vue) {
|
1794
|
+
|
1795
|
+
var _ = Vue.util;
|
1796
|
+
var componentDef =
|
1797
|
+
// 0.12
|
1798
|
+
Vue.directive('_component') ||
|
1799
|
+
// 1.0
|
1800
|
+
Vue.internalDirectives.component;
|
1801
|
+
// <router-view> extends the internal component directive
|
1802
|
+
var viewDef = _.extend({}, componentDef);
|
1803
|
+
|
1804
|
+
// with some overrides
|
1805
|
+
_.extend(viewDef, {
|
1806
|
+
|
1807
|
+
_isRouterView: true,
|
1808
|
+
|
1809
|
+
bind: function bind() {
|
1810
|
+
var route = this.vm.$route;
|
1811
|
+
/* istanbul ignore if */
|
1812
|
+
if (!route) {
|
1813
|
+
warn$1('<router-view> can only be used inside a ' + 'router-enabled app.');
|
1814
|
+
return;
|
1815
|
+
}
|
1816
|
+
// force dynamic directive so v-component doesn't
|
1817
|
+
// attempt to build right now
|
1818
|
+
this._isDynamicLiteral = true;
|
1819
|
+
// finally, init by delegating to v-component
|
1820
|
+
componentDef.bind.call(this);
|
1821
|
+
|
1822
|
+
// locate the parent view
|
1823
|
+
var parentView = undefined;
|
1824
|
+
var parent = this.vm;
|
1825
|
+
while (parent) {
|
1826
|
+
if (parent._routerView) {
|
1827
|
+
parentView = parent._routerView;
|
1828
|
+
break;
|
1829
|
+
}
|
1830
|
+
parent = parent.$parent;
|
1831
|
+
}
|
1832
|
+
if (parentView) {
|
1833
|
+
// register self as a child of the parent view,
|
1834
|
+
// instead of activating now. This is so that the
|
1835
|
+
// child's activate hook is called after the
|
1836
|
+
// parent's has resolved.
|
1837
|
+
this.parentView = parentView;
|
1838
|
+
parentView.childView = this;
|
1839
|
+
} else {
|
1840
|
+
// this is the root view!
|
1841
|
+
var router = route.router;
|
1842
|
+
router._rootView = this;
|
1843
|
+
}
|
1844
|
+
|
1845
|
+
// handle late-rendered view
|
1846
|
+
// two possibilities:
|
1847
|
+
// 1. root view rendered after transition has been
|
1848
|
+
// validated;
|
1849
|
+
// 2. child view rendered after parent view has been
|
1850
|
+
// activated.
|
1851
|
+
var transition = route.router._currentTransition;
|
1852
|
+
if (!parentView && transition.done || parentView && parentView.activated) {
|
1853
|
+
var depth = parentView ? parentView.depth + 1 : 0;
|
1854
|
+
activate(this, transition, depth);
|
1855
|
+
}
|
1856
|
+
},
|
1857
|
+
|
1858
|
+
unbind: function unbind() {
|
1859
|
+
if (this.parentView) {
|
1860
|
+
this.parentView.childView = null;
|
1861
|
+
}
|
1862
|
+
componentDef.unbind.call(this);
|
1863
|
+
}
|
1864
|
+
});
|
1865
|
+
|
1866
|
+
Vue.elementDirective('router-view', viewDef);
|
1867
|
+
}
|
1868
|
+
|
1869
|
+
var trailingSlashRE = /\/$/;
|
1870
|
+
var regexEscapeRE = /[-.*+?^${}()|[\]\/\\]/g;
|
1871
|
+
var queryStringRE = /\?.*$/;
|
1872
|
+
|
1873
|
+
// install v-link, which provides navigation support for
|
1874
|
+
// HTML5 history mode
|
1875
|
+
function Link (Vue) {
|
1876
|
+
var _Vue$util = Vue.util;
|
1877
|
+
var _bind = _Vue$util.bind;
|
1878
|
+
var isObject = _Vue$util.isObject;
|
1879
|
+
var addClass = _Vue$util.addClass;
|
1880
|
+
var removeClass = _Vue$util.removeClass;
|
1881
|
+
|
1882
|
+
var onPriority = Vue.directive('on').priority;
|
1883
|
+
var LINK_UPDATE = '__vue-router-link-update__';
|
1884
|
+
|
1885
|
+
var activeId = 0;
|
1886
|
+
|
1887
|
+
Vue.directive('link-active', {
|
1888
|
+
priority: 9999,
|
1889
|
+
bind: function bind() {
|
1890
|
+
var _this = this;
|
1891
|
+
|
1892
|
+
var id = String(activeId++);
|
1893
|
+
// collect v-links contained within this element.
|
1894
|
+
// we need do this here before the parent-child relationship
|
1895
|
+
// gets messed up by terminal directives (if, for, components)
|
1896
|
+
var childLinks = this.el.querySelectorAll('[v-link]');
|
1897
|
+
for (var i = 0, l = childLinks.length; i < l; i++) {
|
1898
|
+
var link = childLinks[i];
|
1899
|
+
var existingId = link.getAttribute(LINK_UPDATE);
|
1900
|
+
var value = existingId ? existingId + ',' + id : id;
|
1901
|
+
// leave a mark on the link element which can be persisted
|
1902
|
+
// through fragment clones.
|
1903
|
+
link.setAttribute(LINK_UPDATE, value);
|
1904
|
+
}
|
1905
|
+
this.vm.$on(LINK_UPDATE, this.cb = function (link, path) {
|
1906
|
+
if (link.activeIds.indexOf(id) > -1) {
|
1907
|
+
link.updateClasses(path, _this.el);
|
1908
|
+
}
|
1909
|
+
});
|
1910
|
+
},
|
1911
|
+
unbind: function unbind() {
|
1912
|
+
this.vm.$off(LINK_UPDATE, this.cb);
|
1913
|
+
}
|
1914
|
+
});
|
1915
|
+
|
1916
|
+
Vue.directive('link', {
|
1917
|
+
priority: onPriority - 2,
|
1918
|
+
|
1919
|
+
bind: function bind() {
|
1920
|
+
var vm = this.vm;
|
1921
|
+
/* istanbul ignore if */
|
1922
|
+
if (!vm.$route) {
|
1923
|
+
warn$1('v-link can only be used inside a router-enabled app.');
|
1924
|
+
return;
|
1925
|
+
}
|
1926
|
+
this.router = vm.$route.router;
|
1927
|
+
// update things when the route changes
|
1928
|
+
this.unwatch = vm.$watch('$route', _bind(this.onRouteUpdate, this));
|
1929
|
+
// check v-link-active ids
|
1930
|
+
var activeIds = this.el.getAttribute(LINK_UPDATE);
|
1931
|
+
if (activeIds) {
|
1932
|
+
this.el.removeAttribute(LINK_UPDATE);
|
1933
|
+
this.activeIds = activeIds.split(',');
|
1934
|
+
}
|
1935
|
+
// no need to handle click if link expects to be opened
|
1936
|
+
// in a new window/tab.
|
1937
|
+
/* istanbul ignore if */
|
1938
|
+
if (this.el.tagName === 'A' && this.el.getAttribute('target') === '_blank') {
|
1939
|
+
return;
|
1940
|
+
}
|
1941
|
+
// handle click
|
1942
|
+
this.handler = _bind(this.onClick, this);
|
1943
|
+
this.el.addEventListener('click', this.handler);
|
1944
|
+
},
|
1945
|
+
|
1946
|
+
update: function update(target) {
|
1947
|
+
this.target = target;
|
1948
|
+
if (isObject(target)) {
|
1949
|
+
this.append = target.append;
|
1950
|
+
this.exact = target.exact;
|
1951
|
+
this.prevActiveClass = this.activeClass;
|
1952
|
+
this.activeClass = target.activeClass;
|
1953
|
+
}
|
1954
|
+
this.onRouteUpdate(this.vm.$route);
|
1955
|
+
},
|
1956
|
+
|
1957
|
+
onClick: function onClick(e) {
|
1958
|
+
// don't redirect with control keys
|
1959
|
+
/* istanbul ignore if */
|
1960
|
+
if (e.metaKey || e.ctrlKey || e.shiftKey) return;
|
1961
|
+
// don't redirect when preventDefault called
|
1962
|
+
/* istanbul ignore if */
|
1963
|
+
if (e.defaultPrevented) return;
|
1964
|
+
// don't redirect on right click
|
1965
|
+
/* istanbul ignore if */
|
1966
|
+
if (e.button !== 0) return;
|
1967
|
+
|
1968
|
+
var target = this.target;
|
1969
|
+
if (target) {
|
1970
|
+
// v-link with expression, just go
|
1971
|
+
e.preventDefault();
|
1972
|
+
this.router.go(target);
|
1973
|
+
} else {
|
1974
|
+
// no expression, delegate for an <a> inside
|
1975
|
+
var el = e.target;
|
1976
|
+
while (el.tagName !== 'A' && el !== this.el) {
|
1977
|
+
el = el.parentNode;
|
1978
|
+
}
|
1979
|
+
if (el.tagName === 'A' && sameOrigin(el)) {
|
1980
|
+
e.preventDefault();
|
1981
|
+
var path = el.pathname;
|
1982
|
+
if (this.router.history.root) {
|
1983
|
+
path = path.replace(this.router.history.rootRE, '');
|
1984
|
+
}
|
1985
|
+
this.router.go({
|
1986
|
+
path: path,
|
1987
|
+
replace: target && target.replace,
|
1988
|
+
append: target && target.append
|
1989
|
+
});
|
1990
|
+
}
|
1991
|
+
}
|
1992
|
+
},
|
1993
|
+
|
1994
|
+
onRouteUpdate: function onRouteUpdate(route) {
|
1995
|
+
// router.stringifyPath is dependent on current route
|
1996
|
+
// and needs to be called again whenver route changes.
|
1997
|
+
var newPath = this.router.stringifyPath(this.target);
|
1998
|
+
if (this.path !== newPath) {
|
1999
|
+
this.path = newPath;
|
2000
|
+
this.updateActiveMatch();
|
2001
|
+
this.updateHref();
|
2002
|
+
}
|
2003
|
+
if (this.activeIds) {
|
2004
|
+
this.vm.$emit(LINK_UPDATE, this, route.path);
|
2005
|
+
} else {
|
2006
|
+
this.updateClasses(route.path, this.el);
|
2007
|
+
}
|
2008
|
+
},
|
2009
|
+
|
2010
|
+
updateActiveMatch: function updateActiveMatch() {
|
2011
|
+
this.activeRE = this.path && !this.exact ? new RegExp('^' + this.path.replace(/\/$/, '').replace(queryStringRE, '').replace(regexEscapeRE, '\\$&') + '(\\/|$)') : null;
|
2012
|
+
},
|
2013
|
+
|
2014
|
+
updateHref: function updateHref() {
|
2015
|
+
if (this.el.tagName !== 'A') {
|
2016
|
+
return;
|
2017
|
+
}
|
2018
|
+
var path = this.path;
|
2019
|
+
var router = this.router;
|
2020
|
+
var isAbsolute = path.charAt(0) === '/';
|
2021
|
+
// do not format non-hash relative paths
|
2022
|
+
var href = path && (router.mode === 'hash' || isAbsolute) ? router.history.formatPath(path, this.append) : path;
|
2023
|
+
if (href) {
|
2024
|
+
this.el.href = href;
|
2025
|
+
} else {
|
2026
|
+
this.el.removeAttribute('href');
|
2027
|
+
}
|
2028
|
+
},
|
2029
|
+
|
2030
|
+
updateClasses: function updateClasses(path, el) {
|
2031
|
+
var activeClass = this.activeClass || this.router._linkActiveClass;
|
2032
|
+
// clear old class
|
2033
|
+
if (this.prevActiveClass && this.prevActiveClass !== activeClass) {
|
2034
|
+
toggleClasses(el, this.prevActiveClass, removeClass);
|
2035
|
+
}
|
2036
|
+
// remove query string before matching
|
2037
|
+
var dest = this.path.replace(queryStringRE, '');
|
2038
|
+
path = path.replace(queryStringRE, '');
|
2039
|
+
// add new class
|
2040
|
+
if (this.exact) {
|
2041
|
+
if (dest === path ||
|
2042
|
+
// also allow additional trailing slash
|
2043
|
+
dest.charAt(dest.length - 1) !== '/' && dest === path.replace(trailingSlashRE, '')) {
|
2044
|
+
toggleClasses(el, activeClass, addClass);
|
2045
|
+
} else {
|
2046
|
+
toggleClasses(el, activeClass, removeClass);
|
2047
|
+
}
|
2048
|
+
} else {
|
2049
|
+
if (this.activeRE && this.activeRE.test(path)) {
|
2050
|
+
toggleClasses(el, activeClass, addClass);
|
2051
|
+
} else {
|
2052
|
+
toggleClasses(el, activeClass, removeClass);
|
2053
|
+
}
|
2054
|
+
}
|
2055
|
+
},
|
2056
|
+
|
2057
|
+
unbind: function unbind() {
|
2058
|
+
this.el.removeEventListener('click', this.handler);
|
2059
|
+
this.unwatch && this.unwatch();
|
2060
|
+
}
|
2061
|
+
});
|
2062
|
+
|
2063
|
+
function sameOrigin(link) {
|
2064
|
+
return link.protocol === location.protocol && link.hostname === location.hostname && link.port === location.port;
|
2065
|
+
}
|
2066
|
+
|
2067
|
+
// this function is copied from v-bind:class implementation until
|
2068
|
+
// we properly expose it...
|
2069
|
+
function toggleClasses(el, key, fn) {
|
2070
|
+
key = key.trim();
|
2071
|
+
if (key.indexOf(' ') === -1) {
|
2072
|
+
fn(el, key);
|
2073
|
+
return;
|
2074
|
+
}
|
2075
|
+
var keys = key.split(/\s+/);
|
2076
|
+
for (var i = 0, l = keys.length; i < l; i++) {
|
2077
|
+
fn(el, keys[i]);
|
2078
|
+
}
|
2079
|
+
}
|
2080
|
+
}
|
2081
|
+
|
2082
|
+
var historyBackends = {
|
2083
|
+
abstract: AbstractHistory,
|
2084
|
+
hash: HashHistory,
|
2085
|
+
html5: HTML5History
|
2086
|
+
};
|
2087
|
+
|
2088
|
+
// late bind during install
|
2089
|
+
var Vue = undefined;
|
2090
|
+
|
2091
|
+
/**
|
2092
|
+
* Router constructor
|
2093
|
+
*
|
2094
|
+
* @param {Object} [options]
|
2095
|
+
*/
|
2096
|
+
|
2097
|
+
var Router = (function () {
|
2098
|
+
function Router() {
|
2099
|
+
var _this = this;
|
2100
|
+
|
2101
|
+
var _ref = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
|
2102
|
+
|
2103
|
+
var _ref$hashbang = _ref.hashbang;
|
2104
|
+
var hashbang = _ref$hashbang === undefined ? true : _ref$hashbang;
|
2105
|
+
var _ref$abstract = _ref.abstract;
|
2106
|
+
var abstract = _ref$abstract === undefined ? false : _ref$abstract;
|
2107
|
+
var _ref$history = _ref.history;
|
2108
|
+
var history = _ref$history === undefined ? false : _ref$history;
|
2109
|
+
var _ref$saveScrollPosition = _ref.saveScrollPosition;
|
2110
|
+
var saveScrollPosition = _ref$saveScrollPosition === undefined ? false : _ref$saveScrollPosition;
|
2111
|
+
var _ref$transitionOnLoad = _ref.transitionOnLoad;
|
2112
|
+
var transitionOnLoad = _ref$transitionOnLoad === undefined ? false : _ref$transitionOnLoad;
|
2113
|
+
var _ref$suppressTransitionError = _ref.suppressTransitionError;
|
2114
|
+
var suppressTransitionError = _ref$suppressTransitionError === undefined ? false : _ref$suppressTransitionError;
|
2115
|
+
var _ref$root = _ref.root;
|
2116
|
+
var root = _ref$root === undefined ? null : _ref$root;
|
2117
|
+
var _ref$linkActiveClass = _ref.linkActiveClass;
|
2118
|
+
var linkActiveClass = _ref$linkActiveClass === undefined ? 'v-link-active' : _ref$linkActiveClass;
|
2119
|
+
babelHelpers.classCallCheck(this, Router);
|
2120
|
+
|
2121
|
+
/* istanbul ignore if */
|
2122
|
+
if (!Router.installed) {
|
2123
|
+
throw new Error('Please install the Router with Vue.use() before ' + 'creating an instance.');
|
2124
|
+
}
|
2125
|
+
|
2126
|
+
// Vue instances
|
2127
|
+
this.app = null;
|
2128
|
+
this._children = [];
|
2129
|
+
|
2130
|
+
// route recognizer
|
2131
|
+
this._recognizer = new RouteRecognizer();
|
2132
|
+
this._guardRecognizer = new RouteRecognizer();
|
2133
|
+
|
2134
|
+
// state
|
2135
|
+
this._started = false;
|
2136
|
+
this._startCb = null;
|
2137
|
+
this._currentRoute = {};
|
2138
|
+
this._currentTransition = null;
|
2139
|
+
this._previousTransition = null;
|
2140
|
+
this._notFoundHandler = null;
|
2141
|
+
this._notFoundRedirect = null;
|
2142
|
+
this._beforeEachHooks = [];
|
2143
|
+
this._afterEachHooks = [];
|
2144
|
+
|
2145
|
+
// trigger transition on initial render?
|
2146
|
+
this._rendered = false;
|
2147
|
+
this._transitionOnLoad = transitionOnLoad;
|
2148
|
+
|
2149
|
+
// history mode
|
2150
|
+
this._root = root;
|
2151
|
+
this._abstract = abstract;
|
2152
|
+
this._hashbang = hashbang;
|
2153
|
+
|
2154
|
+
// check if HTML5 history is available
|
2155
|
+
var hasPushState = typeof window !== 'undefined' && window.history && window.history.pushState;
|
2156
|
+
this._history = history && hasPushState;
|
2157
|
+
this._historyFallback = history && !hasPushState;
|
2158
|
+
|
2159
|
+
// create history object
|
2160
|
+
var inBrowser = Vue.util.inBrowser;
|
2161
|
+
this.mode = !inBrowser || this._abstract ? 'abstract' : this._history ? 'html5' : 'hash';
|
2162
|
+
|
2163
|
+
var History = historyBackends[this.mode];
|
2164
|
+
this.history = new History({
|
2165
|
+
root: root,
|
2166
|
+
hashbang: this._hashbang,
|
2167
|
+
onChange: function onChange(path, state, anchor) {
|
2168
|
+
_this._match(path, state, anchor);
|
2169
|
+
}
|
2170
|
+
});
|
2171
|
+
|
2172
|
+
// other options
|
2173
|
+
this._saveScrollPosition = saveScrollPosition;
|
2174
|
+
this._linkActiveClass = linkActiveClass;
|
2175
|
+
this._suppress = suppressTransitionError;
|
2176
|
+
}
|
2177
|
+
|
2178
|
+
/**
|
2179
|
+
* Allow directly passing components to a route
|
2180
|
+
* definition.
|
2181
|
+
*
|
2182
|
+
* @param {String} path
|
2183
|
+
* @param {Object} handler
|
2184
|
+
*/
|
2185
|
+
|
2186
|
+
// API ===================================================
|
2187
|
+
|
2188
|
+
/**
|
2189
|
+
* Register a map of top-level paths.
|
2190
|
+
*
|
2191
|
+
* @param {Object} map
|
2192
|
+
*/
|
2193
|
+
|
2194
|
+
Router.prototype.map = function map(_map) {
|
2195
|
+
for (var route in _map) {
|
2196
|
+
this.on(route, _map[route]);
|
2197
|
+
}
|
2198
|
+
return this;
|
2199
|
+
};
|
2200
|
+
|
2201
|
+
/**
|
2202
|
+
* Register a single root-level path
|
2203
|
+
*
|
2204
|
+
* @param {String} rootPath
|
2205
|
+
* @param {Object} handler
|
2206
|
+
* - {String} component
|
2207
|
+
* - {Object} [subRoutes]
|
2208
|
+
* - {Boolean} [forceRefresh]
|
2209
|
+
* - {Function} [before]
|
2210
|
+
* - {Function} [after]
|
2211
|
+
*/
|
2212
|
+
|
2213
|
+
Router.prototype.on = function on(rootPath, handler) {
|
2214
|
+
if (rootPath === '*') {
|
2215
|
+
this._notFound(handler);
|
2216
|
+
} else {
|
2217
|
+
this._addRoute(rootPath, handler, []);
|
2218
|
+
}
|
2219
|
+
return this;
|
2220
|
+
};
|
2221
|
+
|
2222
|
+
/**
|
2223
|
+
* Set redirects.
|
2224
|
+
*
|
2225
|
+
* @param {Object} map
|
2226
|
+
*/
|
2227
|
+
|
2228
|
+
Router.prototype.redirect = function redirect(map) {
|
2229
|
+
for (var path in map) {
|
2230
|
+
this._addRedirect(path, map[path]);
|
2231
|
+
}
|
2232
|
+
return this;
|
2233
|
+
};
|
2234
|
+
|
2235
|
+
/**
|
2236
|
+
* Set aliases.
|
2237
|
+
*
|
2238
|
+
* @param {Object} map
|
2239
|
+
*/
|
2240
|
+
|
2241
|
+
Router.prototype.alias = function alias(map) {
|
2242
|
+
for (var path in map) {
|
2243
|
+
this._addAlias(path, map[path]);
|
2244
|
+
}
|
2245
|
+
return this;
|
2246
|
+
};
|
2247
|
+
|
2248
|
+
/**
|
2249
|
+
* Set global before hook.
|
2250
|
+
*
|
2251
|
+
* @param {Function} fn
|
2252
|
+
*/
|
2253
|
+
|
2254
|
+
Router.prototype.beforeEach = function beforeEach(fn) {
|
2255
|
+
this._beforeEachHooks.push(fn);
|
2256
|
+
return this;
|
2257
|
+
};
|
2258
|
+
|
2259
|
+
/**
|
2260
|
+
* Set global after hook.
|
2261
|
+
*
|
2262
|
+
* @param {Function} fn
|
2263
|
+
*/
|
2264
|
+
|
2265
|
+
Router.prototype.afterEach = function afterEach(fn) {
|
2266
|
+
this._afterEachHooks.push(fn);
|
2267
|
+
return this;
|
2268
|
+
};
|
2269
|
+
|
2270
|
+
/**
|
2271
|
+
* Navigate to a given path.
|
2272
|
+
* The path can be an object describing a named path in
|
2273
|
+
* the format of { name: '...', params: {}, query: {}}
|
2274
|
+
* The path is assumed to be already decoded, and will
|
2275
|
+
* be resolved against root (if provided)
|
2276
|
+
*
|
2277
|
+
* @param {String|Object} path
|
2278
|
+
* @param {Boolean} [replace]
|
2279
|
+
*/
|
2280
|
+
|
2281
|
+
Router.prototype.go = function go(path) {
|
2282
|
+
var replace = false;
|
2283
|
+
var append = false;
|
2284
|
+
if (Vue.util.isObject(path)) {
|
2285
|
+
replace = path.replace;
|
2286
|
+
append = path.append;
|
2287
|
+
}
|
2288
|
+
path = this.stringifyPath(path);
|
2289
|
+
if (path) {
|
2290
|
+
this.history.go(path, replace, append);
|
2291
|
+
}
|
2292
|
+
};
|
2293
|
+
|
2294
|
+
/**
|
2295
|
+
* Short hand for replacing current path
|
2296
|
+
*
|
2297
|
+
* @param {String} path
|
2298
|
+
*/
|
2299
|
+
|
2300
|
+
Router.prototype.replace = function replace(path) {
|
2301
|
+
if (typeof path === 'string') {
|
2302
|
+
path = { path: path };
|
2303
|
+
}
|
2304
|
+
path.replace = true;
|
2305
|
+
this.go(path);
|
2306
|
+
};
|
2307
|
+
|
2308
|
+
/**
|
2309
|
+
* Start the router.
|
2310
|
+
*
|
2311
|
+
* @param {VueConstructor} App
|
2312
|
+
* @param {String|Element} container
|
2313
|
+
* @param {Function} [cb]
|
2314
|
+
*/
|
2315
|
+
|
2316
|
+
Router.prototype.start = function start(App, container, cb) {
|
2317
|
+
/* istanbul ignore if */
|
2318
|
+
if (this._started) {
|
2319
|
+
warn$1('already started.');
|
2320
|
+
return;
|
2321
|
+
}
|
2322
|
+
this._started = true;
|
2323
|
+
this._startCb = cb;
|
2324
|
+
if (!this.app) {
|
2325
|
+
/* istanbul ignore if */
|
2326
|
+
if (!App || !container) {
|
2327
|
+
throw new Error('Must start vue-router with a component and a ' + 'root container.');
|
2328
|
+
}
|
2329
|
+
/* istanbul ignore if */
|
2330
|
+
if (App instanceof Vue) {
|
2331
|
+
throw new Error('Must start vue-router with a component, not a ' + 'Vue instance.');
|
2332
|
+
}
|
2333
|
+
this._appContainer = container;
|
2334
|
+
var Ctor = this._appConstructor = typeof App === 'function' ? App : Vue.extend(App);
|
2335
|
+
// give it a name for better debugging
|
2336
|
+
Ctor.options.name = Ctor.options.name || 'RouterApp';
|
2337
|
+
}
|
2338
|
+
|
2339
|
+
// handle history fallback in browsers that do not
|
2340
|
+
// support HTML5 history API
|
2341
|
+
if (this._historyFallback) {
|
2342
|
+
var _location = window.location;
|
2343
|
+
var _history = new HTML5History({ root: this._root });
|
2344
|
+
var path = _history.root ? _location.pathname.replace(_history.rootRE, '') : _location.pathname;
|
2345
|
+
if (path && path !== '/') {
|
2346
|
+
_location.assign((_history.root || '') + '/' + this.history.formatPath(path) + _location.search);
|
2347
|
+
return;
|
2348
|
+
}
|
2349
|
+
}
|
2350
|
+
|
2351
|
+
this.history.start();
|
2352
|
+
};
|
2353
|
+
|
2354
|
+
/**
|
2355
|
+
* Stop listening to route changes.
|
2356
|
+
*/
|
2357
|
+
|
2358
|
+
Router.prototype.stop = function stop() {
|
2359
|
+
this.history.stop();
|
2360
|
+
this._started = false;
|
2361
|
+
};
|
2362
|
+
|
2363
|
+
/**
|
2364
|
+
* Normalize named route object / string paths into
|
2365
|
+
* a string.
|
2366
|
+
*
|
2367
|
+
* @param {Object|String|Number} path
|
2368
|
+
* @return {String}
|
2369
|
+
*/
|
2370
|
+
|
2371
|
+
Router.prototype.stringifyPath = function stringifyPath(path) {
|
2372
|
+
var generatedPath = '';
|
2373
|
+
if (path && typeof path === 'object') {
|
2374
|
+
if (path.name) {
|
2375
|
+
var extend = Vue.util.extend;
|
2376
|
+
var currentParams = this._currentTransition && this._currentTransition.to.params;
|
2377
|
+
var targetParams = path.params || {};
|
2378
|
+
var params = currentParams ? extend(extend({}, currentParams), targetParams) : targetParams;
|
2379
|
+
generatedPath = encodeURI(this._recognizer.generate(path.name, params));
|
2380
|
+
} else if (path.path) {
|
2381
|
+
generatedPath = encodeURI(path.path);
|
2382
|
+
}
|
2383
|
+
if (path.query) {
|
2384
|
+
// note: the generated query string is pre-URL-encoded by the recognizer
|
2385
|
+
var query = this._recognizer.generateQueryString(path.query);
|
2386
|
+
if (generatedPath.indexOf('?') > -1) {
|
2387
|
+
generatedPath += '&' + query.slice(1);
|
2388
|
+
} else {
|
2389
|
+
generatedPath += query;
|
2390
|
+
}
|
2391
|
+
}
|
2392
|
+
} else {
|
2393
|
+
generatedPath = encodeURI(path ? path + '' : '');
|
2394
|
+
}
|
2395
|
+
return generatedPath;
|
2396
|
+
};
|
2397
|
+
|
2398
|
+
// Internal methods ======================================
|
2399
|
+
|
2400
|
+
/**
|
2401
|
+
* Add a route containing a list of segments to the internal
|
2402
|
+
* route recognizer. Will be called recursively to add all
|
2403
|
+
* possible sub-routes.
|
2404
|
+
*
|
2405
|
+
* @param {String} path
|
2406
|
+
* @param {Object} handler
|
2407
|
+
* @param {Array} segments
|
2408
|
+
*/
|
2409
|
+
|
2410
|
+
Router.prototype._addRoute = function _addRoute(path, handler, segments) {
|
2411
|
+
guardComponent(path, handler);
|
2412
|
+
handler.path = path;
|
2413
|
+
handler.fullPath = (segments.reduce(function (path, segment) {
|
2414
|
+
return path + segment.path;
|
2415
|
+
}, '') + path).replace('//', '/');
|
2416
|
+
segments.push({
|
2417
|
+
path: path,
|
2418
|
+
handler: handler
|
2419
|
+
});
|
2420
|
+
this._recognizer.add(segments, {
|
2421
|
+
as: handler.name
|
2422
|
+
});
|
2423
|
+
// add sub routes
|
2424
|
+
if (handler.subRoutes) {
|
2425
|
+
for (var subPath in handler.subRoutes) {
|
2426
|
+
// recursively walk all sub routes
|
2427
|
+
this._addRoute(subPath, handler.subRoutes[subPath],
|
2428
|
+
// pass a copy in recursion to avoid mutating
|
2429
|
+
// across branches
|
2430
|
+
segments.slice());
|
2431
|
+
}
|
2432
|
+
}
|
2433
|
+
};
|
2434
|
+
|
2435
|
+
/**
|
2436
|
+
* Set the notFound route handler.
|
2437
|
+
*
|
2438
|
+
* @param {Object} handler
|
2439
|
+
*/
|
2440
|
+
|
2441
|
+
Router.prototype._notFound = function _notFound(handler) {
|
2442
|
+
guardComponent('*', handler);
|
2443
|
+
this._notFoundHandler = [{ handler: handler }];
|
2444
|
+
};
|
2445
|
+
|
2446
|
+
/**
|
2447
|
+
* Add a redirect record.
|
2448
|
+
*
|
2449
|
+
* @param {String} path
|
2450
|
+
* @param {String} redirectPath
|
2451
|
+
*/
|
2452
|
+
|
2453
|
+
Router.prototype._addRedirect = function _addRedirect(path, redirectPath) {
|
2454
|
+
if (path === '*') {
|
2455
|
+
this._notFoundRedirect = redirectPath;
|
2456
|
+
} else {
|
2457
|
+
this._addGuard(path, redirectPath, this.replace);
|
2458
|
+
}
|
2459
|
+
};
|
2460
|
+
|
2461
|
+
/**
|
2462
|
+
* Add an alias record.
|
2463
|
+
*
|
2464
|
+
* @param {String} path
|
2465
|
+
* @param {String} aliasPath
|
2466
|
+
*/
|
2467
|
+
|
2468
|
+
Router.prototype._addAlias = function _addAlias(path, aliasPath) {
|
2469
|
+
this._addGuard(path, aliasPath, this._match);
|
2470
|
+
};
|
2471
|
+
|
2472
|
+
/**
|
2473
|
+
* Add a path guard.
|
2474
|
+
*
|
2475
|
+
* @param {String} path
|
2476
|
+
* @param {String} mappedPath
|
2477
|
+
* @param {Function} handler
|
2478
|
+
*/
|
2479
|
+
|
2480
|
+
Router.prototype._addGuard = function _addGuard(path, mappedPath, _handler) {
|
2481
|
+
var _this2 = this;
|
2482
|
+
|
2483
|
+
this._guardRecognizer.add([{
|
2484
|
+
path: path,
|
2485
|
+
handler: function handler(match, query) {
|
2486
|
+
var realPath = mapParams(mappedPath, match.params, query);
|
2487
|
+
_handler.call(_this2, realPath);
|
2488
|
+
}
|
2489
|
+
}]);
|
2490
|
+
};
|
2491
|
+
|
2492
|
+
/**
|
2493
|
+
* Check if a path matches any redirect records.
|
2494
|
+
*
|
2495
|
+
* @param {String} path
|
2496
|
+
* @return {Boolean} - if true, will skip normal match.
|
2497
|
+
*/
|
2498
|
+
|
2499
|
+
Router.prototype._checkGuard = function _checkGuard(path) {
|
2500
|
+
var matched = this._guardRecognizer.recognize(path, true);
|
2501
|
+
if (matched) {
|
2502
|
+
matched[0].handler(matched[0], matched.queryParams);
|
2503
|
+
return true;
|
2504
|
+
} else if (this._notFoundRedirect) {
|
2505
|
+
matched = this._recognizer.recognize(path);
|
2506
|
+
if (!matched) {
|
2507
|
+
this.replace(this._notFoundRedirect);
|
2508
|
+
return true;
|
2509
|
+
}
|
2510
|
+
}
|
2511
|
+
};
|
2512
|
+
|
2513
|
+
/**
|
2514
|
+
* Match a URL path and set the route context on vm,
|
2515
|
+
* triggering view updates.
|
2516
|
+
*
|
2517
|
+
* @param {String} path
|
2518
|
+
* @param {Object} [state]
|
2519
|
+
* @param {String} [anchor]
|
2520
|
+
*/
|
2521
|
+
|
2522
|
+
Router.prototype._match = function _match(path, state, anchor) {
|
2523
|
+
var _this3 = this;
|
2524
|
+
|
2525
|
+
if (this._checkGuard(path)) {
|
2526
|
+
return;
|
2527
|
+
}
|
2528
|
+
|
2529
|
+
var currentRoute = this._currentRoute;
|
2530
|
+
var currentTransition = this._currentTransition;
|
2531
|
+
|
2532
|
+
if (currentTransition) {
|
2533
|
+
if (currentTransition.to.path === path) {
|
2534
|
+
// do nothing if we have an active transition going to the same path
|
2535
|
+
return;
|
2536
|
+
} else if (currentRoute.path === path) {
|
2537
|
+
// We are going to the same path, but we also have an ongoing but
|
2538
|
+
// not-yet-validated transition. Abort that transition and reset to
|
2539
|
+
// prev transition.
|
2540
|
+
currentTransition.aborted = true;
|
2541
|
+
this._currentTransition = this._prevTransition;
|
2542
|
+
return;
|
2543
|
+
} else {
|
2544
|
+
// going to a totally different path. abort ongoing transition.
|
2545
|
+
currentTransition.aborted = true;
|
2546
|
+
}
|
2547
|
+
}
|
2548
|
+
|
2549
|
+
// construct new route and transition context
|
2550
|
+
var route = new Route(path, this);
|
2551
|
+
var transition = new RouteTransition(this, route, currentRoute);
|
2552
|
+
|
2553
|
+
// current transition is updated right now.
|
2554
|
+
// however, current route will only be updated after the transition has
|
2555
|
+
// been validated.
|
2556
|
+
this._prevTransition = currentTransition;
|
2557
|
+
this._currentTransition = transition;
|
2558
|
+
|
2559
|
+
if (!this.app) {
|
2560
|
+
(function () {
|
2561
|
+
// initial render
|
2562
|
+
var router = _this3;
|
2563
|
+
_this3.app = new _this3._appConstructor({
|
2564
|
+
el: _this3._appContainer,
|
2565
|
+
created: function created() {
|
2566
|
+
this.$router = router;
|
2567
|
+
},
|
2568
|
+
_meta: {
|
2569
|
+
$route: route
|
2570
|
+
}
|
2571
|
+
});
|
2572
|
+
})();
|
2573
|
+
}
|
2574
|
+
|
2575
|
+
// check global before hook
|
2576
|
+
var beforeHooks = this._beforeEachHooks;
|
2577
|
+
var startTransition = function startTransition() {
|
2578
|
+
transition.start(function () {
|
2579
|
+
_this3._postTransition(route, state, anchor);
|
2580
|
+
});
|
2581
|
+
};
|
2582
|
+
|
2583
|
+
if (beforeHooks.length) {
|
2584
|
+
transition.runQueue(beforeHooks, function (hook, _, next) {
|
2585
|
+
if (transition === _this3._currentTransition) {
|
2586
|
+
transition.callHook(hook, null, next, {
|
2587
|
+
expectBoolean: true
|
2588
|
+
});
|
2589
|
+
}
|
2590
|
+
}, startTransition);
|
2591
|
+
} else {
|
2592
|
+
startTransition();
|
2593
|
+
}
|
2594
|
+
|
2595
|
+
if (!this._rendered && this._startCb) {
|
2596
|
+
this._startCb.call(null);
|
2597
|
+
}
|
2598
|
+
|
2599
|
+
// HACK:
|
2600
|
+
// set rendered to true after the transition start, so
|
2601
|
+
// that components that are acitvated synchronously know
|
2602
|
+
// whether it is the initial render.
|
2603
|
+
this._rendered = true;
|
2604
|
+
};
|
2605
|
+
|
2606
|
+
/**
|
2607
|
+
* Set current to the new transition.
|
2608
|
+
* This is called by the transition object when the
|
2609
|
+
* validation of a route has succeeded.
|
2610
|
+
*
|
2611
|
+
* @param {Transition} transition
|
2612
|
+
*/
|
2613
|
+
|
2614
|
+
Router.prototype._onTransitionValidated = function _onTransitionValidated(transition) {
|
2615
|
+
// set current route
|
2616
|
+
var route = this._currentRoute = transition.to;
|
2617
|
+
// update route context for all children
|
2618
|
+
if (this.app.$route !== route) {
|
2619
|
+
this.app.$route = route;
|
2620
|
+
this._children.forEach(function (child) {
|
2621
|
+
child.$route = route;
|
2622
|
+
});
|
2623
|
+
}
|
2624
|
+
// call global after hook
|
2625
|
+
if (this._afterEachHooks.length) {
|
2626
|
+
this._afterEachHooks.forEach(function (hook) {
|
2627
|
+
return hook.call(null, {
|
2628
|
+
to: transition.to,
|
2629
|
+
from: transition.from
|
2630
|
+
});
|
2631
|
+
});
|
2632
|
+
}
|
2633
|
+
this._currentTransition.done = true;
|
2634
|
+
};
|
2635
|
+
|
2636
|
+
/**
|
2637
|
+
* Handle stuff after the transition.
|
2638
|
+
*
|
2639
|
+
* @param {Route} route
|
2640
|
+
* @param {Object} [state]
|
2641
|
+
* @param {String} [anchor]
|
2642
|
+
*/
|
2643
|
+
|
2644
|
+
Router.prototype._postTransition = function _postTransition(route, state, anchor) {
|
2645
|
+
// handle scroll positions
|
2646
|
+
// saved scroll positions take priority
|
2647
|
+
// then we check if the path has an anchor
|
2648
|
+
var pos = state && state.pos;
|
2649
|
+
if (pos && this._saveScrollPosition) {
|
2650
|
+
Vue.nextTick(function () {
|
2651
|
+
window.scrollTo(pos.x, pos.y);
|
2652
|
+
});
|
2653
|
+
} else if (anchor) {
|
2654
|
+
Vue.nextTick(function () {
|
2655
|
+
var el = document.getElementById(anchor.slice(1));
|
2656
|
+
if (el) {
|
2657
|
+
window.scrollTo(window.scrollX, el.offsetTop);
|
2658
|
+
}
|
2659
|
+
});
|
2660
|
+
}
|
2661
|
+
};
|
2662
|
+
|
2663
|
+
return Router;
|
2664
|
+
})();
|
2665
|
+
|
2666
|
+
function guardComponent(path, handler) {
|
2667
|
+
var comp = handler.component;
|
2668
|
+
if (Vue.util.isPlainObject(comp)) {
|
2669
|
+
comp = handler.component = Vue.extend(comp);
|
2670
|
+
}
|
2671
|
+
/* istanbul ignore if */
|
2672
|
+
if (typeof comp !== 'function') {
|
2673
|
+
handler.component = null;
|
2674
|
+
warn$1('invalid component for route "' + path + '".');
|
2675
|
+
}
|
2676
|
+
}
|
2677
|
+
|
2678
|
+
/* Installation */
|
2679
|
+
|
2680
|
+
Router.installed = false;
|
2681
|
+
|
2682
|
+
/**
|
2683
|
+
* Installation interface.
|
2684
|
+
* Install the necessary directives.
|
2685
|
+
*/
|
2686
|
+
|
2687
|
+
Router.install = function (externalVue) {
|
2688
|
+
/* istanbul ignore if */
|
2689
|
+
if (Router.installed) {
|
2690
|
+
warn$1('already installed.');
|
2691
|
+
return;
|
2692
|
+
}
|
2693
|
+
Vue = externalVue;
|
2694
|
+
applyOverride(Vue);
|
2695
|
+
View(Vue);
|
2696
|
+
Link(Vue);
|
2697
|
+
exports$1.Vue = Vue;
|
2698
|
+
Router.installed = true;
|
2699
|
+
};
|
2700
|
+
|
2701
|
+
// auto install
|
2702
|
+
/* istanbul ignore if */
|
2703
|
+
if (typeof window !== 'undefined' && window.Vue) {
|
2704
|
+
window.Vue.use(Router);
|
2705
|
+
}
|
2706
|
+
|
2707
|
+
return Router;
|
2708
|
+
|
2709
|
+
}));
|