vuejs-rails 1.0.26 → 2.0.1

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.
@@ -1,2709 +1,1837 @@
1
- /*!
2
- * vue-router v0.7.13
1
+ /**
2
+ * vue-router v2.0.0
3
3
  * (c) 2016 Evan You
4
- * Released under the MIT License.
4
+ * @license MIT
5
5
  */
6
6
  (function (global, factory) {
7
7
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
8
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");
9
+ (global.VueRouter = factory());
10
+ }(this, (function () { 'use strict';
11
+
12
+ var View = {
13
+ name: 'router-view',
14
+ functional: true,
15
+ props: {
16
+ name: {
17
+ type: String,
18
+ default: 'default'
17
19
  }
18
- };
19
- function Target(path, matcher, delegate) {
20
- this.path = path;
21
- this.matcher = matcher;
22
- this.delegate = delegate;
23
- }
20
+ },
21
+ render: function render (h, ref) {
22
+ var props = ref.props;
23
+ var children = ref.children;
24
+ var parent = ref.parent;
25
+ var data = ref.data;
24
26
 
25
- Target.prototype = {
26
- to: function to(target, callback) {
27
- var delegate = this.delegate;
27
+ data.routerView = true
28
28
 
29
- if (delegate && delegate.willAddRoute) {
30
- target = delegate.willAddRoute(this.matcher.target, target);
29
+ var route = parent.$route
30
+ var cache = parent._routerViewCache || (parent._routerViewCache = {})
31
+ var depth = 0
32
+ var inactive = false
33
+
34
+ while (parent) {
35
+ if (parent.$vnode && parent.$vnode.data.routerView) {
36
+ depth++
37
+ }
38
+ if (parent._inactive) {
39
+ inactive = true
31
40
  }
41
+ parent = parent.$parent
42
+ }
32
43
 
33
- this.matcher.add(this.path, target);
44
+ data.routerViewDepth = depth
45
+ var matched = route.matched[depth]
46
+ if (!matched) {
47
+ return h()
48
+ }
34
49
 
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);
50
+ var component = inactive
51
+ ? cache[props.name]
52
+ : (cache[props.name] = matched.components[props.name])
53
+
54
+ if (!inactive) {
55
+ (data.hook || (data.hook = {})).init = function (vnode) {
56
+ matched.instances[props.name] = vnode.child
40
57
  }
41
- return this;
42
58
  }
43
- };
44
59
 
45
- function Matcher(target) {
46
- this.routes = {};
47
- this.children = {};
48
- this.target = target;
60
+ return h(component, data, children)
49
61
  }
62
+ }
50
63
 
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);
64
+ /* */
61
65
 
62
- if (delegate && delegate.contextEntered) {
63
- delegate.contextEntered(target, match);
64
- }
66
+ function resolvePath (
67
+ relative,
68
+ base,
69
+ append
70
+ ) {
71
+ if (relative.charAt(0) === '/') {
72
+ return relative
73
+ }
65
74
 
66
- callback(match);
67
- }
68
- };
75
+ if (relative.charAt(0) === '?' || relative.charAt(0) === '#') {
76
+ return base + relative
77
+ }
69
78
 
70
- function generateMatch(startingPath, matcher, delegate) {
71
- return function (path, nestedCallback) {
72
- var fullPath = startingPath + path;
79
+ var stack = base.split('/')
73
80
 
74
- if (nestedCallback) {
75
- nestedCallback(generateMatch(fullPath, matcher, delegate));
76
- } else {
77
- return new Target(startingPath + path, matcher, delegate);
78
- }
79
- };
81
+ // remove trailing segment if:
82
+ // - not appending
83
+ // - appending to trailing slash (last segment is empty)
84
+ if (!append || !stack[stack.length - 1]) {
85
+ stack.pop()
80
86
  }
81
87
 
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;
88
+ // resolve relative path
89
+ var segments = relative.replace(/^\//, '').split('/')
90
+ for (var i = 0; i < segments.length; i++) {
91
+ var segment = segments[i]
92
+ if (segment === '.') {
93
+ continue
94
+ } else if (segment === '..') {
95
+ stack.pop()
96
+ } else {
97
+ stack.push(segment)
86
98
  }
99
+ }
87
100
 
88
- path = path.substr(len);
89
- var route = { path: path, handler: handler };
90
- routeArray.push(route);
101
+ // ensure leading slash
102
+ if (stack[0] !== '') {
103
+ stack.unshift('')
91
104
  }
92
105
 
93
- function eachRoute(baseRoute, matcher, callback, binding) {
94
- var routes = matcher.routes;
106
+ return stack.join('/')
107
+ }
95
108
 
96
- for (var path in routes) {
97
- if (routes.hasOwnProperty(path)) {
98
- var routeArray = baseRoute.slice();
99
- addRoute(routeArray, path, routes[path]);
109
+ function parsePath (path) {
110
+ var hash = ''
111
+ var query = ''
100
112
 
101
- if (matcher.children[path]) {
102
- eachRoute(routeArray, matcher.children[path], callback, binding);
103
- } else {
104
- callback.call(binding, routeArray);
105
- }
106
- }
107
- }
113
+ var hashIndex = path.indexOf('#')
114
+ if (hashIndex >= 0) {
115
+ hash = path.slice(hashIndex)
116
+ path = path.slice(0, hashIndex)
108
117
  }
109
118
 
110
- function map (callback, addRouteCallback) {
111
- var matcher = new Matcher();
112
-
113
- callback(generateMatch("", matcher, this.delegate));
119
+ var queryIndex = path.indexOf('?')
120
+ if (queryIndex >= 0) {
121
+ query = path.slice(queryIndex + 1)
122
+ path = path.slice(0, queryIndex)
123
+ }
114
124
 
115
- eachRoute([], matcher, function (route) {
116
- if (addRouteCallback) {
117
- addRouteCallback(this, route);
118
- } else {
119
- this.add(route);
120
- }
121
- }, this);
125
+ return {
126
+ path: path,
127
+ query: query,
128
+ hash: hash
122
129
  }
130
+ }
123
131
 
124
- var specials = ['/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\'];
132
+ function cleanPath (path) {
133
+ return path.replace(/\/\//g, '/')
134
+ }
125
135
 
126
- var escapeRegex = new RegExp('(\\' + specials.join('|\\') + ')', 'g');
136
+ /* */
127
137
 
128
- var noWarning = false;
129
- function warn(msg) {
130
- if (!noWarning && typeof console !== 'undefined') {
131
- console.error('[vue-router] ' + msg);
132
- }
138
+ function assert (condition, message) {
139
+ if (!condition) {
140
+ throw new Error(("[vue-router] " + message))
133
141
  }
142
+ }
134
143
 
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
- }
144
+ function warn (condition, message) {
145
+ if (!condition) {
146
+ typeof console !== 'undefined' && console.warn(("[vue-router] " + message))
141
147
  }
148
+ }
142
149
 
143
- function isArray(test) {
144
- return Object.prototype.toString.call(test) === "[object Array]";
145
- }
150
+ /* */
146
151
 
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
152
+ var encode = encodeURIComponent
153
+ var decode = decodeURIComponent
163
154
 
164
- function StaticSegment(string) {
165
- this.string = string;
166
- }
167
- StaticSegment.prototype = {
168
- eachChar: function eachChar(callback) {
169
- var string = this.string,
170
- ch;
155
+ function resolveQuery (
156
+ query,
157
+ extraQuery
158
+ ) {
159
+ if ( extraQuery === void 0 ) extraQuery = {};
171
160
 
172
- for (var i = 0, l = string.length; i < l; i++) {
173
- ch = string.charAt(i);
174
- callback({ validChars: ch });
175
- }
176
- },
161
+ if (query) {
162
+ var parsedQuery
163
+ try {
164
+ parsedQuery = parseQuery(query)
165
+ } catch (e) {
166
+ warn(false, e.message)
167
+ parsedQuery = {}
168
+ }
169
+ for (var key in extraQuery) {
170
+ parsedQuery[key] = extraQuery[key]
171
+ }
172
+ return parsedQuery
173
+ } else {
174
+ return extraQuery
175
+ }
176
+ }
177
177
 
178
- regex: function regex() {
179
- return this.string.replace(escapeRegex, '\\$1');
180
- },
178
+ function parseQuery (query) {
179
+ var res = Object.create(null)
181
180
 
182
- generate: function generate() {
183
- return this.string;
184
- }
185
- };
181
+ query = query.trim().replace(/^(\?|#|&)/, '')
186
182
 
187
- function DynamicSegment(name) {
188
- this.name = name;
183
+ if (!query) {
184
+ return res
189
185
  }
190
- DynamicSegment.prototype = {
191
- eachChar: function eachChar(callback) {
192
- callback({ invalidChars: "/", repeat: true });
193
- },
194
186
 
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;
187
+ query.split('&').forEach(function (param) {
188
+ var parts = param.replace(/\+/g, ' ').split('=')
189
+ var key = decode(parts.shift())
190
+ var val = parts.length > 0
191
+ ? decode(parts.join('='))
192
+ : null
193
+
194
+ if (res[key] === undefined) {
195
+ res[key] = val
196
+ } else if (Array.isArray(res[key])) {
197
+ res[key].push(val)
198
+ } else {
199
+ res[key] = [res[key], val]
202
200
  }
203
- };
201
+ })
204
202
 
205
- function StarSegment(name) {
206
- this.name = name;
207
- }
208
- StarSegment.prototype = {
209
- eachChar: function eachChar(callback) {
210
- callback({ invalidChars: "", repeat: true });
211
- },
203
+ return res
204
+ }
212
205
 
213
- regex: function regex() {
214
- return "(.+)";
215
- },
206
+ function stringifyQuery (obj) {
207
+ var res = obj ? Object.keys(obj).sort().map(function (key) {
208
+ var val = obj[key]
216
209
 
217
- generate: function generate(params) {
218
- var val = params[this.name];
219
- return val == null ? ":" + this.name : val;
210
+ if (val === undefined) {
211
+ return ''
220
212
  }
221
- };
222
213
 
223
- function EpsilonSegment() {}
224
- EpsilonSegment.prototype = {
225
- eachChar: function eachChar() {},
226
- regex: function regex() {
227
- return "";
228
- },
229
- generate: function generate() {
230
- return "";
214
+ if (val === null) {
215
+ return encode(key)
231
216
  }
232
- };
233
217
 
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);
218
+ if (Array.isArray(val)) {
219
+ var result = []
220
+ val.slice().forEach(function (val2) {
221
+ if (val2 === undefined) {
222
+ return
223
+ }
224
+ if (val2 === null) {
225
+ result.push(encode(key))
226
+ } else {
227
+ result.push(encode(key) + '=' + encode(val2))
228
+ }
229
+ })
230
+ return result.join('&')
239
231
  }
240
232
 
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
- }
233
+ return encode(key) + '=' + encode(val)
234
+ }).filter(function (x) { return x.length > 0; }).join('&') : null
235
+ return res ? ("?" + res) : ''
236
+ }
237
+
238
+ /* */
239
+
240
+ function createRoute (
241
+ record,
242
+ location,
243
+ redirectedFrom
244
+ ) {
245
+ var route = {
246
+ name: location.name || (record && record.name),
247
+ meta: (record && record.meta) || {},
248
+ path: location.path || '/',
249
+ hash: location.hash || '',
250
+ query: location.query || {},
251
+ params: location.params || {},
252
+ fullPath: getFullPath(location),
253
+ matched: record ? formatMatch(record) : []
254
+ }
255
+ if (redirectedFrom) {
256
+ route.redirectedFrom = getFullPath(redirectedFrom)
257
+ }
258
+ return Object.freeze(route)
259
+ }
260
+
261
+ // the starting route that represents the initial state
262
+ var START = createRoute(null, {
263
+ path: '/'
264
+ })
265
+
266
+ function formatMatch (record) {
267
+ var res = []
268
+ while (record) {
269
+ res.unshift(record)
270
+ record = record.parent
271
+ }
272
+ return res
273
+ }
274
+
275
+ function getFullPath (ref) {
276
+ var path = ref.path;
277
+ var query = ref.query; if ( query === void 0 ) query = {};
278
+ var hash = ref.hash; if ( hash === void 0 ) hash = '';
279
+
280
+ return (path || '/') + stringifyQuery(query) + hash
281
+ }
282
+
283
+ var trailingSlashRE = /\/$/
284
+ function isSameRoute (a, b) {
285
+ if (b === START) {
286
+ return a === b
287
+ } else if (!b) {
288
+ return false
289
+ } else if (a.path && b.path) {
290
+ return (
291
+ a.path.replace(trailingSlashRE, '') === b.path.replace(trailingSlashRE, '') &&
292
+ a.hash === b.hash &&
293
+ isObjectEqual(a.query, b.query)
294
+ )
295
+ } else if (a.name && b.name) {
296
+ return (
297
+ a.name === b.name &&
298
+ a.hash === b.hash &&
299
+ isObjectEqual(a.query, b.query) &&
300
+ isObjectEqual(a.params, b.params)
301
+ )
302
+ } else {
303
+ return false
304
+ }
305
+ }
285
306
 
286
- specificity.val = +specificity.val;
307
+ function isObjectEqual (a, b) {
308
+ if ( a === void 0 ) a = {};
309
+ if ( b === void 0 ) b = {};
287
310
 
288
- return results;
311
+ var aKeys = Object.keys(a)
312
+ var bKeys = Object.keys(b)
313
+ if (aKeys.length !== bKeys.length) {
314
+ return false
315
+ }
316
+ return aKeys.every(function (key) { return String(a[key]) === String(b[key]); })
317
+ }
318
+
319
+ function isIncludedRoute (current, target) {
320
+ return (
321
+ current.path.indexOf(target.path) === 0 &&
322
+ (!target.hash || current.hash === target.hash) &&
323
+ queryIncludes(current.query, target.query)
324
+ )
325
+ }
326
+
327
+ function queryIncludes (current, target) {
328
+ for (var key in target) {
329
+ if (!(key in current)) {
330
+ return false
331
+ }
332
+ }
333
+ return true
334
+ }
335
+
336
+ /* */
337
+
338
+ function normalizeLocation (
339
+ raw,
340
+ current,
341
+ append
342
+ ) {
343
+ var next = typeof raw === 'string' ? { path: raw } : raw
344
+ if (next.name || next._normalized) {
345
+ return next
289
346
  }
290
347
 
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 = [];
348
+ var parsedPath = parsePath(next.path || '')
349
+ var basePath = (current && current.path) || '/'
350
+ var path = parsedPath.path
351
+ ? resolvePath(parsedPath.path, basePath, append)
352
+ : (current && current.path) || '/'
353
+ var query = resolveQuery(parsedPath.query, next.query)
354
+ var hash = next.hash || parsedPath.hash
355
+ if (hash && hash.charAt(0) !== '#') {
356
+ hash = "#" + hash
311
357
  }
312
358
 
313
- State.prototype = {
314
- get: function get(charSpec) {
315
- var nextStates = this.nextStates;
359
+ return {
360
+ _normalized: true,
361
+ path: path,
362
+ query: query,
363
+ hash: hash
364
+ }
365
+ }
316
366
 
317
- for (var i = 0, l = nextStates.length; i < l; i++) {
318
- var child = nextStates[i];
367
+ /* */
319
368
 
320
- var isEqual = child.charSpec.validChars === charSpec.validChars;
321
- isEqual = isEqual && child.charSpec.invalidChars === charSpec.invalidChars;
369
+ // work around weird flow bug
370
+ var toTypes = [String, Object]
322
371
 
323
- if (isEqual) {
324
- return child;
325
- }
326
- }
372
+ var Link = {
373
+ name: 'router-link',
374
+ props: {
375
+ to: {
376
+ type: toTypes,
377
+ required: true
327
378
  },
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;
379
+ tag: {
380
+ type: String,
381
+ default: 'a'
353
382
  },
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
- }
383
+ exact: Boolean,
384
+ append: Boolean,
385
+ replace: Boolean,
386
+ activeClass: String
387
+ },
388
+ render: function render (h) {
389
+ var this$1 = this;
390
+
391
+ var router = this.$router
392
+ var current = this.$route
393
+ var to = normalizeLocation(this.to, current, this.append)
394
+ var resolved = router.match(to)
395
+ var fullPath = resolved.redirectedFrom || resolved.fullPath
396
+ var base = router.history.base
397
+ var href = base ? cleanPath(base + fullPath) : fullPath
398
+ var classes = {}
399
+ var activeClass = this.activeClass || router.options.linkActiveClass || 'router-link-active'
400
+ var compareTarget = to.path ? createRoute(null, to) : resolved
401
+ classes[activeClass] = this.exact
402
+ ? isSameRoute(current, compareTarget)
403
+ : isIncludedRoute(current, compareTarget)
404
+
405
+ var on = {
406
+ click: function (e) {
407
+ e.preventDefault()
408
+ if (this$1.replace) {
409
+ router.replace(to)
410
+ } else {
411
+ router.push(to)
379
412
  }
380
413
  }
414
+ }
381
415
 
382
- return returned;
416
+ var data = {
417
+ class: classes
383
418
  }
384
419
 
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;
420
+ if (this.tag === 'a') {
421
+ data.on = on
422
+ data.attrs = { href: href }
423
+ } else {
424
+ // find the first <a> child and apply listener and href
425
+ var a = findAnchor(this.$slots.default)
426
+ if (a) {
427
+ var aData = a.data || (a.data = {})
428
+ aData.on = on
429
+ var aAttrs = aData.attrs || (aData.attrs = {})
430
+ aAttrs.href = href
431
+ }
395
432
  }
396
- END IF **/
397
- };
398
433
 
399
- /** IF DEBUG
400
- function debug(log) {
401
- console.log(log);
434
+ return h(this.tag, data, this.$slots.default)
402
435
  }
436
+ }
403
437
 
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(", ")
438
+ function findAnchor (children) {
439
+ if (children) {
440
+ var child
441
+ for (var i = 0; i < children.length; i++) {
442
+ child = children[i]
443
+ if (child.tag === 'a') {
444
+ return child
445
+ }
446
+ if (child.children && (child = findAnchor(child.children))) {
447
+ return child
448
+ }
449
+ }
409
450
  }
410
- END IF **/
451
+ }
411
452
 
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
- }
453
+ function install (Vue) {
454
+ if (install.installed) { return }
455
+ install.installed = true
418
456
 
419
- function recognizeChar(states, ch) {
420
- var nextStates = [];
457
+ Object.defineProperty(Vue.prototype, '$router', {
458
+ get: function get () { return this.$root._router }
459
+ })
421
460
 
422
- for (var i = 0, l = states.length; i < l; i++) {
423
- var state = states[i];
461
+ Object.defineProperty(Vue.prototype, '$route', {
462
+ get: function get$1 () { return this.$root._route }
463
+ })
424
464
 
425
- nextStates = nextStates.concat(state.match(ch));
465
+ Vue.mixin({
466
+ beforeCreate: function beforeCreate () {
467
+ if (this.$options.router) {
468
+ this._router = this.$options.router
469
+ this._router.init(this)
470
+ Vue.util.defineReactive(this, '_route', this._router.history.current)
471
+ }
426
472
  }
473
+ })
427
474
 
428
- return nextStates;
429
- }
475
+ Vue.component('router-view', View)
476
+ Vue.component('router-link', Link)
477
+ }
430
478
 
431
- var oCreate = Object.create || function (proto) {
432
- function F() {}
433
- F.prototype = proto;
434
- return new F();
435
- };
479
+ var __moduleExports = Array.isArray || function (arr) {
480
+ return Object.prototype.toString.call(arr) == '[object Array]';
481
+ };
436
482
 
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
- }
483
+ var isarray = __moduleExports
463
484
 
464
- result.push({ handler: handler.handler, params: params, isDynamic: !!names.length });
485
+ /**
486
+ * Expose `pathToRegexp`.
487
+ */
488
+ var index = pathToRegexp
489
+ var parse_1 = parse
490
+ var compile_1 = compile
491
+ var tokensToFunction_1 = tokensToFunction
492
+ var tokensToRegExp_1 = tokensToRegExp
493
+
494
+ /**
495
+ * The main path matching regexp utility.
496
+ *
497
+ * @type {RegExp}
498
+ */
499
+ var PATH_REGEXP = new RegExp([
500
+ // Match escaped characters that would otherwise appear in future matches.
501
+ // This allows the user to escape special characters that won't transform.
502
+ '(\\\\.)',
503
+ // Match Express-style parameters and un-named parameters with a prefix
504
+ // and optional suffixes. Matches appear as:
505
+ //
506
+ // "/:test(\\d+)?" => ["/", "test", "\d+", undefined, "?", undefined]
507
+ // "/route(\\d+)" => [undefined, undefined, undefined, "\d+", undefined, undefined]
508
+ // "/*" => ["/", undefined, undefined, undefined, undefined, "*"]
509
+ '([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?|(\\*))'
510
+ ].join('|'), 'g')
511
+
512
+ /**
513
+ * Parse a string for the raw tokens.
514
+ *
515
+ * @param {string} str
516
+ * @return {!Array}
517
+ */
518
+ function parse (str) {
519
+ var tokens = []
520
+ var key = 0
521
+ var index = 0
522
+ var path = ''
523
+ var res
524
+
525
+ while ((res = PATH_REGEXP.exec(str)) != null) {
526
+ var m = res[0]
527
+ var escaped = res[1]
528
+ var offset = res.index
529
+ path += str.slice(index, offset)
530
+ index = offset + m.length
531
+
532
+ // Ignore already escaped sequences.
533
+ if (escaped) {
534
+ path += escaped[1]
535
+ continue
465
536
  }
466
537
 
467
- return result;
468
- }
538
+ var next = str[index]
539
+ var prefix = res[2]
540
+ var name = res[3]
541
+ var capture = res[4]
542
+ var group = res[5]
543
+ var modifier = res[6]
544
+ var asterisk = res[7]
545
+
546
+ // Push the current path onto the tokens.
547
+ if (path) {
548
+ tokens.push(path)
549
+ path = ''
550
+ }
469
551
 
470
- function addSegment(currentState, segment) {
471
- segment.eachChar(function (ch) {
472
- var state;
552
+ var partial = prefix != null && next != null && next !== prefix
553
+ var repeat = modifier === '+' || modifier === '*'
554
+ var optional = modifier === '?' || modifier === '*'
555
+ var delimiter = res[2] || '/'
556
+ var pattern = capture || group || (asterisk ? '.*' : '[^' + delimiter + ']+?')
557
+
558
+ tokens.push({
559
+ name: name || key++,
560
+ prefix: prefix || '',
561
+ delimiter: delimiter,
562
+ optional: optional,
563
+ repeat: repeat,
564
+ partial: partial,
565
+ asterisk: !!asterisk,
566
+ pattern: escapeGroup(pattern)
567
+ })
568
+ }
473
569
 
474
- currentState = currentState.put(ch);
475
- });
570
+ // Match any characters still remaining.
571
+ if (index < str.length) {
572
+ path += str.substr(index)
573
+ }
476
574
 
477
- return currentState;
575
+ // If the path exists, push it onto the end.
576
+ if (path) {
577
+ tokens.push(path)
478
578
  }
479
579
 
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);
580
+ return tokens
581
+ }
582
+
583
+ /**
584
+ * Compile a string to a template function for the path.
585
+ *
586
+ * @param {string} str
587
+ * @return {!function(Object=, Object=)}
588
+ */
589
+ function compile (str) {
590
+ return tokensToFunction(parse(str))
591
+ }
592
+
593
+ /**
594
+ * Prettier encoding of URI path segments.
595
+ *
596
+ * @param {string}
597
+ * @return {string}
598
+ */
599
+ function encodeURIComponentPretty (str) {
600
+ return encodeURI(str).replace(/[\/?#]/g, function (c) {
601
+ return '%' + c.charCodeAt(0).toString(16).toUpperCase()
602
+ })
603
+ }
604
+
605
+ /**
606
+ * Encode the asterisk parameter. Similar to `pretty`, but allows slashes.
607
+ *
608
+ * @param {string}
609
+ * @return {string}
610
+ */
611
+ function encodeAsterisk (str) {
612
+ return encodeURI(str).replace(/[?#]/g, function (c) {
613
+ return '%' + c.charCodeAt(0).toString(16).toUpperCase()
614
+ })
615
+ }
616
+
617
+ /**
618
+ * Expose a method for transforming tokens into the path function.
619
+ */
620
+ function tokensToFunction (tokens) {
621
+ // Compile all the tokens into regexps.
622
+ var matches = new Array(tokens.length)
623
+
624
+ // Compile all the patterns before compilation.
625
+ for (var i = 0; i < tokens.length; i++) {
626
+ if (typeof tokens[i] === 'object') {
627
+ matches[i] = new RegExp('^(?:' + tokens[i].pattern + ')$')
628
+ }
484
629
  }
485
630
 
486
- // The main interface
631
+ return function (obj, opts) {
632
+ var path = ''
633
+ var data = obj || {}
634
+ var options = opts || {}
635
+ var encode = options.pretty ? encodeURIComponentPretty : encodeURIComponent
487
636
 
488
- var RouteRecognizer = function RouteRecognizer() {
489
- this.rootState = new State();
490
- this.names = {};
491
- };
637
+ for (var i = 0; i < tokens.length; i++) {
638
+ var token = tokens[i]
492
639
 
493
- RouteRecognizer.prototype = {
494
- add: function add(routes, options) {
495
- var currentState = this.rootState,
496
- regex = "^",
497
- specificity = {},
498
- handlers = [],
499
- allSegments = [],
500
- name;
640
+ if (typeof token === 'string') {
641
+ path += token
501
642
 
502
- var isEmpty = true;
643
+ continue
644
+ }
503
645
 
504
- for (var i = 0, l = routes.length; i < l; i++) {
505
- var route = routes[i],
506
- names = [];
646
+ var value = data[token.name]
647
+ var segment
507
648
 
508
- var segments = parse(route.path, names, specificity);
649
+ if (value == null) {
650
+ if (token.optional) {
651
+ // Prepend partial segment prefixes.
652
+ if (token.partial) {
653
+ path += token.prefix
654
+ }
509
655
 
510
- allSegments = allSegments.concat(segments);
656
+ continue
657
+ } else {
658
+ throw new TypeError('Expected "' + token.name + '" to be defined')
659
+ }
660
+ }
511
661
 
512
- for (var j = 0, m = segments.length; j < m; j++) {
513
- var segment = segments[j];
662
+ if (isarray(value)) {
663
+ if (!token.repeat) {
664
+ throw new TypeError('Expected "' + token.name + '" to not repeat, but received `' + JSON.stringify(value) + '`')
665
+ }
514
666
 
515
- if (segment instanceof EpsilonSegment) {
516
- continue;
667
+ if (value.length === 0) {
668
+ if (token.optional) {
669
+ continue
670
+ } else {
671
+ throw new TypeError('Expected "' + token.name + '" to not be empty')
517
672
  }
673
+ }
518
674
 
519
- isEmpty = false;
675
+ for (var j = 0; j < value.length; j++) {
676
+ segment = encode(value[j])
520
677
 
521
- // Add a "/" for the new segment
522
- currentState = currentState.put({ validChars: "/" });
523
- regex += "/";
678
+ if (!matches[i].test(segment)) {
679
+ throw new TypeError('Expected all "' + token.name + '" to match "' + token.pattern + '", but received `' + JSON.stringify(segment) + '`')
680
+ }
524
681
 
525
- // Add a representation of the segment to the NFA and regex
526
- currentState = addSegment(currentState, segment);
527
- regex += segment.regex();
682
+ path += (j === 0 ? token.prefix : token.delimiter) + segment
528
683
  }
529
684
 
530
- var handler = { handler: route.handler, names: names };
531
- handlers.push(handler);
685
+ continue
532
686
  }
533
687
 
534
- if (isEmpty) {
535
- currentState = currentState.put({ validChars: "/" });
536
- regex += "/";
688
+ segment = token.asterisk ? encodeAsterisk(value) : encode(value)
689
+
690
+ if (!matches[i].test(segment)) {
691
+ throw new TypeError('Expected "' + token.name + '" to match "' + token.pattern + '", but received "' + segment + '"')
537
692
  }
538
693
 
539
- currentState.handlers = handlers;
540
- currentState.regex = new RegExp(regex + "$");
541
- currentState.specificity = specificity;
694
+ path += token.prefix + segment
695
+ }
542
696
 
543
- if (name = options && options.as) {
544
- this.names[name] = {
545
- segments: allSegments,
546
- handlers: handlers
547
- };
548
- }
549
- },
697
+ return path
698
+ }
699
+ }
550
700
 
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
- }
701
+ /**
702
+ * Escape a regular expression string.
703
+ *
704
+ * @param {string} str
705
+ * @return {string}
706
+ */
707
+ function escapeString (str) {
708
+ return str.replace(/([.+*?=^!:${}()[\]|\/\\])/g, '\\$1')
709
+ }
710
+
711
+ /**
712
+ * Escape the capturing group by escaping special characters and meaning.
713
+ *
714
+ * @param {string} group
715
+ * @return {string}
716
+ */
717
+ function escapeGroup (group) {
718
+ return group.replace(/([=!:$\/()])/g, '\\$1')
719
+ }
720
+
721
+ /**
722
+ * Attach the keys as a property of the regexp.
723
+ *
724
+ * @param {!RegExp} re
725
+ * @param {Array} keys
726
+ * @return {!RegExp}
727
+ */
728
+ function attachKeys (re, keys) {
729
+ re.keys = keys
730
+ return re
731
+ }
732
+
733
+ /**
734
+ * Get the flags for a regexp from the options.
735
+ *
736
+ * @param {Object} options
737
+ * @return {string}
738
+ */
739
+ function flags (options) {
740
+ return options.sensitive ? '' : 'i'
741
+ }
742
+
743
+ /**
744
+ * Pull out keys from a regexp.
745
+ *
746
+ * @param {!RegExp} path
747
+ * @param {!Array} keys
748
+ * @return {!RegExp}
749
+ */
750
+ function regexpToRegexp (path, keys) {
751
+ // Use a negative lookahead to match only capturing groups.
752
+ var groups = path.source.match(/\((?!\?)/g)
753
+
754
+ if (groups) {
755
+ for (var i = 0; i < groups.length; i++) {
756
+ keys.push({
757
+ name: i,
758
+ prefix: null,
759
+ delimiter: null,
760
+ optional: false,
761
+ repeat: false,
762
+ partial: false,
763
+ asterisk: false,
764
+ pattern: null
765
+ })
766
+ }
767
+ }
557
768
 
558
- for (var i = 0, l = route.handlers.length; i < l; i++) {
559
- result.push(route.handlers[i]);
560
- }
769
+ return attachKeys(path, keys)
770
+ }
561
771
 
562
- return result;
563
- },
772
+ /**
773
+ * Transform an array into a regexp.
774
+ *
775
+ * @param {!Array} path
776
+ * @param {Array} keys
777
+ * @param {!Object} options
778
+ * @return {!RegExp}
779
+ */
780
+ function arrayToRegexp (path, keys, options) {
781
+ var parts = []
564
782
 
565
- hasRoute: function hasRoute(name) {
566
- return !!this.names[name];
567
- },
783
+ for (var i = 0; i < path.length; i++) {
784
+ parts.push(pathToRegexp(path[i], keys, options).source)
785
+ }
568
786
 
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
- }
787
+ var regexp = new RegExp('(?:' + parts.join('|') + ')', flags(options))
575
788
 
576
- var segments = route.segments;
789
+ return attachKeys(regexp, keys)
790
+ }
577
791
 
578
- for (var i = 0, l = segments.length; i < l; i++) {
579
- var segment = segments[i];
792
+ /**
793
+ * Create a path regexp from string input.
794
+ *
795
+ * @param {string} path
796
+ * @param {!Array} keys
797
+ * @param {!Object} options
798
+ * @return {!RegExp}
799
+ */
800
+ function stringToRegexp (path, keys, options) {
801
+ var tokens = parse(path)
802
+ var re = tokensToRegExp(tokens, options)
803
+
804
+ // Attach keys back to the regexp.
805
+ for (var i = 0; i < tokens.length; i++) {
806
+ if (typeof tokens[i] !== 'string') {
807
+ keys.push(tokens[i])
808
+ }
809
+ }
580
810
 
581
- if (segment instanceof EpsilonSegment) {
582
- continue;
583
- }
811
+ return attachKeys(re, keys)
812
+ }
584
813
 
585
- output += "/";
586
- output += segment.generate(params);
587
- }
814
+ /**
815
+ * Expose a function for taking tokens and returning a RegExp.
816
+ *
817
+ * @param {!Array} tokens
818
+ * @param {Object=} options
819
+ * @return {!RegExp}
820
+ */
821
+ function tokensToRegExp (tokens, options) {
822
+ options = options || {}
588
823
 
589
- if (output.charAt(0) !== '/') {
590
- output = '/' + output;
591
- }
824
+ var strict = options.strict
825
+ var end = options.end !== false
826
+ var route = ''
827
+ var lastToken = tokens[tokens.length - 1]
828
+ var endsWithSlash = typeof lastToken === 'string' && /\/$/.test(lastToken)
592
829
 
593
- if (params && params.queryParams) {
594
- output += this.generateQueryString(params.queryParams);
595
- }
830
+ // Iterate over the tokens and create our regexp string.
831
+ for (var i = 0; i < tokens.length; i++) {
832
+ var token = tokens[i]
596
833
 
597
- return output;
598
- },
834
+ if (typeof token === 'string') {
835
+ route += escapeString(token)
836
+ } else {
837
+ var prefix = escapeString(token.prefix)
838
+ var capture = '(?:' + token.pattern + ')'
599
839
 
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
- }
840
+ if (token.repeat) {
841
+ capture += '(?:' + prefix + capture + ')*'
607
842
  }
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
- }
843
+
844
+ if (token.optional) {
845
+ if (!token.partial) {
846
+ capture = '(?:' + prefix + '(' + capture + '))?'
621
847
  } else {
622
- pair += "=" + encodeURIComponent(value);
623
- pairs.push(pair);
848
+ capture = prefix + '(' + capture + ')?'
624
849
  }
850
+ } else {
851
+ capture = prefix + '(' + capture + ')'
625
852
  }
626
853
 
627
- if (pairs.length === 0) {
628
- return '';
629
- }
854
+ route += capture
855
+ }
856
+ }
630
857
 
631
- return "?" + pairs.join("&");
632
- },
858
+ // In non-strict mode we allow a slash at the end of match. If the path to
859
+ // match already ends with a slash, we remove it for consistency. The slash
860
+ // is valid at the end of a path match, not in the middle. This is important
861
+ // in non-ending mode, where "/test/" shouldn't match "/test//route".
862
+ if (!strict) {
863
+ route = (endsWithSlash ? route.slice(0, -2) : route) + '(?:\\/(?=$))?'
864
+ }
633
865
 
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
- },
866
+ if (end) {
867
+ route += '$'
868
+ } else {
869
+ // In non-ending mode, we need the capturing groups to match as much as
870
+ // possible by using a positive lookahead to the end or next path segment.
871
+ route += strict && endsWithSlash ? '' : '(?=\\/|$)'
872
+ }
664
873
 
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
- }
874
+ return new RegExp('^' + route, flags(options))
875
+ }
876
+
877
+ /**
878
+ * Normalize the given path string, returning a regular expression.
879
+ *
880
+ * An empty array can be passed in for the keys, which will hold the
881
+ * placeholder key descriptions. For example, using `/user/:id`, `keys` will
882
+ * contain `[{ name: 'id', delimiter: '/', optional: false, repeat: false }]`.
883
+ *
884
+ * @param {(string|RegExp|Array)} path
885
+ * @param {(Array|Object)=} keys
886
+ * @param {Object=} options
887
+ * @return {!RegExp}
888
+ */
889
+ function pathToRegexp (path, keys, options) {
890
+ keys = keys || []
891
+
892
+ if (!isarray(keys)) {
893
+ options = /** @type {!Object} */ (keys)
894
+ keys = []
895
+ } else if (!options) {
896
+ options = {}
897
+ }
683
898
 
684
- path = tryDecode(path);
685
- if (!path) return;
899
+ if (path instanceof RegExp) {
900
+ return regexpToRegexp(path, /** @type {!Array} */ (keys))
901
+ }
686
902
 
687
- // DEBUG GROUP path
903
+ if (isarray(path)) {
904
+ return arrayToRegexp(/** @type {!Array} */ (path), /** @type {!Array} */ (keys), options)
905
+ }
688
906
 
689
- if (path.charAt(0) !== "/") {
690
- path = "/" + path;
691
- }
907
+ return stringToRegexp(/** @type {string} */ (path), /** @type {!Array} */ (keys), options)
908
+ }
692
909
 
693
- pathLen = path.length;
694
- if (pathLen > 1 && path.charAt(pathLen - 1) === "/") {
695
- path = path.substr(0, pathLen - 1);
696
- isSlashDropped = true;
697
- }
910
+ index.parse = parse_1;
911
+ index.compile = compile_1;
912
+ index.tokensToFunction = tokensToFunction_1;
913
+ index.tokensToRegExp = tokensToRegExp_1;
698
914
 
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
- }
915
+ /* */
705
916
 
706
- // END DEBUG GROUP
917
+ function createRouteMap (routes) {
918
+ var pathMap = Object.create(null)
919
+ var nameMap = Object.create(null)
707
920
 
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
- }
921
+ routes.forEach(function (route) {
922
+ addRouteRecord(pathMap, nameMap, route)
923
+ })
714
924
 
715
- states = sortSolutions(solutions);
925
+ return {
926
+ pathMap: pathMap,
927
+ nameMap: nameMap
928
+ }
929
+ }
930
+
931
+ function addRouteRecord (
932
+ pathMap,
933
+ nameMap,
934
+ route,
935
+ parent,
936
+ matchAs
937
+ ) {
938
+ var path = route.path;
939
+ var name = route.name;
940
+ assert(path != null, "\"path\" is required in a route configuration.")
941
+
942
+ var record = {
943
+ path: normalizePath(path, parent),
944
+ components: route.components || { default: route.component },
945
+ instances: {},
946
+ name: name,
947
+ parent: parent,
948
+ matchAs: matchAs,
949
+ redirect: route.redirect,
950
+ beforeEnter: route.beforeEnter,
951
+ meta: route.meta || {}
952
+ }
716
953
 
717
- var state = solutions[0];
954
+ if (route.children) {
955
+ // Warn if route is named and has a default child route.
956
+ // If users navigate to this route by name, the default child will
957
+ // not be rendered (GH Issue #629)
958
+ if ("production" !== 'production') {}
959
+ route.children.forEach(function (child) {
960
+ addRouteRecord(pathMap, nameMap, child, record)
961
+ })
962
+ }
718
963
 
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
- }
964
+ if (route.alias) {
965
+ if (Array.isArray(route.alias)) {
966
+ route.alias.forEach(function (alias) {
967
+ addRouteRecord(pathMap, nameMap, { path: alias }, parent, record.path)
968
+ })
969
+ } else {
970
+ addRouteRecord(pathMap, nameMap, { path: route.alias }, parent, record.path)
727
971
  }
728
- };
972
+ }
729
973
 
730
- RouteRecognizer.prototype.map = map;
974
+ pathMap[record.path] = record
975
+ if (name) { nameMap[name] = record }
976
+ }
731
977
 
732
- var genQuery = RouteRecognizer.prototype.generateQueryString;
978
+ function normalizePath (path, parent) {
979
+ path = path.replace(/\/$/, '')
980
+ if (path[0] === '/') { return path }
981
+ if (parent == null) { return path }
982
+ return cleanPath(((parent.path) + "/" + path))
983
+ }
733
984
 
734
- // export default for holding the Vue reference
735
- var exports$1 = {};
736
- /**
737
- * Warn stuff.
738
- *
739
- * @param {String} msg
740
- */
985
+ /* */
741
986
 
742
- function warn$1(msg) {
743
- /* istanbul ignore next */
744
- if (typeof console !== 'undefined') {
745
- console.error('[vue-router] ' + msg);
987
+ var regexpCache = Object.create(null)
988
+
989
+ var regexpCompileCache = Object.create(null)
990
+
991
+ function createMatcher (routes) {
992
+ var ref = createRouteMap(routes);
993
+ var pathMap = ref.pathMap;
994
+ var nameMap = ref.nameMap;
995
+
996
+ function match (
997
+ raw,
998
+ currentRoute,
999
+ redirectedFrom
1000
+ ) {
1001
+ var location = normalizeLocation(raw, currentRoute)
1002
+ var name = location.name;
1003
+
1004
+ if (name) {
1005
+ var record = nameMap[name]
1006
+ if (record) {
1007
+ location.path = fillParams(record.path, location.params, ("named route \"" + name + "\""))
1008
+ return _createRoute(record, location, redirectedFrom)
1009
+ }
1010
+ } else if (location.path) {
1011
+ location.params = {}
1012
+ for (var path in pathMap) {
1013
+ if (matchRoute(path, location.params, location.path)) {
1014
+ return _createRoute(pathMap[path], location, redirectedFrom)
1015
+ }
1016
+ }
746
1017
  }
1018
+ // no match
1019
+ return _createRoute(null, location)
747
1020
  }
748
1021
 
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);
1022
+ function redirect (
1023
+ record,
1024
+ location
1025
+ ) {
1026
+ var originalRedirect = record.redirect
1027
+ var redirect = typeof originalRedirect === 'function'
1028
+ ? originalRedirect(createRoute(record, location))
1029
+ : originalRedirect
1030
+
1031
+ if (typeof redirect === 'string') {
1032
+ redirect = { path: redirect }
763
1033
  }
764
- // a query!
765
- if (relative.charAt(0) === '?') {
766
- return base + relative;
1034
+
1035
+ if (!redirect || typeof redirect !== 'object') {
1036
+ warn(false, ("invalid redirect option: " + (JSON.stringify(redirect))))
1037
+ return _createRoute(null, location)
767
1038
  }
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();
1039
+
1040
+ var re = redirect
1041
+ var name = re.name;
1042
+ var path = re.path;
1043
+ var query = location.query;
1044
+ var hash = location.hash;
1045
+ var params = location.params;
1046
+ query = re.hasOwnProperty('query') ? re.query : query
1047
+ hash = re.hasOwnProperty('hash') ? re.hash : hash
1048
+ params = re.hasOwnProperty('params') ? re.params : params
1049
+
1050
+ if (name) {
1051
+ // resolved named direct
1052
+ var targetRecord = nameMap[name]
1053
+ assert(targetRecord, ("redirect failed: named route \"" + name + "\" not found."))
1054
+ return match({
1055
+ _normalized: true,
1056
+ name: name,
1057
+ query: query,
1058
+ hash: hash,
1059
+ params: params
1060
+ }, undefined, location)
1061
+ } else if (path) {
1062
+ // 1. resolve relative redirect
1063
+ var rawPath = resolveRecordPath(path, record)
1064
+ // 2. resolve params
1065
+ var resolvedPath = fillParams(rawPath, params, ("redirect route with path \"" + rawPath + "\""))
1066
+ // 3. rematch with existing query and hash
1067
+ return match({
1068
+ _normalized: true,
1069
+ path: resolvedPath,
1070
+ query: query,
1071
+ hash: hash
1072
+ }, undefined, location)
1073
+ } else {
1074
+ warn(false, ("invalid redirect option: " + (JSON.stringify(redirect))))
1075
+ return _createRoute(null, location)
774
1076
  }
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
- }
1077
+ }
1078
+
1079
+ function alias (
1080
+ record,
1081
+ location,
1082
+ matchAs
1083
+ ) {
1084
+ var aliasedPath = fillParams(matchAs, location.params, ("aliased route with path \"" + matchAs + "\""))
1085
+ var aliasedMatch = match({
1086
+ _normalized: true,
1087
+ path: aliasedPath
1088
+ })
1089
+ if (aliasedMatch) {
1090
+ var matched = aliasedMatch.matched
1091
+ var aliasedRecord = matched[matched.length - 1]
1092
+ location.params = aliasedMatch.params
1093
+ return _createRoute(aliasedRecord, location)
786
1094
  }
787
- // ensure leading slash
788
- if (stack[0] !== '') {
789
- stack.unshift('');
1095
+ return _createRoute(null, location)
1096
+ }
1097
+
1098
+ function _createRoute (
1099
+ record,
1100
+ location,
1101
+ redirectedFrom
1102
+ ) {
1103
+ if (record && record.redirect) {
1104
+ return redirect(record, redirectedFrom || location)
790
1105
  }
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;
1106
+ if (record && record.matchAs) {
1107
+ return alias(record, location, record.matchAs)
842
1108
  }
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;
1109
+ return createRoute(record, location, redirectedFrom)
872
1110
  }
873
1111
 
874
- var hashRE = /#.*$/;
1112
+ return match
1113
+ }
1114
+
1115
+ function matchRoute (
1116
+ path,
1117
+ params,
1118
+ pathname
1119
+ ) {
1120
+ var keys, regexp
1121
+ var hit = regexpCache[path]
1122
+ if (hit) {
1123
+ keys = hit.keys
1124
+ regexp = hit.regexp
1125
+ } else {
1126
+ keys = []
1127
+ regexp = index(path, keys)
1128
+ regexpCache[path] = { keys: keys, regexp: regexp }
1129
+ }
1130
+ var m = pathname.match(regexp)
875
1131
 
876
- var HTML5History = (function () {
877
- function HTML5History(_ref) {
878
- var root = _ref.root;
879
- var onChange = _ref.onChange;
880
- babelHelpers.classCallCheck(this, HTML5History);
1132
+ if (!m) {
1133
+ return false
1134
+ } else if (!params) {
1135
+ return true
1136
+ }
881
1137
 
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
- }
1138
+ for (var i = 1, len = m.length; i < len; ++i) {
1139
+ var key = keys[i - 1]
1140
+ var val = typeof m[i] === 'string' ? decodeURIComponent(m[i]) : m[i]
1141
+ if (key) { params[key.name] = val }
1142
+ }
898
1143
 
899
- HTML5History.prototype.start = function start() {
900
- var _this = this;
1144
+ return true
1145
+ }
1146
+
1147
+ function fillParams (
1148
+ path,
1149
+ params,
1150
+ routeMsg
1151
+ ) {
1152
+ try {
1153
+ var filler =
1154
+ regexpCompileCache[path] ||
1155
+ (regexpCompileCache[path] = index.compile(path))
1156
+ return filler(params || {}, { pretty: true })
1157
+ } catch (e) {
1158
+ assert(false, ("missing param for " + routeMsg + ": " + (e.message)))
1159
+ return ''
1160
+ }
1161
+ }
901
1162
 
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
- }
1163
+ function resolveRecordPath (path, record) {
1164
+ return resolvePath(path, record.parent ? record.parent.path : '/', true)
1165
+ }
960
1166
 
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
- };
1167
+ /* */
997
1168
 
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
- };
1169
+ var inBrowser = typeof window !== 'undefined'
1003
1170
 
1004
- return HashHistory;
1005
- })();
1171
+ var supportsHistory = inBrowser && (function () {
1172
+ var ua = window.navigator.userAgent
1006
1173
 
1007
- var AbstractHistory = (function () {
1008
- function AbstractHistory(_ref) {
1009
- var onChange = _ref.onChange;
1010
- babelHelpers.classCallCheck(this, AbstractHistory);
1174
+ if (
1175
+ (ua.indexOf('Android 2.') !== -1 || ua.indexOf('Android 4.0') !== -1) &&
1176
+ ua.indexOf('Mobile Safari') !== -1 &&
1177
+ ua.indexOf('Chrome') === -1 &&
1178
+ ua.indexOf('Windows Phone') === -1
1179
+ ) {
1180
+ return false
1181
+ }
1011
1182
 
1012
- this.onChange = onChange;
1013
- this.currentPath = '/';
1014
- }
1183
+ return window.history && 'pushState' in window.history
1184
+ })()
1015
1185
 
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();
1186
+ /* */
1187
+
1188
+ function runQueue (queue, fn, cb) {
1189
+ var step = function (index) {
1190
+ if (index >= queue.length) {
1191
+ cb()
1074
1192
  } else {
1075
- transition.callHook(hook, fromComponent, next, {
1076
- expectBoolean: true
1077
- });
1193
+ if (queue[index]) {
1194
+ fn(queue[index], function () {
1195
+ step(index + 1)
1196
+ })
1197
+ } else {
1198
+ step(index + 1)
1199
+ }
1078
1200
  }
1079
1201
  }
1202
+ step(0)
1203
+ }
1204
+
1205
+ /* */
1206
+
1207
+
1208
+ var History = function History (router, base) {
1209
+ this.router = router
1210
+ this.base = normalizeBase(base)
1211
+ // start with a route object that stands for "nowhere"
1212
+ this.current = START
1213
+ this.pending = null
1214
+ };
1215
+
1216
+ History.prototype.listen = function listen (cb) {
1217
+ this.cb = cb
1218
+ };
1219
+
1220
+ History.prototype.transitionTo = function transitionTo (location, cb) {
1221
+ var this$1 = this;
1222
+
1223
+ var route = this.router.match(location, this.current)
1224
+ this.confirmTransition(route, function () {
1225
+ this$1.updateRoute(route)
1226
+ cb && cb(route)
1227
+ this$1.ensureURL()
1228
+ })
1229
+ };
1230
+
1231
+ History.prototype.confirmTransition = function confirmTransition (route, cb) {
1232
+ var this$1 = this;
1233
+
1234
+ var current = this.current
1235
+ if (isSameRoute(route, current)) {
1236
+ this.ensureURL()
1237
+ return
1238
+ }
1080
1239
 
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();
1240
+ var ref = resolveQueue(this.current.matched, route.matched);
1241
+ var deactivated = ref.deactivated;
1242
+ var activated = ref.activated;
1243
+
1244
+ var queue = [].concat(
1245
+ // in-component leave guards
1246
+ extractLeaveGuards(deactivated),
1247
+ // global before hooks
1248
+ this.router.beforeHooks,
1249
+ // enter guards
1250
+ activated.map(function (m) { return m.beforeEnter; }),
1251
+ // async components
1252
+ resolveAsyncComponents(activated)
1253
+ )
1254
+
1255
+ this.pending = route
1256
+ var iterator = function (hook, next) {
1257
+ if (this$1.pending !== route) { return }
1258
+ hook(route, current, function (to) {
1259
+ if (to === false) {
1260
+ // next(false) -> abort navigation, ensure current URL
1261
+ this$1.ensureURL()
1262
+ } else if (typeof to === 'string' || typeof to === 'object') {
1263
+ // next('/') or next({ path: '/' }) -> redirect
1264
+ this$1.push(to)
1099
1265
  } else {
1100
- transition.callHook(hook, null, next, {
1101
- expectBoolean: true
1102
- });
1266
+ // confirm transition and pass on the value
1267
+ next(to)
1103
1268
  }
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();
1269
+ })
1270
+ }
1271
+
1272
+ runQueue(queue, iterator, function () {
1273
+ var postEnterCbs = []
1274
+ // wait until async components are resolved before
1275
+ // extracting in-component enter guards
1276
+ runQueue(extractEnterGuards(activated, postEnterCbs), iterator, function () {
1277
+ if (this$1.pending === route) {
1278
+ this$1.pending = null
1279
+ cb(route)
1280
+ this$1.router.app.$nextTick(function () {
1281
+ postEnterCbs.forEach(function (cb) { return cb(); })
1282
+ })
1283
+ }
1284
+ })
1285
+ })
1286
+ };
1287
+
1288
+ History.prototype.updateRoute = function updateRoute (route) {
1289
+ var prev = this.current
1290
+ this.current = route
1291
+ this.cb && this.cb(route)
1292
+ this.router.afterHooks.forEach(function (hook) {
1293
+ hook && hook(route, prev)
1294
+ })
1295
+ };
1296
+
1297
+ function normalizeBase (base) {
1298
+ if (!base) {
1299
+ if (inBrowser) {
1300
+ // respect <base> tag
1301
+ var baseEl = document.querySelector('base')
1302
+ base = baseEl ? baseEl.getAttribute('href') : '/'
1120
1303
  } else {
1121
- transition.callHooks(hook, component, next);
1304
+ base = '/'
1122
1305
  }
1123
1306
  }
1307
+ // make sure there's the starting slash
1308
+ if (base.charAt(0) !== '/') {
1309
+ base = '/' + base
1310
+ }
1311
+ // remove trailing slash
1312
+ return base.replace(/\/$/, '')
1313
+ }
1314
+
1315
+ function resolveQueue (
1316
+ current,
1317
+ next
1318
+ ) {
1319
+ var i
1320
+ var max = Math.max(current.length, next.length)
1321
+ for (i = 0; i < max; i++) {
1322
+ if (current[i] !== next[i]) {
1323
+ break
1324
+ }
1325
+ }
1326
+ return {
1327
+ activated: next.slice(i),
1328
+ deactivated: current.slice(i)
1329
+ }
1330
+ }
1124
1331
 
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);
1332
+ function extractLeaveGuards (matched) {
1333
+ return flatMapComponents(matched, function (def, instance) {
1334
+ var guard = def && def.beforeRouteLeave
1335
+ if (guard) {
1336
+ return function routeLeaveGuard () {
1337
+ return guard.apply(instance, arguments)
1140
1338
  }
1141
- cb && cb();
1142
- return;
1143
1339
  }
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
- }
1340
+ }).reverse()
1341
+ }
1342
+
1343
+ function extractEnterGuards (matched, cbs) {
1344
+ return flatMapComponents(matched, function (def, _, match, key) {
1345
+ var guard = def && def.beforeRouteEnter
1346
+ if (guard) {
1347
+ return function routeEnterGuard (to, from, next) {
1348
+ return guard(to, from, function (cb) {
1349
+ next(cb)
1350
+ if (typeof cb === 'function') {
1351
+ cbs.push(function () {
1352
+ cb(match.instances[key])
1353
+ })
1354
+ }
1355
+ })
1196
1356
  }
1197
1357
  }
1358
+ })
1359
+ }
1198
1360
 
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;
1361
+ function resolveAsyncComponents (matched) {
1362
+ return flatMapComponents(matched, function (def, _, match, key) {
1363
+ // if it's a function and doesn't have Vue options attached,
1364
+ // assume it's an async component resolve function.
1365
+ // we are not using Vue's default async resolving mechanism because
1366
+ // we want to halt the navigation until the incoming component has been
1367
+ // resolved.
1368
+ if (typeof def === 'function' && !def.options) {
1369
+ return function (to, from, next) {
1370
+ var resolve = function (resolvedDef) {
1371
+ match.components[key] = resolvedDef
1372
+ next()
1223
1373
  }
1224
- component.$before(view.anchor, null, false);
1225
- }
1226
- cb && cb();
1227
- };
1228
1374
 
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);
1375
+ var reject = function (reason) {
1376
+ warn(false, ("Failed to resolve async component " + key + ": " + reason))
1377
+ next(false)
1247
1378
  }
1248
- afterData();
1249
- }
1250
- };
1251
1379
 
1252
- if (activateHook) {
1253
- transition.callHooks(activateHook, component, afterActivate, {
1254
- cleanup: cleanup,
1255
- postActivate: true
1256
- });
1257
- } else {
1258
- afterActivate();
1380
+ var res = def(resolve, reject)
1381
+ if (res && typeof res.then === 'function') {
1382
+ res.then(resolve, reject)
1383
+ }
1384
+ }
1259
1385
  }
1386
+ })
1387
+ }
1388
+
1389
+ function flatMapComponents (
1390
+ matched,
1391
+ fn
1392
+ ) {
1393
+ return Array.prototype.concat.apply([], matched.map(function (m) {
1394
+ return Object.keys(m.components).map(function (key) { return fn(
1395
+ m.components[key],
1396
+ m.instances[key],
1397
+ m, key
1398
+ ); })
1399
+ }))
1400
+ }
1401
+
1402
+ /* */
1403
+
1404
+ function saveScrollPosition (key) {
1405
+ if (!key) { return }
1406
+ window.sessionStorage.setItem(key, JSON.stringify({
1407
+ x: window.pageXOffset,
1408
+ y: window.pageYOffset
1409
+ }))
1410
+ }
1411
+
1412
+ function getScrollPosition (key) {
1413
+ if (!key) { return }
1414
+ return JSON.parse(window.sessionStorage.getItem(key))
1415
+ }
1416
+
1417
+ function getElementPosition (el) {
1418
+ var docRect = document.documentElement.getBoundingClientRect()
1419
+ var elRect = el.getBoundingClientRect()
1420
+ return {
1421
+ x: elRect.left - docRect.left,
1422
+ y: elRect.top - docRect.top
1260
1423
  }
1424
+ }
1261
1425
 
