@maplat/ui 0.10.4 → 0.10.6

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.
Files changed (38) hide show
  1. package/LICENSE +223 -223
  2. package/README.md +91 -91
  3. package/dist/assets/locales/en/translation.json +64 -64
  4. package/dist/assets/locales/ja/translation.json +64 -64
  5. package/dist/assets/locales/ko/translation.json +65 -65
  6. package/dist/assets/locales/zh/translation.json +65 -65
  7. package/dist/assets/locales/zh-TW/translation.json +65 -65
  8. package/dist/assets/maplat.css +2 -2
  9. package/dist/assets/maplat.css.map +1 -1
  10. package/dist/assets/maplat.js +1 -1
  11. package/dist/assets/maplat.js.LICENSE.txt +1 -1
  12. package/dist/assets/maplat.js.map +1 -1
  13. package/dist/index.html +125 -37
  14. package/dist/service-worker.js +1 -1
  15. package/dist/service-worker.js.LICENSE.txt +1 -1
  16. package/dist/service-worker.js.map +1 -1
  17. package/legacy/bootstrap-native.js +1934 -1934
  18. package/legacy/detect-element-resize.js +153 -153
  19. package/legacy/iziToast.js +1301 -1301
  20. package/legacy/page.js +1153 -1153
  21. package/legacy/qrcode.js +616 -616
  22. package/less/bootstrap.less +7010 -7010
  23. package/less/font-awesome.less +30 -30
  24. package/less/font-face.less +10 -10
  25. package/less/font-face_packed.less +11 -11
  26. package/less/iziToast.less +1732 -1732
  27. package/less/maplat-specific.less +652 -652
  28. package/less/ui.less +11 -11
  29. package/less/ui_packed.less +10 -10
  30. package/locales/en/translation.json +64 -64
  31. package/locales/ja/translation.json +64 -64
  32. package/locales/ko/translation.json +65 -65
  33. package/locales/zh/translation.json +65 -65
  34. package/locales/zh-TW/translation.json +65 -65
  35. package/package.json +106 -105
  36. package/src/index.js +31 -7
  37. package/src/maplat_control.js +25 -7
  38. package/src/service-worker.js +1 -1