1262
- /**
1263
- * Reuse a view, just reload data if necessary.
1264
- *
1265
- * @param {Directive} view
1266
- * @param {Transition} transition
1267
- */
1426
+ function isValidPosition (obj) {
1427
+ return isNumber(obj.x) || isNumber(obj.y)
1428
+ }
1268
1429
 
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
- }
1430
+ function normalizePosition (obj) {
1431
+ return {
1432
+ x: isNumber(obj.x) ? obj.x : window.pageXOffset,
1433
+ y: isNumber(obj.y) ? obj.y : window.pageYOffset
1275
1434
  }
1435
+ }
1276
1436
 
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
- }
1437
+ function isNumber (v) {
1438
+ return typeof v === 'number'
1439
+ }
1317
1440
 
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
- */
1441
+ /* */
1324
1442
 
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
1443
 
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
- }
1444
+ var genKey = function () { return String(Date.now()); }
1445
+ var _key = genKey()
1460
1446
 
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
- };
1447
+ var HTML5History = (function (History) {
1448
+ function HTML5History (router, base) {
1449
+ var this$1 = this;
1568
1450
 
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
- }
1451
+ History.call(this, router, base)
1629
1452
 
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
- };
1453
+ this.transitionTo(getLocation(this.base))
1670
1454
 
1671
- return RouteTransition;
1672
- })();
1455
+ var expectScroll = router.options.scrollBehavior
1456
+ window.addEventListener('popstate', function (e) {
1457
+ _key = e.state && e.state.key
1458
+ var current = this$1.current
1459
+ this$1.transitionTo(getLocation(this$1.base), function (next) {
1460
+ if (expectScroll) {
1461
+ this$1.handleScroll(next, current, true)
1462
+ }
1463
+ })
1464
+ })
1673
1465
 
1674
- function isPlainOjbect(val) {
1675
- return Object.prototype.toString.call(val) === '[object Object]';
1466
+ if (expectScroll) {
1467
+ window.addEventListener('scroll', function () {
1468
+ saveScrollPosition(_key)
1469
+ })
1470
+ }
1676
1471
  }
1677
1472
 
1678
- function toArray(val) {
1679
- return val ? Array.prototype.slice.call(val) : [];
1680
- }
1473
+ if ( History ) HTML5History.__proto__ = History;
1474
+ HTML5History.prototype = Object.create( History && History.prototype );
1475
+ HTML5History.prototype.constructor = HTML5History;
1681
1476
 
1682
- var internalKeysRE = /^(component|subRoutes|fullPath)$/;
1477
+ HTML5History.prototype.go = function go (n) {
1478
+ window.history.go(n)
1479
+ };
1683
1480
 
1684
- /**
1685
- * Route Context Object
1686
- *
1687
- * @param {String} path
1688
- * @param {Router} router
1689
- */
1481
+ HTML5History.prototype.push = function push (location) {
1482
+ var this$1 = this;
1690
1483
 
1691
- var Route = function Route(path, router) {
1692
- var _this = this;
1484
+ var current = this.current
1485
+ this.transitionTo(location, function (route) {
1486
+ pushState(cleanPath(this$1.base + route.fullPath))
1487
+ this$1.handleScroll(route, current, false)
1488
+ })
1489
+ };
1693
1490
 
1694
- babelHelpers.classCallCheck(this, Route);
1491
+ HTML5History.prototype.replace = function replace (location) {
1492
+ var this$1 = this;
1695
1493
 
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
- }, {});
1494
+ var current = this.current
1495
+ this.transitionTo(location, function (route) {
1496
+ replaceState(cleanPath(this$1.base + route.fullPath))
1497
+ this$1.handleScroll(route, current, false)
1498
+ })
1499
+ };
1500
+
1501
+ HTML5History.prototype.ensureURL = function ensureURL () {
1502
+ if (getLocation(this.base) !== this.current.fullPath) {
1503
+ replaceState(cleanPath(this.base + this.current.fullPath))
1716
1504
  }
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
1505
  };