package/legacy/page.js CHANGED
@@ -1,1153 +1,1153 @@
1
- const Global = function() {};
2
-
3
- Global.prototype.dispatch = function() {
4
-
5
- (function (global, factory) {
6
- typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
7
- typeof define === 'function' && define.amd ? define(factory) :
8
- (global.page = factory());
9
- }(this, (function () { 'use strict';
10
-
11
- var isarray = Array.isArray || function (arr) {
12
- return Object.prototype.toString.call(arr) == '[object Array]';
13
- };
14
-
15
- /**
16
- * Expose `pathToRegexp`.
17
- */
18
- var pathToRegexp_1 = pathToRegexp;
19
- var parse_1 = parse;
20
- var compile_1 = compile;
21
- var tokensToFunction_1 = tokensToFunction;
22
- var tokensToRegExp_1 = tokensToRegExp;
23
-
24
- /**
25
- * The main path matching regexp utility.
26
- *
27
- * @type {RegExp}
28
- */
29
- var PATH_REGEXP = new RegExp([
30
- // Match escaped characters that would otherwise appear in future matches.
31
- // This allows the user to escape special characters that won't transform.
32
- '(\\\\.)',
33
- // Match Express-style parameters and un-named parameters with a prefix
34
- // and optional suffixes. Matches appear as:
35
- //
36
- // "/:test(\\d+)?" => ["/", "test", "\d+", undefined, "?", undefined]
37
- // "/route(\\d+)" => [undefined, undefined, undefined, "\d+", undefined, undefined]
38
- // "/*" => ["/", undefined, undefined, undefined, undefined, "*"]
39
- '([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^()])+)\\))?|\\(((?:\\\\.|[^()])+)\\))([+*?])?|(\\*))'
40
- ].join('|'), 'g');
41
-
42
- /**
43
- * Parse a string for the raw tokens.
44
- *
45
- * @param {String} str
46
- * @return {Array}
47
- */
48
- function parse (str) {
49
- var tokens = [];
50
- var key = 0;
51
- var index = 0;
52
- var path = '';
53
- var res;
54
-
55
- while ((res = PATH_REGEXP.exec(str)) != null) {
56
- var m = res[0];
57
- var escaped = res[1];
58
- var offset = res.index;
59
- path += str.slice(index, offset);
60
- index = offset + m.length;
61
-
62
- // Ignore already escaped sequences.
63
- if (escaped) {
64
- path += escaped[1];
65
- continue
66
- }
67
-
68
- // Push the current path onto the tokens.
69
- if (path) {
70
- tokens.push(path);
71
- path = '';
72
- }
73
-
74
- var prefix = res[2];
75
- var name = res[3];
76
- var capture = res[4];
77
- var group = res[5];
78
- var suffix = res[6];
79
- var asterisk = res[7];
80
-
81
- var repeat = suffix === '+' || suffix === '*';
82
- var optional = suffix === '?' || suffix === '*';
83
- var delimiter = prefix || '/';
84
- var pattern = capture || group || (asterisk ? '.*' : '[^' + delimiter + ']+?');
85
-
86
- tokens.push({
87
- name: name || key++,
88
- prefix: prefix || '',
89
- delimiter: delimiter,
90
- optional: optional,
91
- repeat: repeat,
92
- pattern: escapeGroup(pattern)
93
- });
94
- }
95
-
96
- // Match any characters still remaining.
97
- if (index < str.length) {
98
- path += str.substr(index);
99
- }
100
-
101
- // If the path exists, push it onto the end.
102
- if (path) {
103
- tokens.push(path);
104
- }
105
-
106
- return tokens
107
- }
108
-
109
- /**
110
- * Compile a string to a template function for the path.
111
- *
112
- * @param {String} str
113
- * @return {Function}
114
- */
115
- function compile (str) {
116
- return tokensToFunction(parse(str))
117
- }
118
-
119
- /**
120
- * Expose a method for transforming tokens into the path function.
121
- */
122
- function tokensToFunction (tokens) {
123
- // Compile all the tokens into regexps.
124
- var matches = new Array(tokens.length);
125
-
126
- // Compile all the patterns before compilation.
127
- for (var i = 0; i < tokens.length; i++) {
128
- if (typeof tokens[i] === 'object') {
129
- matches[i] = new RegExp('^' + tokens[i].pattern + '$');
130
- }
131
- }
132
-
133
- return function (obj) {
134
- var path = '';
135
- var data = obj || {};
136
-
137
- for (var i = 0; i < tokens.length; i++) {
138
- var token = tokens[i];
139
-
140
- if (typeof token === 'string') {
141
- path += token;
142
-
143
- continue
144
- }
145
-
146
- var value = data[token.name];
147
- var segment;
148
-
149
- if (value == null) {
150
- if (token.optional) {
151
- continue
152
- } else {
153
- throw new TypeError('Expected "' + token.name + '" to be defined')
154
- }
155
- }
156
-
157
- if (isarray(value)) {
158
- if (!token.repeat) {
159
- throw new TypeError('Expected "' + token.name + '" to not repeat, but received "' + value + '"')
160
- }
161
-
162
- if (value.length === 0) {
163
- if (token.optional) {
164
- continue
165
- } else {
166
- throw new TypeError('Expected "' + token.name + '" to not be empty')
167
- }
168
- }
169
-
170
- for (var j = 0; j < value.length; j++) {
171
- segment = encodeURIComponent(value[j]);
172
-
173
- if (!matches[i].test(segment)) {
174
- throw new TypeError('Expected all "' + token.name + '" to match "' + token.pattern + '", but received "' + segment + '"')
175
- }
176
-
177
- path += (j === 0 ? token.prefix : token.delimiter) + segment;
178
- }
179
-
180
- continue
181
- }
182
-
183
- segment = encodeURIComponent(value);
184
-
185
- if (!matches[i].test(segment)) {
186
- throw new TypeError('Expected "' + token.name + '" to match "' + token.pattern + '", but received "' + segment + '"')
187
- }
188
-
189
- path += token.prefix + segment;
190
- }
191
-
192
- return path
193
- }
194
- }
195
-
196
- /**
197
- * Escape a regular expression string.
198
- *
199
- * @param {String} str
200
- * @return {String}
201
- */
202
- function escapeString (str) {
203
- return str.replace(/([.+*?=^!:${}()[\]|\/])/g, '\\$1')
204
- }
205
-
206
- /**
207
- * Escape the capturing group by escaping special characters and meaning.
208
- *
209
- * @param {String} group
210
- * @return {String}
211
- */
212
- function escapeGroup (group) {
213
- return group.replace(/([=!:$\/()])/g, '\\$1')
214
- }
215
-
216
- /**
217
- * Attach the keys as a property of the regexp.
218
- *
219
- * @param {RegExp} re
220
- * @param {Array} keys
221
- * @return {RegExp}
222
- */
223
- function attachKeys (re, keys) {
224
- re.keys = keys;
225
- return re
226
- }
227
-
228
- /**
229
- * Get the flags for a regexp from the options.
230
- *
231
- * @param {Object} options
232
- * @return {String}
233
- */
234
- function flags (options) {
235
- return options.sensitive ? '' : 'i'
236
- }
237
-
238
- /**
239
- * Pull out keys from a regexp.
240
- *
241
- * @param {RegExp} path
242
- * @param {Array} keys
243
- * @return {RegExp}
244
- */
245
- function regexpToRegexp (path, keys) {
246
- // Use a negative lookahead to match only capturing groups.
247
- var groups = path.source.match(/\((?!\?)/g);
248
-
249
- if (groups) {
250
- for (var i = 0; i < groups.length; i++) {
251
- keys.push({
252
- name: i,
253
- prefix: null,
254
- delimiter: null,
255
- optional: false,
256
- repeat: false,
257
- pattern: null
258
- });
259
- }
260
- }
261
-
262
- return attachKeys(path, keys)
263
- }
264
-
265
- /**
266
- * Transform an array into a regexp.
267
- *
268
- * @param {Array} path
269
- * @param {Array} keys
270
- * @param {Object} options
271
- * @return {RegExp}
272
- */
273
- function arrayToRegexp (path, keys, options) {
274
- var parts = [];
275
-
276
- for (var i = 0; i < path.length; i++) {
277
- parts.push(pathToRegexp(path[i], keys, options).source);
278
- }
279
-
280
- var regexp = new RegExp('(?:' + parts.join('|') + ')', flags(options));
281
-
282
- return attachKeys(regexp, keys)
283
- }
284
-
285
- /**
286
- * Create a path regexp from string input.
287
- *
288
- * @param {String} path
289
- * @param {Array} keys
290
- * @param {Object} options
291
- * @return {RegExp}
292
- */
293
- function stringToRegexp (path, keys, options) {
294
- var tokens = parse(path);
295
- var re = tokensToRegExp(tokens, options);
296
-
297
- // Attach keys back to the regexp.
298
- for (var i = 0; i < tokens.length; i++) {
299
- if (typeof tokens[i] !== 'string') {
300
- keys.push(tokens[i]);
301
- }
302
- }
303
-
304
- return attachKeys(re, keys)
305
- }
306
-
307
- /**
308
- * Expose a function for taking tokens and returning a RegExp.
309
- *
310
- * @param {Array} tokens
311
- * @param {Array} keys
312
- * @param {Object} options
313
- * @return {RegExp}
314
- */
315
- function tokensToRegExp (tokens, options) {
316
- options = options || {};
317
-
318
- var strict = options.strict;
319
- var end = options.end !== false;
320
- var route = '';
321
- var lastToken = tokens[tokens.length - 1];
322
- var endsWithSlash = typeof lastToken === 'string' && /\/$/.test(lastToken);
323
-
324
- // Iterate over the tokens and create our regexp string.
325
- for (var i = 0; i < tokens.length; i++) {
326
- var token = tokens[i];
327
-
328
- if (typeof token === 'string') {
329
- route += escapeString(token);
330
- } else {
331
- var prefix = escapeString(token.prefix);
332
- var capture = token.pattern;
333
-
334
- if (token.repeat) {
335
- capture += '(?:' + prefix + capture + ')*';
336
- }
337
-
338
- if (token.optional) {
339
- if (prefix) {
340
- capture = '(?:' + prefix + '(' + capture + '))?';
341
- } else {
342
- capture = '(' + capture + ')?';
343
- }
344
- } else {
345
- capture = prefix + '(' + capture + ')';
346
- }
347
-
348
- route += capture;
349
- }
350
- }
351
-
352
- // In non-strict mode we allow a slash at the end of match. If the path to
353
- // match already ends with a slash, we remove it for consistency. The slash
354
- // is valid at the end of a path match, not in the middle. This is important
355
- // in non-ending mode, where "/test/" shouldn't match "/test//route".
356
- if (!strict) {
357
- route = (endsWithSlash ? route.slice(0, -2) : route) + '(?:\\/(?=$))?';
358
- }
359
-
360
- if (end) {
361
- route += '$';
362
- } else {
363
- // In non-ending mode, we need the capturing groups to match as much as
364
- // possible by using a positive lookahead to the end or next path segment.
365
- route += strict && endsWithSlash ? '' : '(?=\\/|$)';
366
- }
367
-
368
- return new RegExp('^' + route, flags(options))
369
- }
370
-
371
- /**
372
- * Normalize the given path string, returning a regular expression.
373
- *
374
- * An empty array can be passed in for the keys, which will hold the
375
- * placeholder key descriptions. For example, using `/user/:id`, `keys` will
376
- * contain `[{ name: 'id', delimiter: '/', optional: false, repeat: false }]`.
377
- *
378
- * @param {(String|RegExp|Array)} path
379
- * @param {Array} [keys]
380
- * @param {Object} [options]
381
- * @return {RegExp}
382
- */
383
- function pathToRegexp (path, keys, options) {
384
- keys = keys || [];
385
-
386
- if (!isarray(keys)) {
387
- options = keys;
388
- keys = [];
389
- } else if (!options) {
390
- options = {};
391
- }
392
-
393
- if (path instanceof RegExp) {
394
- return regexpToRegexp(path, keys, options)
395
- }
396
-
397
- if (isarray(path)) {
398
- return arrayToRegexp(path, keys, options)
399
- }
400
-
401
- return stringToRegexp(path, keys, options)
402
- }
403
-
404
- pathToRegexp_1.parse = parse_1;
405
- pathToRegexp_1.compile = compile_1;
406
- pathToRegexp_1.tokensToFunction = tokensToFunction_1;
407
- pathToRegexp_1.tokensToRegExp = tokensToRegExp_1;
408
-
409
- /**
410
- * Module dependencies.
411
- */
412
-
413
-
414
-
415
- /**
416
- * Module exports.
417
- */
418
-
419
- var page_js = page;
420
- page.default = page;
421
- page.Context = Context;
422
- page.Route = Route;
423
- page.sameOrigin = sameOrigin;
424
-
425
- /**
426
- * Short-cuts for global-object checks
427
- */
428
-
429
- var hasDocument = ('undefined' !== typeof document);
430
- var hasWindow = ('undefined' !== typeof window);
431
- var hasHistory = ('undefined' !== typeof history);
432
- var hasProcess = typeof process !== 'undefined';
433
-
434
- /**
435
- * Detect click event
436
- */
437
- var clickEvent = hasDocument && document.ontouchstart ? 'touchstart' : 'click';
438
-
439
- /**
440
- * To work properly with the URL
441
- * history.location generated polyfill in https://github.com/devote/HTML5-History-API
442
- */
443
-
444
- var isLocation = hasWindow && !!(window.history.location || window.location);
445
-
446
- /**
447
- * Perform initial dispatch.
448
- */
449
-
450
- var dispatch = true;
451
-
452
-
453
- /**
454
- * Decode URL components (query string, pathname, hash).
455
- * Accommodates both regular percent encoding and x-www-form-urlencoded format.
456
- */
457
- var decodeURLComponents = true;
458
-
459
- /**
460
- * Base path.
461
- */
462
-
463
- var base = '';
464
-
465
- /**
466
- * Strict path matching.
467
- */
468
-
469
- var strict = false;
470
-
471
- /**
472
- * Running flag.
473
- */
474
-
475
- var running;
476
-
477
- /**
478
- * HashBang option
479
- */
480
-
481
- var hashbang = false;
482
-
483
- /**
484
- * Previous context, for capturing
485
- * page exit events.
486
- */
487
-
488
- var prevContext;
489
-
490
- /**
491
- * The window for which this `page` is running
492
- */
493
- var pageWindow;
494
-
495
- /**
496
- * Register `path` with callback `fn()`,
497
- * or route `path`, or redirection,
498
- * or `page.start()`.
499
- *
500
- * page(fn);
501
- * page('*', fn);
502
- * page('/user/:id', load, user);
503
- * page('/user/' + user.id, { some: 'thing' });
504
- * page('/user/' + user.id);
505
- * page('/from', '/to')
506
- * page();
507
- *
508
- * @param {string|!Function|!Object} path
509
- * @param {Function=} fn
510
- * @api public
511
- */
512
-
513
- function page(path, fn) {
514
- // <callback>
515
- if ('function' === typeof path) {
516
- return page('*', path);
517
- }
518
-
519
- // route <path> to <callback ...>
520
- if ('function' === typeof fn) {
521
- var route = new Route(/** @type {string} */ (path));
522
- for (var i = 1; i < arguments.length; ++i) {
523
- page.callbacks.push(route.middleware(arguments[i]));
524
- }
525
- // show <path> with [state]
526
- } else if ('string' === typeof path) {
527
- page['string' === typeof fn ? 'redirect' : 'show'](path, fn);
528
- // start [options]
529
- } else {
530
- page.start(path);
531
- }
532
- }
533
-
534
- /**
535
- * Callback functions.
536
- */
537
-
538
- page.callbacks = [];
539
- page.exits = [];
540
-
541
- /**
542
- * Current path being processed
543
- * @type {string}
544
- */
545
- page.current = '';
546
-
547
- /**
548
- * Number of pages navigated to.
549
- * @type {number}
550
- *
551
- * page.len == 0;
552
- * page('/login');
553
- * page.len == 1;
554
- */
555
-
556
- page.len = 0;
557
-
558
- /**
559
- * Get or set basepath to `path`.
560
- *
561
- * @param {string} path
562
- * @api public
563
- */
564
-
565
- page.base = function(path) {
566
- if (0 === arguments.length) return base;
567
- base = path;
568
- };
569
-
570
- /**
571
- * Get or set strict path matching to `enable`
572
- *
573
- * @param {boolean} enable
574
- * @api public
575
- */
576
-
577
- page.strict = function(enable) {
578
- if (0 === arguments.length) return strict;
579
- strict = enable;
580
- };
581
-
582
- /**
583
- * Bind with the given `options`.
584
- *
585
- * Options:
586
- *
587
- * - `click` bind to click events [true]
588
- * - `popstate` bind to popstate [true]
589
- * - `dispatch` perform initial dispatch [true]
590
- *
591
- * @param {Object} options
592
- * @api public
593
- */
594
-
595
- page.start = function(options) {
596
- options = options || {};
597
- if (running) return;
598
- running = true;
599
- pageWindow = options.window || (hasWindow && window);
600
- if (false === options.dispatch) dispatch = false;
601
- if (false === options.decodeURLComponents) decodeURLComponents = false;
602
- if (false !== options.popstate && hasWindow) pageWindow.addEventListener('popstate', onpopstate, false);
603
- if (false !== options.click && hasDocument) {
604
- pageWindow.document.addEventListener(clickEvent, onclick, false);
605
- }
606
- hashbang = !!options.hashbang;
607
- if(hashbang && hasWindow && !hasHistory) {
608
- pageWindow.addEventListener('hashchange', onpopstate, false);
609
- }
610
- if (!dispatch) return;
611
-
612
- var url;
613
- if(isLocation) {
614
- var loc = pageWindow.location;
615
-
616
- if(hashbang && ~loc.hash.indexOf('#!')) {
617
- url = loc.hash.substr(2) + loc.search;
618
- } else if (hashbang) {
619
- url = loc.search + loc.hash;
620
- } else {
621
- url = loc.pathname + loc.search + loc.hash;
622
- }
623
- }
624
-
625
- page.replace(url, null, true, dispatch);
626
- };
627
-
628
- /**
629
- * Unbind click and popstate event handlers.
630
- *
631
- * @api public
632
- */
633
-
634
- page.stop = function() {
635
- if (!running) return;
636
- page.current = '';
637
- page.len = 0;
638
- running = false;
639
- hasDocument && pageWindow.document.removeEventListener(clickEvent, onclick, false);
640
- hasWindow && pageWindow.removeEventListener('popstate', onpopstate, false);
641
- hasWindow && pageWindow.removeEventListener('hashchange', onpopstate, false);
642
- };
643
-
644
- /**
645
- * Show `path` with optional `state` object.
646
- *
647
- * @param {string} path
648
- * @param {Object=} state
649
- * @param {boolean=} dispatch
650
- * @param {boolean=} push
651
- * @return {!Context}
652
- * @api public
653
- */
654
-
655
- page.show = function(path, state, dispatch, push) {
656
- var ctx = new Context(path, state),
657
- prev = prevContext;
658
- prevContext = ctx;
659
- page.current = ctx.path;
660
- if (false !== dispatch) page.dispatch(ctx, prev);
661
- if (false !== ctx.handled && false !== push) ctx.pushState();
662
- return ctx;
663
- };
664
-
665
- /**
666
- * Goes back in the history
667
- * Back should always let the current route push state and then go back.
668
- *
669
- * @param {string} path - fallback path to go back if no more history exists, if undefined defaults to page.base
670
- * @param {Object=} state
671
- * @api public
672
- */
673
-
674
- page.back = function(path, state) {
675
- if (page.len > 0) {
676
- // this may need more testing to see if all browsers
677
- // wait for the next tick to go back in history
678
- hasHistory && pageWindow.history.back();
679
- page.len--;
680
- } else if (path) {
681
- setTimeout(function() {
682
- page.show(path, state);
683
- });
684
- }else{
685
- setTimeout(function() {
686
- page.show(getBase(), state);
687
- });
688
- }
689
- };
690
-
691
-
692
- /**
693
- * Register route to redirect from one path to other
694
- * or just redirect to another route
695
- *
696
- * @param {string} from - if param 'to' is undefined redirects to 'from'
697
- * @param {string=} to
698
- * @api public
699
- */
700
- page.redirect = function(from, to) {
701
- // Define route from a path to another
702
- if ('string' === typeof from && 'string' === typeof to) {
703
- page(from, function(e) {
704
- setTimeout(function() {
705
- page.replace(/** @type {!string} */ (to));
706
- }, 0);
707
- });
708
- }
709
-
710
- // Wait for the push state and replace it with another
711
- if ('string' === typeof from && 'undefined' === typeof to) {
712
- setTimeout(function() {
713
- page.replace(from);
714
- }, 0);
715
- }
716
- };
717
-
718
- /**
719
- * Replace `path` with optional `state` object.
720
- *
721
- * @param {string} path
722
- * @param {Object=} state
723
- * @param {boolean=} init
724
- * @param {boolean=} dispatch
725
- * @return {!Context}
726
- * @api public
727
- */
728
-
729
-
730
- page.replace = function(path, state, init, dispatch) {
731
- var ctx = new Context(path, state),
732
- prev = prevContext;
733
- prevContext = ctx;
734
- page.current = ctx.path;
735
- ctx.init = init;
736
- ctx.save(); // save before dispatching, which may redirect
737
- if (false !== dispatch) page.dispatch(ctx, prev);
738
- return ctx;
739
- };
740
-
741
- /**
742
- * Dispatch the given `ctx`.
743
- *
744
- * @param {Context} ctx
745
- * @api private
746
- */
747
-
748
- page.dispatch = function(ctx, prev) {
749
- var i = 0,
750
- j = 0;
751
-
752
- function nextExit() {
753
- var fn = page.exits[j++];
754
- if (!fn) return nextEnter();
755
- fn(prev, nextExit);
756
- }
757
-
758
- function nextEnter() {
759
- var fn = page.callbacks[i++];
760
-
761
- if (ctx.path !== page.current) {
762
- ctx.handled = false;
763
- return;
764
- }
765
- if (!fn) return unhandled(ctx);
766
- fn(ctx, nextEnter);
767
- }
768
-
769
- if (prev) {
770
- nextExit();
771
- } else {
772
- nextEnter();
773
- }
774
- };
775
-
776
- /**
777
- * Unhandled `ctx`. When it's not the initial
778
- * popstate then redirect. If you wish to handle
779
- * 404s on your own use `page('*', callback)`.
780
- *
781
- * @param {Context} ctx
782
- * @api private
783
- */
784
- function unhandled(ctx) {
785
- if (ctx.handled) return;
786
- var current;
787
-
788
- if (hashbang) {
789
- current = isLocation && getBase() + pageWindow.location.hash.replace('#!', '');
790
- } else {
791
- current = isLocation && pageWindow.location.pathname + pageWindow.location.search;
792
- }
793
-
794
- if (current === ctx.canonicalPath) return;
795
- page.stop();
796
- ctx.handled = false;
797
- isLocation && (pageWindow.location.href = ctx.canonicalPath);
798
- }
799
-
800
- /**
801
- * Register an exit route on `path` with
802
- * callback `fn()`, which will be called
803
- * on the previous context when a new
804
- * page is visited.
805
- */
806
- page.exit = function(path, fn) {
807
- if (typeof path === 'function') {
808
- return page.exit('*', path);
809
- }
810
-
811
- var route = new Route(path);
812
- for (var i = 1; i < arguments.length; ++i) {
813
- page.exits.push(route.middleware(arguments[i]));
814
- }
815
- };
816
-
817
- /**
818
- * Remove URL encoding from the given `str`.
819
- * Accommodates whitespace in both x-www-form-urlencoded
820
- * and regular percent-encoded form.
821
- *
822
- * @param {string} val - URL component to decode
823
- */
824
- function decodeURLEncodedURIComponent(val) {
825
- if (typeof val !== 'string') { return val; }
826
- return decodeURLComponents ? decodeURIComponent(val.replace(/\+/g, ' ')) : val;
827
- }
828
-
829
- /**
830
- * Initialize a new "request" `Context`
831
- * with the given `path` and optional initial `state`.
832
- *
833
- * @constructor
834
- * @param {string} path
835
- * @param {Object=} state
836
- * @api public
837
- */
838
-
839
- function Context(path, state) {
840
- var pageBase = getBase();
841
- if ('/' === path[0] && 0 !== path.indexOf(pageBase)) path = pageBase + (hashbang ? '#!' : '') + path;
842
- var i = path.indexOf('?');
843
-
844
- this.canonicalPath = path;
845
- this.path = path.replace(pageBase, '') || '/';
846
- if (hashbang) this.path = this.path.replace('#!', '') || '/';
847
-
848
- this.title = (hasDocument && pageWindow.document.title);
849
- this.state = state || {};
850
- this.state.path = path;
851
- this.querystring = ~i ? decodeURLEncodedURIComponent(path.slice(i + 1)) : '';
852
- this.pathname = decodeURLEncodedURIComponent(~i ? path.slice(0, i) : path);
853
- this.params = {};
854
-
855
- // fragment
856
- this.hash = '';
857
- if (!hashbang) {
858
- if (!~this.path.indexOf('#')) return;
859
- var parts = this.path.split('#');
860
- this.path = this.pathname = parts[0];
861
- this.hash = decodeURLEncodedURIComponent(parts[1]) || '';
862
- this.querystring = this.querystring.split('#')[0];
863
- }
864
- }
865
-
866
- /**
867
- * Expose `Context`.
868
- */
869
-
870
- page.Context = Context;
871
-
872
- /**
873
- * Push state.
874
- *
875
- * @api private
876
- */
877
-
878
- Context.prototype.pushState = function() {
879
- page.len++;
880
- if (hasHistory) {
881
- pageWindow.history.pushState(this.state, this.title,
882
- hashbang && this.path !== '/' ? '#!' + this.path : this.canonicalPath);
883
- }
884
- };
885
-
886
- /**
887
- * Save the context state.
888
- *
889
- * @api public
890
- */
891
-
892
- Context.prototype.save = function() {
893
- if (hasHistory && pageWindow.location.protocol !== 'file:') {
894
- pageWindow.history.replaceState(this.state, this.title,
895
- hashbang && this.path !== '/' ? '#!' + this.path : this.canonicalPath);
896
- }
897
- };
898
-
899
- /**
900
- * Initialize `Route` with the given HTTP `path`,
901
- * and an array of `callbacks` and `options`.
902
- *
903
- * Options:
904
- *
905
- * - `sensitive` enable case-sensitive routes
906
- * - `strict` enable strict matching for trailing slashes
907
- *
908
- * @constructor
909
- * @param {string} path
910
- * @param {Object=} options
911
- * @api private
912
- */
913
-
914
- function Route(path, options) {
915
- options = options || {};
916
- options.strict = options.strict || strict;
917
- this.path = (path === '*') ? '(.*)' : path;
918
- this.method = 'GET';
919
- this.regexp = pathToRegexp_1(this.path,
920
- this.keys = [],
921
- options);
922
- }
923
-
924
- /**
925
- * Expose `Route`.
926
- */
927
-
928
- page.Route = Route;
929
-
930
- /**
931
- * Return route middleware with
932
- * the given callback `fn()`.
933
- *
934
- * @param {Function} fn
935
- * @return {Function}
936
- * @api public
937
- */
938
-
939
- Route.prototype.middleware = function(fn) {
940
- var self = this;
941
- return function(ctx, next) {
942
- if (self.match(ctx.path, ctx.params)) return fn(ctx, next);
943
- next();
944
- };
945
- };
946
-
947
- /**
948
- * Check if this route matches `path`, if so
949
- * populate `params`.
950
- *
951
- * @param {string} path
952
- * @param {Object} params
953
- * @return {boolean}
954
- * @api private
955
- */
956
-
957
- Route.prototype.match = function(path, params) {
958
- var keys = this.keys,
959
- qsIndex = path.indexOf('?'),
960
- pathname = ~qsIndex ? path.slice(0, qsIndex) : path,
961
- m = this.regexp.exec(decodeURIComponent(pathname));
962
-
963
- if (!m) return false;
964
-
965
- for (var i = 1, len = m.length; i < len; ++i) {
966
- var key = keys[i - 1];
967
- var val = decodeURLEncodedURIComponent(m[i]);
968
- if (val !== undefined || !(hasOwnProperty.call(params, key.name))) {
969
- params[key.name] = val;
970
- }
971
- }
972
-
973
- return true;
974
- };
975
-
976
-
977
- /**
978
- * Handle "populate" events.
979
- */
980
-
981
- var onpopstate = (function () {
982
- var loaded = false;
983
- if ( ! hasWindow ) {
984
- return;
985
- }
986
- if (hasDocument && document.readyState === 'complete') {
987
- loaded = true;
988
- } else {
989
- window.addEventListener('load', function() {
990
- setTimeout(function() {
991
- loaded = true;
992
- }, 0);
993
- });
994
- }
995
- return function onpopstate(e) {
996
- if (!loaded) return;
997
- if (e.state) {
998
- var path = e.state.path;
999
- page.replace(path, e.state);
1000
- } else if (isLocation) {
1001
- var loc = pageWindow.location;
1002
- page.show(loc.pathname + loc.hash, undefined, undefined, false);
1003
- }
1004
- };
1005
- })();
1006
- /**
1007
- * Handle "click" events.
1008
- */
1009
-
1010
- /* jshint +W054 */
1011
- function onclick(e) {
1012
- if (1 !== which(e)) return;
1013
-
1014
- if (e.metaKey || e.ctrlKey || e.shiftKey) return;
1015
- if (e.defaultPrevented) return;
1016
-
1017
- // ensure link
1018
- // use shadow dom when available if not, fall back to composedPath() for browsers that only have shady
1019
- var el = e.target;
1020
- var eventPath = e.path || (e.composedPath ? e.composedPath() : null);
1021
-
1022
- if(eventPath) {
1023
- for (var i = 0; i < eventPath.length; i++) {
1024
- if (!eventPath[i].nodeName) continue;
1025
- if (eventPath[i].nodeName.toUpperCase() !== 'A') continue;
1026
- if (!eventPath[i].href) continue;
1027
-
1028
- el = eventPath[i];
1029
- break;
1030
- }
1031
- }
1032
- // continue ensure link
1033
- // el.nodeName for svg links are 'a' instead of 'A'
1034
- while (el && 'A' !== el.nodeName.toUpperCase()) el = el.parentNode;
1035
- if (!el || 'A' !== el.nodeName.toUpperCase()) return;
1036
-
1037
- // check if link is inside an svg
1038
- // in this case, both href and target are always inside an object
1039
- var svg = (typeof el.href === 'object') && el.href.constructor.name === 'SVGAnimatedString';
1040
-
1041
- // Ignore if tag has
1042
- // 1. "download" attribute
1043
- // 2. rel="external" attribute
1044
- if (el.hasAttribute('download') || el.getAttribute('rel') === 'external') return;
1045
-
1046
- // ensure non-hash for the same path
1047
- var link = el.getAttribute('href');
1048
- if(!hashbang && samePath(el) && (el.hash || '#' === link)) return;
1049
-
1050
- // Check for mailto: in the href
1051
- if (link && link.indexOf('mailto:') > -1) return;
1052
-
1053
- // check target
1054
- // svg target is an object and its desired value is in .baseVal property
1055
- if (svg ? el.target.baseVal : el.target) return;
1056
-
1057
- // x-origin
1058
- // note: svg links that are not relative don't call click events (and skip page.js)
1059
- // consequently, all svg links tested inside page.js are relative and in the same origin
1060
- if (!svg && !sameOrigin(el.href)) return;
1061
-
1062
- // rebuild path
1063
- // There aren't .pathname and .search properties in svg links, so we use href
1064
- // Also, svg href is an object and its desired value is in .baseVal property
1065
- var path = svg ? el.href.baseVal : (el.pathname + el.search + (el.hash || ''));
1066
-
1067
- path = path[0] !== '/' ? '/' + path : path;
1068
-
1069
- // strip leading "/[drive letter]:" on NW.js on Windows
1070
- if (hasProcess && path.match(/^\/[a-zA-Z]:\//)) {
1071
- path = path.replace(/^\/[a-zA-Z]:\//, '/');
1072
- }
1073
-
1074
- // same page
1075
- var orig = path;
1076
- var pageBase = getBase();
1077
-
1078
- if (path.indexOf(pageBase) === 0) {
1079
- path = path.substr(base.length);
1080
- }
1081
-
1082
- if (hashbang) path = path.replace('#!', '');
1083
-
1084
- if (pageBase && orig === path) return;
1085
-
1086
- e.preventDefault();
1087
- page.show(orig);
1088
- }
1089
-
1090
- /**
1091
- * Event button.
1092
- */
1093
-
1094
- function which(e) {
1095
- e = e || (hasWindow && window.event);
1096
- return null == e.which ? e.button : e.which;
1097
- }
1098
-
1099
- /**
1100
- * Convert to a URL object
1101
- */
1102
- function toURL(href) {
1103
- if(typeof URL === 'function' && isLocation) {
1104
- return new URL(href, location.toString());
1105
- } else if (hasDocument) {
1106
- var anc = document.createElement('a');
1107
- anc.href = href;
1108
- return anc;
1109
- }
1110
- }
1111
-
1112
- /**
1113
- * Check if `href` is the same origin.
1114
- */
1115
-
1116
- function sameOrigin(href) {
1117
- if(!href || !isLocation) return false;
1118
- var url = toURL(href);
1119
-
1120
- var loc = pageWindow.location;
1121
- return loc.protocol === url.protocol &&
1122
- loc.hostname === url.hostname &&
1123
- loc.port === url.port;
1124
- }
1125
-
1126
- function samePath(url) {
1127
- if(!isLocation) return false;
1128
- var loc = pageWindow.location;
1129
- return url.pathname === loc.pathname &&
1130
- url.search === loc.search;
1131
- }
1132
-
1133
- /**
1134
- * Gets the `base`, which depends on whether we are using History or
1135
- * hashbang routing.
1136
- */
1137
- function getBase() {
1138
- if(!!base) return base;
1139
- var loc = hasWindow && pageWindow && pageWindow.location;
1140
- return (hasWindow && hashbang && loc && loc.protocol === 'file:') ? loc.pathname : base;
1141
- }
1142
-
1143
- page.sameOrigin = sameOrigin;
1144
-
1145
- return page_js;
1146
-
1147
- })));
1148
- return this;
1149
- };
1150
-
1151
- const global = new Global().dispatch();
1152
-
1153
- export default global.page;
1
+ const Global = function() {};
2
+
3
+ Global.prototype.dispatch = function() {
4
+
5
+ (function (global, factory) {
6
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
7
+ typeof define === 'function' && define.amd ? define(factory) :
8
+ (global.page = factory());
9
+ }(this, (function () { 'use strict';
10
+
11
+ var isarray = Array.isArray || function (arr) {
12
+ return Object.prototype.toString.call(arr) == '[object Array]';
13
+ };
14
+
15
+ /**
16
+ * Expose `pathToRegexp`.
17
+ */
18
+ var pathToRegexp_1 = pathToRegexp;
19
+ var parse_1 = parse;
20
+ var compile_1 = compile;
21
+ var tokensToFunction_1 = tokensToFunction;
22
+ var tokensToRegExp_1 = tokensToRegExp;
23
+
24
+ /**
25
+ * The main path matching regexp utility.
26
+ *
27
+ * @type {RegExp}
28
+ */
29
+ var PATH_REGEXP = new RegExp([
30
+ // Match escaped characters that would otherwise appear in future matches.
31
+ // This allows the user to escape special characters that won't transform.
32
+ '(\\\\.)',
33
+ // Match Express-style parameters and un-named parameters with a prefix
34
+ // and optional suffixes. Matches appear as:
35
+ //
36
+ // "/:test(\\d+)?" => ["/", "test", "\d+", undefined, "?", undefined]
37
+ // "/route(\\d+)" => [undefined, undefined, undefined, "\d+", undefined, undefined]
38
+ // "/*" => ["/", undefined, undefined, undefined, undefined, "*"]
39
+ '([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^()])+)\\))?|\\(((?:\\\\.|[^()])+)\\))([+*?])?|(\\*))'
40
+ ].join('|'), 'g');
41
+
42
+ /**
43
+ * Parse a string for the raw tokens.
44
+ *
45
+ * @param {String} str
46
+ * @return {Array}
47
+ */
48
+ function parse (str) {
49
+ var tokens = [];
50
+ var key = 0;
51
+ var index = 0;
52
+ var path = '';
53
+ var res;
54
+
55
+ while ((res = PATH_REGEXP.exec(str)) != null) {
56
+ var m = res[0];
57
+ var escaped = res[1];
58
+ var offset = res.index;
59
+ path += str.slice(index, offset);
60
+ index = offset + m.length;
61
+
62
+ // Ignore already escaped sequences.
63
+ if (escaped) {
64
+ path += escaped[1];
65
+ continue
66
+ }
67
+
68
+ // Push the current path onto the tokens.
69
+ if (path) {
70
+ tokens.push(path);
71
+ path = '';
72
+ }
73
+
74
+ var prefix = res[2];
75
+ var name = res[3];
76
+ var capture = res[4];
77
+ var group = res[5];
78
+ var suffix = res[6];
79
+ var asterisk = res[7];
80
+
81
+ var repeat = suffix === '+' || suffix === '*';
82
+ var optional = suffix === '?' || suffix === '*';
83
+ var delimiter = prefix || '/';
84
+ var pattern = capture || group || (asterisk ? '.*' : '[^' + delimiter + ']+?');
85
+
86
+ tokens.push({
87
+ name: name || key++,
88
+ prefix: prefix || '',
89
+ delimiter: delimiter,
90
+ optional: optional,
91
+ repeat: repeat,
92
+ pattern: escapeGroup(pattern)
93
+ });
94
+ }
95
+
96
+ // Match any characters still remaining.
97
+ if (index < str.length) {
98
+ path += str.substr(index);
99
+ }
100
+
101
+ // If the path exists, push it onto the end.
102
+ if (path) {
103
+ tokens.push(path);
104
+ }
105
+
106
+ return tokens
107
+ }
108
+
109
+ /**
110
+ * Compile a string to a template function for the path.
111
+ *
112
+ * @param {String} str
113
+ * @return {Function}
114
+ */
115
+ function compile (str) {
116
+ return tokensToFunction(parse(str))
117
+ }
118
+
119
+ /**
120
+ * Expose a method for transforming tokens into the path function.
121
+ */
122
+ function tokensToFunction (tokens) {
123
+ // Compile all the tokens into regexps.
124
+ var matches = new Array(tokens.length);
125
+
126
+ // Compile all the patterns before compilation.
127
+ for (var i = 0; i < tokens.length; i++) {
128
+ if (typeof tokens[i] === 'object') {
129
+ matches[i] = new RegExp('^' + tokens[i].pattern + '$');
130
+ }
131
+ }
132
+
133
+ return function (obj) {
134
+ var path = '';
135
+ var data = obj || {};
136
+
137
+ for (var i = 0; i < tokens.length; i++) {
138
+ var token = tokens[i];
139
+
140
+ if (typeof token === 'string') {
141
+ path += token;
142
+
143
+ continue
144
+ }
145
+
146
+ var value = data[token.name];
147
+ var segment;
148
+
149
+ if (value == null) {
150
+ if (token.optional) {
151
+ continue
152
+ } else {
153
+ throw new TypeError('Expected "' + token.name + '" to be defined')
154
+ }
155
+ }
156
+
157
+ if (isarray(value)) {
158
+ if (!token.repeat) {
159
+ throw new TypeError('Expected "' + token.name + '" to not repeat, but received "' + value + '"')
160
+ }
161
+
162
+ if (value.length === 0) {
163
+ if (token.optional) {
164
+ continue
165
+ } else {
166
+ throw new TypeError('Expected "' + token.name + '" to not be empty')
167
+ }
168
+ }
169
+
170
+ for (var j = 0; j < value.length; j++) {
171
+ segment = encodeURIComponent(value[j]);
172
+
173
+ if (!matches[i].test(segment)) {
174
+ throw new TypeError('Expected all "' + token.name + '" to match "' + token.pattern + '", but received "' + segment + '"')
175
+ }
176
+
177
+ path += (j === 0 ? token.prefix : token.delimiter) + segment;
178
+ }
179
+
180
+ continue
181
+ }
182
+
183
+ segment = encodeURIComponent(value);
184
+
185
+ if (!matches[i].test(segment)) {
186
+ throw new TypeError('Expected "' + token.name + '" to match "' + token.pattern + '", but received "' + segment + '"')
187
+ }
188
+
189
+ path += token.prefix + segment;
190
+ }
191
+
192
+ return path
193
+ }
194
+ }
195
+
196
+ /**
197
+ * Escape a regular expression string.
198
+ *
199
+ * @param {String} str
200
+ * @return {String}
201
+ */
202
+ function escapeString (str) {
203
+ return str.replace(/([.+*?=^!:${}()[\]|\/])/g, '\\$1')
204
+ }
205
+
206
+ /**
207
+ * Escape the capturing group by escaping special characters and meaning.
208
+ *
209
+ * @param {String} group
210
+ * @return {String}
211
+ */
212
+ function escapeGroup (group) {
213
+ return group.replace(/([=!:$\/()])/g, '\\$1')
214
+ }
215
+
216
+ /**
217
+ * Attach the keys as a property of the regexp.
218
+ *
219
+ * @param {RegExp} re
220
+ * @param {Array} keys
221
+ * @return {RegExp}
222
+ */
223
+ function attachKeys (re, keys) {
224
+ re.keys = keys;
225
+ return re
226
+ }
227
+
228
+ /**
229
+ * Get the flags for a regexp from the options.
230
+ *
231
+ * @param {Object} options
232
+ * @return {String}
233
+ */
234
+ function flags (options) {
235
+ return options.sensitive ? '' : 'i'
236
+ }
237
+
238
+ /**
239
+ * Pull out keys from a regexp.
240
+ *
241
+ * @param {RegExp} path
242
+ * @param {Array} keys
243
+ * @return {RegExp}
244
+ */
245
+ function regexpToRegexp (path, keys) {
246
+ // Use a negative lookahead to match only capturing groups.
247
+ var groups = path.source.match(/\((?!\?)/g);
248
+
249
+ if (groups) {
250
+ for (var i = 0; i < groups.length; i++) {
251
+ keys.push({
252
+ name: i,
253
+ prefix: null,
254
+ delimiter: null,
255
+ optional: false,
256
+ repeat: false,
257
+ pattern: null
258
+ });
259
+ }
260
+ }
261
+
262
+ return attachKeys(path, keys)
263
+ }
264
+
265
+ /**
266
+ * Transform an array into a regexp.
267
+ *
268
+ * @param {Array} path
269
+ * @param {Array} keys
270
+ * @param {Object} options
271
+ * @return {RegExp}
272
+ */
273
+ function arrayToRegexp (path, keys, options) {
274
+ var parts = [];
275
+
276
+ for (var i = 0; i < path.length; i++) {
277
+ parts.push(pathToRegexp(path[i], keys, options).source);
278
+ }
279
+
280
+ var regexp = new RegExp('(?:' + parts.join('|') + ')', flags(options));
281
+
282
+ return attachKeys(regexp, keys)
283
+ }
284
+
285
+ /**
286
+ * Create a path regexp from string input.
287
+ *
288
+ * @param {String} path
289
+ * @param {Array} keys
290
+ * @param {Object} options
291
+ * @return {RegExp}
292
+ */
293
+ function stringToRegexp (path, keys, options) {
294
+ var tokens = parse(path);
295
+ var re = tokensToRegExp(tokens, options);
296
+
297
+ // Attach keys back to the regexp.
298
+ for (var i = 0; i < tokens.length; i++) {
299
+ if (typeof tokens[i] !== 'string') {
300
+ keys.push(tokens[i]);
301
+ }
302
+ }
303
+
304
+ return attachKeys(re, keys)
305
+ }
306
+
307
+ /**
308
+ * Expose a function for taking tokens and returning a RegExp.
309
+ *
310
+ * @param {Array} tokens
311
+ * @param {Array} keys
312
+ * @param {Object} options
313
+ * @return {RegExp}
314
+ */
315
+ function tokensToRegExp (tokens, options) {
316
+ options = options || {};
317
+
318
+ var strict = options.strict;
319
+ var end = options.end !== false;
320
+ var route = '';
321
+ var lastToken = tokens[tokens.length - 1];
322
+ var endsWithSlash = typeof lastToken === 'string' && /\/$/.test(lastToken);
323
+
324
+ // Iterate over the tokens and create our regexp string.
325
+ for (var i = 0; i < tokens.length; i++) {
326
+ var token = tokens[i];
327
+
328
+ if (typeof token === 'string') {
329
+ route += escapeString(token);
330
+ } else {
331
+ var prefix = escapeString(token.prefix);
332
+ var capture = token.pattern;
333
+
334
+ if (token.repeat) {
335
+ capture += '(?:' + prefix + capture + ')*';
336
+ }
337
+
338
+ if (token.optional) {
339
+ if (prefix) {
340
+ capture = '(?:' + prefix + '(' + capture + '))?';
341
+ } else {
342
+ capture = '(' + capture + ')?';
343
+ }
344
+ } else {
345
+ capture = prefix + '(' + capture + ')';
346
+ }
347
+
348
+ route += capture;
349
+ }
350
+ }
351
+
352
+ // In non-strict mode we allow a slash at the end of match. If the path to
353
+ // match already ends with a slash, we remove it for consistency. The slash
354
+ // is valid at the end of a path match, not in the middle. This is important
355
+ // in non-ending mode, where "/test/" shouldn't match "/test//route".
356
+ if (!strict) {
357
+ route = (endsWithSlash ? route.slice(0, -2) : route) + '(?:\\/(?=$))?';
358
+ }
359
+
360
+ if (end) {
361
+ route += '$';
362
+ } else {
363
+ // In non-ending mode, we need the capturing groups to match as much as
364
+ // possible by using a positive lookahead to the end or next path segment.
365
+ route += strict && endsWithSlash ? '' : '(?=\\/|$)';
366
+ }
367
+
368
+ return new RegExp('^' + route, flags(options))
369
+ }
370
+
371
+ /**
372
+ * Normalize the given path string, returning a regular expression.
373
+ *
374
+ * An empty array can be passed in for the keys, which will hold the
375
+ * placeholder key descriptions. For example, using `/user/:id`, `keys` will
376
+ * contain `[{ name: 'id', delimiter: '/', optional: false, repeat: false }]`.
377
+ *
378
+ * @param {(String|RegExp|Array)} path
379
+ * @param {Array} [keys]
380
+ * @param {Object} [options]
381
+ * @return {RegExp}
382
+ */
383
+ function pathToRegexp (path, keys, options) {
384
+ keys = keys || [];
385
+
386
+ if (!isarray(keys)) {
387
+ options = keys;
388
+ keys = [];
389
+ } else if (!options) {
390
+ options = {};
391
+ }
392
+
393
+ if (path instanceof RegExp) {
394
+ return regexpToRegexp(path, keys, options)
395
+ }
396
+
397
+ if (isarray(path)) {
398
+ return arrayToRegexp(path, keys, options)
399
+ }
400
+
401
+ return stringToRegexp(path, keys, options)
402
+ }
403
+
404
+ pathToRegexp_1.parse = parse_1;
405
+ pathToRegexp_1.compile = compile_1;
406
+ pathToRegexp_1.tokensToFunction = tokensToFunction_1;
407
+ pathToRegexp_1.tokensToRegExp = tokensToRegExp_1;
408
+
409
+ /**
410
+ * Module dependencies.
411
+ */
412
+
413
+
414
+
415
+ /**
416
+ * Module exports.
417
+ */
418
+
419
+ var page_js = page;
420
+ page.default = page;
421
+ page.Context = Context;
422
+ page.Route = Route;
423
+ page.sameOrigin = sameOrigin;
424
+
425
+ /**
426
+ * Short-cuts for global-object checks
427
+ */
428
+
429
+ var hasDocument = ('undefined' !== typeof document);
430
+ var hasWindow = ('undefined' !== typeof window);
431
+ var hasHistory = ('undefined' !== typeof history);
432
+ var hasProcess = typeof process !== 'undefined';
433
+
434
+ /**
435
+ * Detect click event
436
+ */
437
+ var clickEvent = hasDocument && document.ontouchstart ? 'touchstart' : 'click';
438
+
439
+ /**
440
+ * To work properly with the URL
441
+ * history.location generated polyfill in https://github.com/devote/HTML5-History-API
442
+ */
443
+
444
+ var isLocation = hasWindow && !!(window.history.location || window.location);
445
+
446
+ /**
447
+ * Perform initial dispatch.
448
+ */
449
+
450
+ var dispatch = true;
451
+
452
+
453
+ /**
454
+ * Decode URL components (query string, pathname, hash).
455
+ * Accommodates both regular percent encoding and x-www-form-urlencoded format.
456
+ */
457
+ var decodeURLComponents = true;
458
+
459
+ /**
460
+ * Base path.
461
+ */
462
+
463
+ var base = '';
464
+
465
+ /**
466
+ * Strict path matching.
467
+ */
468
+
469
+ var strict = false;
470
+
471
+ /**
472
+ * Running flag.
473
+ */
474
+
475
+ var running;
476
+
477
+ /**
478
+ * HashBang option
479
+ */
480
+
481
+ var hashbang = false;
482
+
483
+ /**
484
+ * Previous context, for capturing
485
+ * page exit events.
486
+ */
487
+
488
+ var prevContext;
489
+
490
+ /**
491
+ * The window for which this `page` is running
492
+ */
493
+ var pageWindow;
494
+
495
+ /**
496
+ * Register `path` with callback `fn()`,
497
+ * or route `path`, or redirection,
498
+ * or `page.start()`.
499
+ *
500
+ * page(fn);
501
+ * page('*', fn);
502
+ * page('/user/:id', load, user);
503
+ * page('/user/' + user.id, { some: 'thing' });
504
+ * page('/user/' + user.id);
505
+ * page('/from', '/to')
506
+ * page();
507
+ *
508
+ * @param {string|!Function|!Object} path
509
+ * @param {Function=} fn
510
+ * @api public
511
+ */
512
+
513
+ function page(path, fn) {
514
+ // <callback>
515
+ if ('function' === typeof path) {
516
+ return page('*', path);
517
+ }
518
+
519
+ // route <path> to <callback ...>
520
+ if ('function' === typeof fn) {
521
+ var route = new Route(/** @type {string} */ (path));
522
+ for (var i = 1; i < arguments.length; ++i) {
523
+ page.callbacks.push(route.middleware(arguments[i]));
524
+ }
525
+ // show <path> with [state]
526
+ } else if ('string' === typeof path) {
527
+ page['string' === typeof fn ? 'redirect' : 'show'](path, fn);
528
+ // start [options]
529
+ } else {
530
+ page.start(path);
531
+ }
532
+ }
533
+
534
+ /**
535
+ * Callback functions.
536
+ */
537
+
538
+ page.callbacks = [];
539
+ page.exits = [];
540
+
541
+ /**
542
+ * Current path being processed
543
+ * @type {string}
544
+ */
545
+ page.current = '';
546
+
547
+ /**
548
+ * Number of pages navigated to.
549
+ * @type {number}
550
+ *
551
+ * page.len == 0;
552
+ * page('/login');
553
+ * page.len == 1;
554
+ */
555
+
556
+ page.len = 0;
557
+
558
+ /**
559
+ * Get or set basepath to `path`.
560
+ *
561
+ * @param {string} path
562
+ * @api public
563
+ */
564
+
565
+ page.base = function(path) {
566
+ if (0 === arguments.length) return base;
567
+ base = path;
568
+ };
569
+
570
+ /**
571
+ * Get or set strict path matching to `enable`
572
+ *
573
+ * @param {boolean} enable
574
+ * @api public
575
+ */
576
+
577
+ page.strict = function(enable) {
578
+ if (0 === arguments.length) return strict;
579
+ strict = enable;
580
+ };
581
+
582
+ /**
583
+ * Bind with the given `options`.
584
+ *
585
+ * Options:
586
+ *
587
+ * - `click` bind to click events [true]
588
+ * - `popstate` bind to popstate [true]
589
+ * - `dispatch` perform initial dispatch [true]
590
+ *
591
+ * @param {Object} options
592
+ * @api public
593
+ */
594
+
595
+ page.start = function(options) {
596
+ options = options || {};
597
+ if (running) return;
598
+ running = true;
599
+ pageWindow = options.window || (hasWindow && window);
600
+ if (false === options.dispatch) dispatch = false;
601
+ if (false === options.decodeURLComponents) decodeURLComponents = false;
602
+ if (false !== options.popstate && hasWindow) pageWindow.addEventListener('popstate', onpopstate, false);
603
+ if (false !== options.click && hasDocument) {
604
+ pageWindow.document.addEventListener(clickEvent, onclick, false);
605
+ }
606
+ hashbang = !!options.hashbang;
607
+ if(hashbang && hasWindow && !hasHistory) {
608
+ pageWindow.addEventListener('hashchange', onpopstate, false);
609
+ }
610
+ if (!dispatch) return;
611
+
612
+ var url;
613
+ if(isLocation) {
614
+ var loc = pageWindow.location;
615
+
616
+ if(hashbang && ~loc.hash.indexOf('#!')) {
617
+ url = loc.hash.substr(2) + loc.search;
618
+ } else if (hashbang) {
619
+ url = loc.search + loc.hash;
620
+ } else {
621
+ url = loc.pathname + loc.search + loc.hash;
622
+ }
623
+ }
624
+
625
+ page.replace(url, null, true, dispatch);
626
+ };
627
+
628
+ /**
629
+ * Unbind click and popstate event handlers.
630
+ *
631
+ * @api public
632
+ */
633
+
634
+ page.stop = function() {
635
+ if (!running) return;
636
+ page.current = '';
637
+ page.len = 0;
638
+ running = false;
639
+ hasDocument && pageWindow.document.removeEventListener(clickEvent, onclick, false);
640
+ hasWindow && pageWindow.removeEventListener('popstate', onpopstate, false);
641
+ hasWindow && pageWindow.removeEventListener('hashchange', onpopstate, false);
642
+ };
643
+
644
+ /**
645
+ * Show `path` with optional `state` object.
646
+ *
647
+ * @param {string} path
648
+ * @param {Object=} state
649
+ * @param {boolean=} dispatch
650
+ * @param {boolean=} push
651
+ * @return {!Context}
652
+ * @api public
653
+ */
654
+
655
+ page.show = function(path, state, dispatch, push) {
656
+ var ctx = new Context(path, state),
657
+ prev = prevContext;
658
+ prevContext = ctx;
659
+ page.current = ctx.path;
660
+ if (false !== dispatch) page.dispatch(ctx, prev);
661
+ if (false !== ctx.handled && false !== push) ctx.pushState();
662
+ return ctx;
663
+ };
664
+
665
+ /**
666
+ * Goes back in the history
667
+ * Back should always let the current route push state and then go back.
668
+ *
669
+ * @param {string} path - fallback path to go back if no more history exists, if undefined defaults to page.base
670
+ * @param {Object=} state
671
+ * @api public
672
+ */
673
+
674
+ page.back = function(path, state) {
675
+ if (page.len > 0) {
676
+ // this may need more testing to see if all browsers
677
+ // wait for the next tick to go back in history
678
+ hasHistory && pageWindow.history.back();
679
+ page.len--;
680
+ } else if (path) {
681
+ setTimeout(function() {
682
+ page.show(path, state);
683
+ });
684
+ }else{
685
+ setTimeout(function() {
686
+ page.show(getBase(), state);
687
+ });
688
+ }
689
+ };
690
+
691
+
692
+ /**
693
+ * Register route to redirect from one path to other
694
+ * or just redirect to another route
695
+ *
696
+ * @param {string} from - if param 'to' is undefined redirects to 'from'
697
+ * @param {string=} to
698
+ * @api public
699
+ */
700
+ page.redirect = function(from, to) {
701
+ // Define route from a path to another
702
+ if ('string' === typeof from && 'string' === typeof to) {
703
+ page(from, function(e) {
704
+ setTimeout(function() {
705
+ page.replace(/** @type {!string} */ (to));
706
+ }, 0);
707
+ });
708
+ }
709
+
710
+ // Wait for the push state and replace it with another
711
+ if ('string' === typeof from && 'undefined' === typeof to) {
712
+ setTimeout(function() {
713
+ page.replace(from);
714
+ }, 0);
715
+ }
716
+ };
717
+
718
+ /**
719
+ * Replace `path` with optional `state` object.
720
+ *
721
+ * @param {string} path
722
+ * @param {Object=} state
723
+ * @param {boolean=} init
724
+ * @param {boolean=} dispatch
725
+ * @return {!Context}
726
+ * @api public
727
+ */
728
+
729
+
730
+ page.replace = function(path, state, init, dispatch) {
731
+ var ctx = new Context(path, state),
732
+ prev = prevContext;
733
+ prevContext = ctx;
734
+ page.current = ctx.path;
735
+ ctx.init = init;
736
+ ctx.save(); // save before dispatching, which may redirect
737
+ if (false !== dispatch) page.dispatch(ctx, prev);
738
+ return ctx;
739
+ };
740
+
741
+ /**
742
+ * Dispatch the given `ctx`.
743
+ *
744
+ * @param {Context} ctx
745
+ * @api private
746
+ */
747
+
748
+ page.dispatch = function(ctx, prev) {
749
+ var i = 0,
750
+ j = 0;
751
+
752
+ function nextExit() {
753
+ var fn = page.exits[j++];
754
+ if (!fn) return nextEnter();
755
+ fn(prev, nextExit);
756
+ }
757
+
758
+ function nextEnter() {
759
+ var fn = page.callbacks[i++];
760
+
761
+ if (ctx.path !== page.current) {
762
+ ctx.handled = false;
763
+ return;
764
+ }
765
+ if (!fn) return unhandled(ctx);
766
+ fn(ctx, nextEnter);
767
+ }
768
+
769
+ if (prev) {
770
+ nextExit();
771
+ } else {
772
+ nextEnter();
773
+ }
774
+ };
775
+
776
+ /**
777
+ * Unhandled `ctx`. When it's not the initial
778
+ * popstate then redirect. If you wish to handle
779
+ * 404s on your own use `page('*', callback)`.
780
+ *
781
+ * @param {Context} ctx
782
+ * @api private
783
+ */
784
+ function unhandled(ctx) {
785
+ if (ctx.handled) return;
786
+ var current;
787
+
788
+ if (hashbang) {
789
+ current = isLocation && getBase() + pageWindow.location.hash.replace('#!', '');
790
+ } else {
791
+ current = isLocation && pageWindow.location.pathname + pageWindow.location.search;
792
+ }
793
+
794
+ if (current === ctx.canonicalPath) return;
795
+ page.stop();
796
+ ctx.handled = false;
797
+ isLocation && (pageWindow.location.href = ctx.canonicalPath);
798
+ }
799
+
800
+ /**
801
+ * Register an exit route on `path` with
802
+ * callback `fn()`, which will be called
803
+ * on the previous context when a new
804
+ * page is visited.
805
+ */
806
+ page.exit = function(path, fn) {
807
+ if (typeof path === 'function') {
808
+ return page.exit('*', path);
809
+ }
810
+
811
+ var route = new Route(path);
812
+ for (var i = 1; i < arguments.length; ++i) {
813
+ page.exits.push(route.middleware(arguments[i]));
814
+ }
815
+ };
816
+
817
+ /**
818
+ * Remove URL encoding from the given `str`.
819
+ * Accommodates whitespace in both x-www-form-urlencoded
820
+ * and regular percent-encoded form.
821
+ *
822
+ * @param {string} val - URL component to decode
823
+ */
824
+ function decodeURLEncodedURIComponent(val) {
825
+ if (typeof val !== 'string') { return val; }
826
+ return decodeURLComponents ? decodeURIComponent(val.replace(/\+/g, ' ')) : val;
827
+ }
828
+
829
+ /**
830
+ * Initialize a new "request" `Context`
831
+ * with the given `path` and optional initial `state`.
832
+ *
833
+ * @constructor
834
+ * @param {string} path
835
+ * @param {Object=} state
836
+ * @api public
837
+ */
838
+
839
+ function Context(path, state) {
840
+ var pageBase = getBase();
841
+ if ('/' === path[0] && 0 !== path.indexOf(pageBase)) path = pageBase + (hashbang ? '#!' : '') + path;
842
+ var i = path.indexOf('?');
843
+
844
+ this.canonicalPath = path;
845
+ this.path = path.replace(pageBase, '') || '/';
846
+ if (hashbang) this.path = this.path.replace('#!', '') || '/';
847
+
848
+ this.title = (hasDocument && pageWindow.document.title);
849
+ this.state = state || {};
850
+ this.state.path = path;
851
+ this.querystring = ~i ? decodeURLEncodedURIComponent(path.slice(i + 1)) : '';
852
+ this.pathname = decodeURLEncodedURIComponent(~i ? path.slice(0, i) : path);
853
+ this.params = {};
854
+
855
+ // fragment
856
+ this.hash = '';
857
+ if (!hashbang) {
858
+ if (!~this.path.indexOf('#')) return;
859
+ var parts = this.path.split('#');
860
+ this.path = this.pathname = parts[0];
861
+ this.hash = decodeURLEncodedURIComponent(parts[1]) || '';
862
+ this.querystring = this.querystring.split('#')[0];
863
+ }
864
+ }
865
+
866
+ /**
867
+ * Expose `Context`.
868
+ */
869
+
870
+ page.Context = Context;
871
+
872
+ /**
873
+ * Push state.
874
+ *
875
+ * @api private
876
+ */
877
+
878
+ Context.prototype.pushState = function() {
879
+ page.len++;
880
+ if (hasHistory) {
881
+ pageWindow.history.pushState(this.state, this.title,
882
+ hashbang && this.path !== '/' ? '#!' + this.path : this.canonicalPath);
883
+ }
884
+ };
885
+
886
+ /**
887
+ * Save the context state.
888
+ *
889
+ * @api public
890
+ */
891
+
892
+ Context.prototype.save = function() {
893
+ if (hasHistory && pageWindow.location.protocol !== 'file:') {
894
+ pageWindow.history.replaceState(this.state, this.title,
895
+ hashbang && this.path !== '/' ? '#!' + this.path : this.canonicalPath);
896
+ }
897
+ };
898
+
899
+ /**
900
+ * Initialize `Route` with the given HTTP `path`,
901
+ * and an array of `callbacks` and `options`.
902
+ *
903
+ * Options:
904
+ *
905
+ * - `sensitive` enable case-sensitive routes
906
+ * - `strict` enable strict matching for trailing slashes
907
+ *
908
+ * @constructor
909
+ * @param {string} path
910
+ * @param {Object=} options
911
+ * @api private
912
+ */
913
+
914
+ function Route(path, options) {
915
+ options = options || {};
916
+ options.strict = options.strict || strict;
917
+ this.path = (path === '*') ? '(.*)' : path;
918
+ this.method = 'GET';
919
+ this.regexp = pathToRegexp_1(this.path,
920
+ this.keys = [],
921
+ options);
922
+ }
923
+
924
+ /**
925
+ * Expose `Route`.
926
+ */
927
+
928
+ page.Route = Route;
929
+
930
+ /**
931
+ * Return route middleware with
932
+ * the given callback `fn()`.
933
+ *
934
+ * @param {Function} fn
935
+ * @return {Function}
936
+ * @api public
937
+ */
938
+
939
+ Route.prototype.middleware = function(fn) {
940
+ var self = this;
941
+ return function(ctx, next) {
942
+ if (self.match(ctx.path, ctx.params)) return fn(ctx, next);
943
+ next();
944
+ };
945
+ };
946
+
947
+ /**
948
+ * Check if this route matches `path`, if so
949
+ * populate `params`.
950
+ *
951
+ * @param {string} path
952
+ * @param {Object} params
953
+ * @return {boolean}
954
+ * @api private
955
+ */
956
+
957
+ Route.prototype.match = function(path, params) {
958
+ var keys = this.keys,
959
+ qsIndex = path.indexOf('?'),
960
+ pathname = ~qsIndex ? path.slice(0, qsIndex) : path,
961
+ m = this.regexp.exec(decodeURIComponent(pathname));
962
+
963
+ if (!m) return false;
964
+
965
+ for (var i = 1, len = m.length; i < len; ++i) {
966
+ var key = keys[i - 1];
967
+ var val = decodeURLEncodedURIComponent(m[i]);
968
+ if (val !== undefined || !(hasOwnProperty.call(params, key.name))) {
969
+ params[key.name] = val;
970
+ }
971
+ }
972
+
973
+ return true;
974
+ };
975
+
976
+
977
+ /**
978
+ * Handle "populate" events.
979
+ */
980
+
981
+ var onpopstate = (function () {
982
+ var loaded = false;
983
+ if ( ! hasWindow ) {
984
+ return;
985
+ }
986
+ if (hasDocument && document.readyState === 'complete') {
987
+ loaded = true;
988
+ } else {
989
+ window.addEventListener('load', function() {
990
+ setTimeout(function() {
991
+ loaded = true;
992
+ }, 0);
993
+ });
994
+ }
995
+ return function onpopstate(e) {
996
+ if (!loaded) return;
997
+ if (e.state) {
998
+ var path = e.state.path;
999
+ page.replace(path, e.state);
1000
+ } else if (isLocation) {
1001
+ var loc = pageWindow.location;
1002
+ page.show(loc.pathname + loc.hash, undefined, undefined, false);
1003
+ }
1004
+ };
1005
+ })();
1006
+ /**
1007
+ * Handle "click" events.
1008
+ */
1009
+
1010
+ /* jshint +W054 */
1011
+ function onclick(e) {
1012
+ if (1 !== which(e)) return;
1013
+
1014
+ if (e.metaKey || e.ctrlKey || e.shiftKey) return;
1015
+ if (e.defaultPrevented) return;
1016
+
1017
+ // ensure link
1018
+ // use shadow dom when available if not, fall back to composedPath() for browsers that only have shady
1019
+ var el = e.target;
1020
+ var eventPath = e.path || (e.composedPath ? e.composedPath() : null);
1021
+
1022
+ if(eventPath) {
1023
+ for (var i = 0; i < eventPath.length; i++) {
1024
+ if (!eventPath[i].nodeName) continue;
1025
+ if (eventPath[i].nodeName.toUpperCase() !== 'A') continue;
1026
+ if (!eventPath[i].href) continue;
1027
+
1028
+ el = eventPath[i];
1029
+ break;
1030
+ }
1031
+ }
1032
+ // continue ensure link
1033
+ // el.nodeName for svg links are 'a' instead of 'A'
1034
+ while (el && 'A' !== el.nodeName.toUpperCase()) el = el.parentNode;
1035
+ if (!el || 'A' !== el.nodeName.toUpperCase()) return;
1036
+
1037
+ // check if link is inside an svg
1038
+ // in this case, both href and target are always inside an object
1039
+ var svg = (typeof el.href === 'object') && el.href.constructor.name === 'SVGAnimatedString';
1040
+
1041
+ // Ignore if tag has
1042
+ // 1. "download" attribute
1043
+ // 2. rel="external" attribute
1044
+ if (el.hasAttribute('download') || el.getAttribute('rel') === 'external') return;
1045
+
1046
+ // ensure non-hash for the same path
1047
+ var link = el.getAttribute('href');
1048
+ if(!hashbang && samePath(el) && (el.hash || '#' === link)) return;
1049
+
1050
+ // Check for mailto: in the href
1051
+ if (link && link.indexOf('mailto:') > -1) return;
1052
+
1053
+ // check target
1054
+ // svg target is an object and its desired value is in .baseVal property
1055
+ if (svg ? el.target.baseVal : el.target) return;
1056
+
1057
+ // x-origin
1058
+ // note: svg links that are not relative don't call click events (and skip page.js)
1059
+ // consequently, all svg links tested inside page.js are relative and in the same origin
1060
+ if (!svg && !sameOrigin(el.href)) return;
1061
+
1062
+ // rebuild path
1063
+ // There aren't .pathname and .search properties in svg links, so we use href
1064
+ // Also, svg href is an object and its desired value is in .baseVal property
1065
+ var path = svg ? el.href.baseVal : (el.pathname + el.search + (el.hash || ''));
1066
+
1067
+ path = path[0] !== '/' ? '/' + path : path;
1068
+
1069
+ // strip leading "/[drive letter]:" on NW.js on Windows
1070
+ if (hasProcess && path.match(/^\/[a-zA-Z]:\//)) {
1071
+ path = path.replace(/^\/[a-zA-Z]:\//, '/');
1072
+ }
1073
+
1074
+ // same page
1075
+ var orig = path;
1076
+ var pageBase = getBase();
1077
+
1078
+ if (path.indexOf(pageBase) === 0) {
1079
+ path = path.substr(base.length);
1080
+ }
1081
+
1082
+ if (hashbang) path = path.replace('#!', '');
1083
+
1084
+ if (pageBase && orig === path) return;
1085
+
1086
+ e.preventDefault();
1087
+ page.show(orig);
1088
+ }
1089
+
1090
+ /**
1091
+ * Event button.
1092
+ */
1093
+
1094
+ function which(e) {
1095
+ e = e || (hasWindow && window.event);
1096
+ return null == e.which ? e.button : e.which;
1097
+ }
1098
+
1099
+ /**
1100
+ * Convert to a URL object
1101
+ */
1102
+ function toURL(href) {
1103
+ if(typeof URL === 'function' && isLocation) {
1104
+ return new URL(href, location.toString());
1105
+ } else if (hasDocument) {
1106
+ var anc = document.createElement('a');
1107
+ anc.href = href;
1108
+ return anc;
1109
+ }
1110
+ }
1111
+
1112
+ /**
1113
+ * Check if `href` is the same origin.
1114
+ */
1115
+
1116
+ function sameOrigin(href) {
1117
+ if(!href || !isLocation) return false;
1118
+ var url = toURL(href);
1119
+
1120
+ var loc = pageWindow.location;
1121
+ return loc.protocol === url.protocol &&
1122
+ loc.hostname === url.hostname &&
1123
+ loc.port === url.port;
1124
+ }
1125
+
1126
+ function samePath(url) {
1127
+ if(!isLocation) return false;
1128
+ var loc = pageWindow.location;
1129
+ return url.pathname === loc.pathname &&
1130
+ url.search === loc.search;
1131
+ }
1132
+
1133
+ /**
1134
+ * Gets the `base`, which depends on whether we are using History or
1135
+ * hashbang routing.
1136
+ */
1137
+ function getBase() {
1138
+ if(!!base) return base;
1139
+ var loc = hasWindow && pageWindow && pageWindow.location;
1140
+ return (hasWindow && hashbang && loc && loc.protocol === 'file:') ? loc.pathname : base;
1141
+ }
1142
+
1143
+ page.sameOrigin = sameOrigin;
1144
+
1145
+ return page_js;
1146
+
1147
+ })));
1148
+ return this;
1149
+ };
1150
+
1151
+ const global = new Global().dispatch();
1152
+
1153
+ export default global.page;