1729
1506
 
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);
1507
+ HTML5History.prototype.handleScroll = function handleScroll (to, from, isPop) {
1508
+ var router = this.router
1509
+ if (!router.app) {
1510
+ return
1511
+ }
1512
+
1513
+ var behavior = router.options.scrollBehavior
1514
+ if (!behavior) {
1515
+ return
1516
+ }
1517
+ assert(typeof behavior === 'function', "scrollBehavior must be a function")
1518
+
1519
+ // wait until re-render finishes before scrolling
1520
+ router.app.$nextTick(function () {
1521
+ var position = getScrollPosition(_key)
1522
+ var shouldScroll = behavior(to, from, isPop ? position : null)
1523
+ if (!shouldScroll) {
1524
+ return
1525
+ }
1526
+ var isObject = typeof shouldScroll === 'object'
1527
+ if (isObject && typeof shouldScroll.selector === 'string') {
1528
+ var el = document.querySelector(shouldScroll.selector)
1529
+ if (el) {
1530
+ position = getElementPosition(el)
1531
+ } else if (isValidPosition(shouldScroll)) {
1532
+ position = normalizePosition(shouldScroll)
1754
1533
  }
1534
+ } else if (isObject && isValidPosition(shouldScroll)) {
1535
+ position = normalizePosition(shouldScroll)
1755
1536
  }
1756
- init.call(this, options);
1757
- };
1758
1537
 
1759
- var destroy = Vue.prototype._destroy;
1760
- Vue.prototype._destroy = function () {
1761
- if (!this._isBeingDestroyed && this.$router) {
1762
- this.$router._children.$remove(this);
1538
+ if (position) {
1539
+ window.scrollTo(position.x, position.y)
1763
1540
  }
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
- };
1541
+ })
1542
+ };
1543
+
1544
+ return HTML5History;
1545
+ }(History));
1546
+
1547
+ function getLocation (base) {
1548
+ var path = window.location.pathname
1549
+ if (base && path.indexOf(base) === 0) {
1550
+ path = path.slice(base.length)
1551
+ }
1552
+ return (path || '/') + window.location.search + window.location.hash
1553
+ }
1554
+
1555
+ function pushState (url, replace) {
1556
+ // try...catch the pushState call to get around Safari
1557
+ // DOM Exception 18 where it limits to 100 pushState calls
1558
+ var history = window.history
1559
+ try {
1560
+ if (replace) {
1561
+ history.replaceState({ key: _key }, '', url)
1562
+ } else {
1563
+ _key = genKey()
1564
+ history.pushState({ key: _key }, '', url)
1790
1565
  }
1566
+ saveScrollPosition(_key)
1567
+ } catch (e) {
1568
+ window.location[replace ? 'assign' : 'replace'](url)
1791
1569
  }
1570
+ }
1792
1571
 
1793
- function View (Vue) {
1572
+ function replaceState (url) {
1573
+ pushState(url, true)
1574
+ }
1794
1575
 
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);
1576
+ /* */
1803
1577
 
1804
- // with some overrides
1805
- _.extend(viewDef, {
1806
1578
 
1807
- _isRouterView: true,
1579
+ var HashHistory = (function (History) {
1580
+ function HashHistory (router, base, fallback) {
1581
+ var this$1 = this;
1808
1582
 
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
- }
1583
+ History.call(this, router, base)
1844
1584
 
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
- },
1585
+ // check history fallback deeplinking
1586
+ if (fallback && this.checkFallback()) {
1587
+ return
1588
+ }
1857
1589
 
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
- });
1590
+ ensureSlash()
1591
+ this.transitionTo(getHash())
1915
1592
 
1916
- Vue.directive('link', {
1917
- priority: onPriority - 2,
1593
+ window.addEventListener('hashchange', function () {
1594
+ this$1.onHashChange()
1595
+ })
1596
+ }
1918
1597
 
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
- },
1598
+ if ( History ) HashHistory.__proto__ = History;
1599
+ HashHistory.prototype = Object.create( History && History.prototype );
1600
+ HashHistory.prototype.constructor = HashHistory;
1601
+
1602
+ HashHistory.prototype.checkFallback = function checkFallback () {
1603
+ var location = getLocation(this.base)
1604
+ if (!/^\/#/.test(location)) {
1605
+ window.location.replace(
1606
+ cleanPath(this.base + '/#' + location)
1607
+ )
1608
+ return true
1609
+ }
1610
+ };
2009
1611
 
2010
- updateActiveMatch: function updateActiveMatch() {
2011
- this.activeRE = this.path && !this.exact ? new RegExp('^' + this.path.replace(/\/$/, '').replace(queryStringRE, '').replace(regexEscapeRE, '\\$&') + '(\\/|$)') : null;
2012
- },
1612
+ HashHistory.prototype.onHashChange = function onHashChange () {
1613
+ if (!ensureSlash()) {
1614
+ return
1615
+ }
1616
+ this.transitionTo(getHash(), function (route) {
1617
+ replaceHash(route.fullPath)
1618
+ })
1619
+ };
2013
1620
 
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
- },
1621
+ HashHistory.prototype.push = function push (location) {
1622
+ History.prototype.transitionTo.call(this, location, function (route) {
1623
+ pushHash(route.fullPath)
1624
+ })
1625
+ };
2029
1626
 
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
- },
1627
+ HashHistory.prototype.replace = function replace (location) {
1628
+ History.prototype.transitionTo.call(this, location, function (route) {
1629
+ replaceHash(route.fullPath)
1630
+ })
1631
+ };
2056
1632
 
2057
- unbind: function unbind() {
2058
- this.el.removeEventListener('click', this.handler);
2059
- this.unwatch && this.unwatch();
2060
- }
2061
- });
1633
+ HashHistory.prototype.go = function go (n) {
1634
+ window.history.go(n)
1635
+ };
2062
1636
 
2063
- function sameOrigin(link) {
2064
- return link.protocol === location.protocol && link.hostname === location.hostname && link.port === location.port;
1637
+ HashHistory.prototype.ensureURL = function ensureURL () {
1638
+ if (getHash() !== this.current.fullPath) {
1639
+ replaceHash(this.current.fullPath)
2065
1640
  }
1641
+ };
2066
1642
 
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
- }
1643
+ return HashHistory;
1644
+ }(History));
1645
+
1646
+ function ensureSlash () {
1647
+ var path = getHash()
1648
+ if (path.charAt(0) === '/') {
1649
+ return true
1650
+ }
1651
+ replaceHash('/' + path)
1652
+ return false
1653
+ }
1654
+
1655
+ function getHash () {
1656
+ // We can't use window.location.hash here because it's not
1657
+ // consistent across browsers - Firefox will pre-decode it!
1658
+ var href = window.location.href
1659
+ var index = href.indexOf('#')
1660
+ return index === -1 ? '' : href.slice(index + 1)
1661
+ }
1662
+
1663
+ function pushHash (path) {
1664
+ window.location.hash = path
1665
+ }
1666
+
1667
+ function replaceHash (path) {
1668
+ var i = window.location.href.indexOf('#')
1669
+ window.location.replace(
1670
+ window.location.href.slice(0, i >= 0 ? i : 0) + '#' + path
1671
+ )
1672
+ }
1673
+
1674
+ /* */
1675
+
1676
+
1677
+ var AbstractHistory = (function (History) {
1678
+ function AbstractHistory (router) {
1679
+ History.call(this, router)
1680
+ this.stack = []
1681
+ this.index = 0
2080
1682
  }
2081
1683
 
2082
- var historyBackends = {
2083
- abstract: AbstractHistory,
2084
- hash: HashHistory,
2085
- html5: HTML5History
1684
+ if ( History ) AbstractHistory.__proto__ = History;
1685
+ AbstractHistory.prototype = Object.create( History && History.prototype );
1686
+ AbstractHistory.prototype.constructor = AbstractHistory;
1687
+
1688
+ AbstractHistory.prototype.push = function push (location) {
1689
+ var this$1 = this;
1690
+
1691
+ History.prototype.transitionTo.call(this, location, function (route) {
1692
+ this$1.stack = this$1.stack.slice(0, this$1.index + 1).concat(route)
1693
+ this$1.index++
1694
+ })
2086
1695
  };
2087
1696
 
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
- }
1697
+ AbstractHistory.prototype.replace = function replace (location) {
1698
+ var this$1 = this;
2125
1699
 
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
- });
1700
+ History.prototype.transitionTo.call(this, location, function (route) {
1701
+ this$1.stack = this$1.stack.slice(0, this$1.index).concat(route)
1702
+ })
1703
+ };
1704
+
1705
+ AbstractHistory.prototype.go = function go (n) {
1706
+ var this$1 = this;
2171
1707
 
2172
- // other options
2173
- this._saveScrollPosition = saveScrollPosition;
2174
- this._linkActiveClass = linkActiveClass;
2175
- this._suppress = suppressTransitionError;
1708
+ var targetIndex = this.index + n
1709
+ if (targetIndex < 0 || targetIndex >= this.stack.length) {
1710
+ return
2176
1711
  }
1712
+ var location = this.stack[targetIndex]
1713
+ this.confirmTransition(location, function () {
1714
+ this$1.index = targetIndex
1715
+ this$1.updateRoute(location)
1716
+ })
1717
+ };
2177
1718
 
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
- };
1719
+ AbstractHistory.prototype.ensureURL = function ensureURL () {
1720
+ // noop
1721
+ };
2293
1722
 
2294
- /**
2295
- * Short hand for replacing current path
2296
- *
2297
- * @param {String} path
2298
- */
1723
+ return AbstractHistory;
1724
+ }(History));
2299
1725
 
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
- }
1726
+ /* */
2338
1727
 
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
- }
1728
+ var VueRouter = function VueRouter (options) {
1729
+ if ( options === void 0 ) options = {};
2350
1730
 
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
- }
1731
+ this.app = null
1732
+ this.options = options
1733
+ this.beforeHooks = []
1734
+ this.afterHooks = []
1735
+ this.match = createMatcher(options.routes || [])
2528
1736
 
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
- }
1737
+ var mode = options.mode || 'hash'
1738
+ this.fallback = mode === 'history' && !supportsHistory
1739
+ if (this.fallback) {
1740
+ mode = 'hash'
1741
+ }
1742
+ if (!inBrowser) {
1743
+ mode = 'abstract'
1744
+ }
1745
+ this.mode = mode
1746
+ };
1747
+
1748
+ var prototypeAccessors = { currentRoute: {} };
1749
+
1750
+ prototypeAccessors.currentRoute.get = function () {
1751
+ return this.history && this.history.current
1752
+ };
1753
+
1754
+ VueRouter.prototype.init = function init (app /* Vue component instance */) {
1755
+ var this$1 = this;
1756
+
1757
+ assert(
1758
+ install.installed,
1759
+ "not installed. Make sure to call `Vue.use(VueRouter)` " +
1760
+ "before creating root instance."
1761
+ )
1762
+
1763
+ this.app = app
1764
+
1765
+ var ref = this;
1766
+ var mode = ref.mode;
1767
+ var options = ref.options;
1768
+ var fallback = ref.fallback;
1769
+ switch (mode) {
1770
+ case 'history':
1771
+ this.history = new HTML5History(this, options.base)
1772
+ break
1773
+ case 'hash':
1774
+ this.history = new HashHistory(this, options.base, fallback)
1775
+ break
1776
+ case 'abstract':
1777
+ this.history = new AbstractHistory(this)
1778
+ break
1779
+ default:
1780
+ assert(false, ("invalid mode: " + mode))
1781
+ }
2548
1782
 
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
- }
1783
+ this.history.listen(function (route) {
1784
+ this$1.app._route = route
1785
+ })
1786
+ };
2574
1787
 
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
- }
1788
+ VueRouter.prototype.beforeEach = function beforeEach (fn) {
1789
+ this.beforeHooks.push(fn)
1790
+ };
2594
1791
 
2595
- if (!this._rendered && this._startCb) {
2596
- this._startCb.call(null);
2597
- }
1792
+ VueRouter.prototype.afterEach = function afterEach (fn) {
1793
+ this.afterHooks.push(fn)
1794
+ };
2598
1795
 
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
- };
1796
+ VueRouter.prototype.push = function push (location) {
1797
+ this.history.push(location)
1798
+ };
2662
1799
 
2663
- return Router;
2664
- })();
1800
+ VueRouter.prototype.replace = function replace (location) {
1801
+ this.history.replace(location)
1802
+ };
2665
1803
 
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
- }
1804
+ VueRouter.prototype.go = function go (n) {
1805
+ this.history.go(n)
1806
+ };
2677
1807
 
2678
- /* Installation */
1808
+ VueRouter.prototype.back = function back () {
1809
+ this.go(-1)
1810
+ };
2679
1811
 
2680
- Router.installed = false;
1812
+ VueRouter.prototype.forward = function forward () {
1813
+ this.go(1)
1814
+ };
2681
1815
 
2682
- /**
2683
- * Installation interface.
2684
- * Install the necessary directives.
2685
- */
1816
+ VueRouter.prototype.getMatchedComponents = function getMatchedComponents () {
1817
+ if (!this.currentRoute) {
1818
+ return []
1819
+ }
1820
+ return [].concat.apply([], this.currentRoute.matched.map(function (m) {
1821
+ return Object.keys(m.components).map(function (key) {
1822
+ return m.components[key]
1823
+ })
1824
+ }))
1825
+ };
2686
1826
 
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
- };
1827
+ Object.defineProperties( VueRouter.prototype, prototypeAccessors );
2700
1828
 
2701
- // auto install
2702
- /* istanbul ignore if */
2703
- if (typeof window !== 'undefined' && window.Vue) {
2704
- window.Vue.use(Router);
2705
- }
1829
+ VueRouter.install = install
1830
+
1831
+ if (inBrowser && window.Vue) {
1832
+ window.Vue.use(VueRouter)
1833
+ }
2706
1834
 
2707
- return Router;
1835
+ return VueRouter;
2708
1836
 
2709
- }));
1837
+ })));