|
@@ -408,16 +408,6 @@ pathToRegexp_1.tokensToRegExp = tokensToRegExp_1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- /**
|
|
|
|
- * Module exports.
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
- var page_js = page;
|
|
|
|
- page.default = page;
|
|
|
|
- page.Context = Context;
|
|
|
|
- page.Route = Route;
|
|
|
|
- page.sameOrigin = sameOrigin;
|
|
|
|
-
|
|
|
|
/**
|
|
/**
|
|
* Short-cuts for global-object checks
|
|
* Short-cuts for global-object checks
|
|
*/
|
|
*/
|
|
@@ -440,116 +430,67 @@ pathToRegexp_1.tokensToRegExp = tokensToRegExp_1;
|
|
var isLocation = hasWindow && !!(window.history.location || window.location);
|
|
var isLocation = hasWindow && !!(window.history.location || window.location);
|
|
|
|
|
|
/**
|
|
/**
|
|
- * Perform initial dispatch.
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
- var dispatch = true;
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Decode URL components (query string, pathname, hash).
|
|
|
|
- * Accommodates both regular percent encoding and x-www-form-urlencoded format.
|
|
|
|
- */
|
|
|
|
- var decodeURLComponents = true;
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Base path.
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
- var base = '';
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Strict path matching.
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
- var strict = false;
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Running flag.
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
- var running;
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * HashBang option
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
- var hashbang = false;
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Previous context, for capturing
|
|
|
|
- * page exit events.
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
- var prevContext;
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * The window for which this `page` is running
|
|
|
|
|
|
+ * The page instance
|
|
|
|
+ * @api private
|
|
*/
|
|
*/
|
|
- var pageWindow;
|
|
|
|
|
|
+ function Page(options) {
|
|
|
|
+ // public things
|
|
|
|
+ this.callbacks = [];
|
|
|
|
+ this.exits = [];
|
|
|
|
+ this.current = '';
|
|
|
|
+ this.len = 0;
|
|
|
|
+
|
|
|
|
+ // private things
|
|
|
|
+ this._dispatch = true;
|
|
|
|
+ this._decodeURLComponents = true;
|
|
|
|
+ this._base = '';
|
|
|
|
+ this._strict = false;
|
|
|
|
+ this._running = false;
|
|
|
|
+ this._hashbang = false;
|
|
|
|
+
|
|
|
|
+ // bound functions
|
|
|
|
+ this._onclick = this._onclick.bind(this);
|
|
|
|
+ this._onpopstate = this._onpopstate.bind(this);
|
|
|
|
+
|
|
|
|
+ this.configure(options);
|
|
|
|
+ }
|
|
|
|
|
|
/**
|
|
/**
|
|
- * Register `path` with callback `fn()`,
|
|
|
|
- * or route `path`, or redirection,
|
|
|
|
- * or `page.start()`.
|
|
|
|
|
|
+ * Configure the instance of page. This can be called multiple times.
|
|
*
|
|
*
|
|
- * page(fn);
|
|
|
|
- * page('*', fn);
|
|
|
|
- * page('/user/:id', load, user);
|
|
|
|
- * page('/user/' + user.id, { some: 'thing' });
|
|
|
|
- * page('/user/' + user.id);
|
|
|
|
- * page('/from', '/to')
|
|
|
|
- * page();
|
|
|
|
- *
|
|
|
|
- * @param {string|!Function|!Object} path
|
|
|
|
- * @param {Function=} fn
|
|
|
|
|
|
+ * @param {Object} options
|
|
* @api public
|
|
* @api public
|
|
*/
|
|
*/
|
|
|
|
|
|
- function page(path, fn) {
|
|
|
|
- // <callback>
|
|
|
|
- if ('function' === typeof path) {
|
|
|
|
- return page('*', path);
|
|
|
|
|
|
+ Page.prototype.configure = function(options) {
|
|
|
|
+ var opts = options || {};
|
|
|
|
+
|
|
|
|
+ this._window = opts.window || (hasWindow && window);
|
|
|
|
+ this._dispatch = opts.dispatch !== false;
|
|
|
|
+ this._decodeURLComponents = opts.decodeURLComponents !== false;
|
|
|
|
+ this._popstate = opts.popstate !== false && hasWindow;
|
|
|
|
+ this._click = opts.click !== false && hasDocument;
|
|
|
|
+ this._hashbang = !!opts.hashbang;
|
|
|
|
+
|
|
|
|
+ var _window = this._window;
|
|
|
|
+ if(this._popstate) {
|
|
|
|
+ _window.addEventListener('popstate', this._onpopstate, false);
|
|
|
|
+ } else if(hasWindow) {
|
|
|
|
+ _window.removeEventListener('popstate', this._onpopstate, false);
|
|
}
|
|
}
|
|
|
|
|
|
- // route <path> to <callback ...>
|
|
|
|
- if ('function' === typeof fn) {
|
|
|
|
- var route = new Route(/** @type {string} */ (path));
|
|
|
|
- for (var i = 1; i < arguments.length; ++i) {
|
|
|
|
- page.callbacks.push(route.middleware(arguments[i]));
|
|
|
|
- }
|
|
|
|
- // show <path> with [state]
|
|
|
|
- } else if ('string' === typeof path) {
|
|
|
|
- page['string' === typeof fn ? 'redirect' : 'show'](path, fn);
|
|
|
|
- // start [options]
|
|
|
|
- } else {
|
|
|
|
- page.start(path);
|
|
|
|
|
|
+ if (this._click) {
|
|
|
|
+ _window.document.addEventListener(clickEvent, this._onclick, false);
|
|
|
|
+ } else if(hasDocument) {
|
|
|
|
+ _window.document.removeEventListener(clickEvent, this._onclick, false);
|
|
}
|
|
}
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Callback functions.
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
- page.callbacks = [];
|
|
|
|
- page.exits = [];
|
|
|
|
|
|
|
|
- /**
|
|
|
|
- * Current path being processed
|
|
|
|
- * @type {string}
|
|
|
|
- */
|
|
|
|
- page.current = '';
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Number of pages navigated to.
|
|
|
|
- * @type {number}
|
|
|
|
- *
|
|
|
|
- * page.len == 0;
|
|
|
|
- * page('/login');
|
|
|
|
- * page.len == 1;
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
- page.len = 0;
|
|
|
|
|
|
+ if(this._hashbang && hasWindow && !hasHistory) {
|
|
|
|
+ _window.addEventListener('hashchange', this._onpopstate, false);
|
|
|
|
+ } else if(hasWindow) {
|
|
|
|
+ _window.removeEventListener('hashchange', this._onpopstate, false);
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
|
|
/**
|
|
/**
|
|
* Get or set basepath to `path`.
|
|
* Get or set basepath to `path`.
|
|
@@ -558,9 +499,22 @@ pathToRegexp_1.tokensToRegExp = tokensToRegExp_1;
|
|
* @api public
|
|
* @api public
|
|
*/
|
|
*/
|
|
|
|
|
|
- page.base = function(path) {
|
|
|
|
- if (0 === arguments.length) return base;
|
|
|
|
- base = path;
|
|
|
|
|
|
+ Page.prototype.base = function(path) {
|
|
|
|
+ if (0 === arguments.length) return this._base;
|
|
|
|
+ this._base = path;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Gets the `base`, which depends on whether we are using History or
|
|
|
|
+ * hashbang routing.
|
|
|
|
+
|
|
|
|
+ * @api private
|
|
|
|
+ */
|
|
|
|
+ Page.prototype._getBase = function() {
|
|
|
|
+ var base = this._base;
|
|
|
|
+ if(!!base) return base;
|
|
|
|
+ var loc = hasWindow && this._window && this._window.location;
|
|
|
|
+ return (hasWindow && this._hashbang && loc && loc.protocol === 'file:') ? loc.pathname : base;
|
|
};
|
|
};
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -570,11 +524,12 @@ pathToRegexp_1.tokensToRegExp = tokensToRegExp_1;
|
|
* @api public
|
|
* @api public
|
|
*/
|
|
*/
|
|
|
|
|
|
- page.strict = function(enable) {
|
|
|
|
- if (0 === arguments.length) return strict;
|
|
|
|
- strict = enable;
|
|
|
|
|
|
+ Page.prototype.strict = function(enable) {
|
|
|
|
+ if (0 === arguments.length) return this._strict;
|
|
|
|
+ this._strict = enable;
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* Bind with the given `options`.
|
|
* Bind with the given `options`.
|
|
*
|
|
*
|
|
@@ -588,37 +543,27 @@ pathToRegexp_1.tokensToRegExp = tokensToRegExp_1;
|
|
* @api public
|
|
* @api public
|
|
*/
|
|
*/
|
|
|
|
|
|
- page.start = function(options) {
|
|
|
|
- options = options || {};
|
|
|
|
- if (running) return;
|
|
|
|
- running = true;
|
|
|
|
- pageWindow = options.window || (hasWindow && window);
|
|
|
|
- if (false === options.dispatch) dispatch = false;
|
|
|
|
- if (false === options.decodeURLComponents) decodeURLComponents = false;
|
|
|
|
- if (false !== options.popstate && hasWindow) pageWindow.addEventListener('popstate', onpopstate, false);
|
|
|
|
- if (false !== options.click && hasDocument) {
|
|
|
|
- pageWindow.document.addEventListener(clickEvent, onclick, false);
|
|
|
|
- }
|
|
|
|
- hashbang = !!options.hashbang;
|
|
|
|
- if(hashbang && hasWindow && !hasHistory) {
|
|
|
|
- pageWindow.addEventListener('hashchange', onpopstate, false);
|
|
|
|
- }
|
|
|
|
- if (!dispatch) return;
|
|
|
|
|
|
+ Page.prototype.start = function(options) {
|
|
|
|
+ this.configure(options);
|
|
|
|
+
|
|
|
|
+ if (!this._dispatch) return;
|
|
|
|
+ this._running = true;
|
|
|
|
|
|
var url;
|
|
var url;
|
|
if(isLocation) {
|
|
if(isLocation) {
|
|
- var loc = pageWindow.location;
|
|
|
|
|
|
+ var window = this._window;
|
|
|
|
+ var loc = window.location;
|
|
|
|
|
|
- if(hashbang && ~loc.hash.indexOf('#!')) {
|
|
|
|
|
|
+ if(this._hashbang && ~loc.hash.indexOf('#!')) {
|
|
url = loc.hash.substr(2) + loc.search;
|
|
url = loc.hash.substr(2) + loc.search;
|
|
- } else if (hashbang) {
|
|
|
|
|
|
+ } else if (this._hashbang) {
|
|
url = loc.search + loc.hash;
|
|
url = loc.search + loc.hash;
|
|
} else {
|
|
} else {
|
|
url = loc.pathname + loc.search + loc.hash;
|
|
url = loc.pathname + loc.search + loc.hash;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- page.replace(url, null, true, dispatch);
|
|
|
|
|
|
+ this.replace(url, null, true, this._dispatch);
|
|
};
|
|
};
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -627,14 +572,16 @@ pathToRegexp_1.tokensToRegExp = tokensToRegExp_1;
|
|
* @api public
|
|
* @api public
|
|
*/
|
|
*/
|
|
|
|
|
|
- page.stop = function() {
|
|
|
|
- if (!running) return;
|
|
|
|
- page.current = '';
|
|
|
|
- page.len = 0;
|
|
|
|
- running = false;
|
|
|
|
- hasDocument && pageWindow.document.removeEventListener(clickEvent, onclick, false);
|
|
|
|
- hasWindow && pageWindow.removeEventListener('popstate', onpopstate, false);
|
|
|
|
- hasWindow && pageWindow.removeEventListener('hashchange', onpopstate, false);
|
|
|
|
|
|
+ Page.prototype.stop = function() {
|
|
|
|
+ if (!this._running) return;
|
|
|
|
+ this.current = '';
|
|
|
|
+ this.len = 0;
|
|
|
|
+ this._running = false;
|
|
|
|
+
|
|
|
|
+ var window = this._window;
|
|
|
|
+ hasDocument && window.document.removeEventListener(clickEvent, this._onclick, false);
|
|
|
|
+ hasWindow && window.removeEventListener('popstate', this._onpopstate, false);
|
|
|
|
+ hasWindow && window.removeEventListener('hashchange', this._onpopstate, false);
|
|
};
|
|
};
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -648,12 +595,12 @@ pathToRegexp_1.tokensToRegExp = tokensToRegExp_1;
|
|
* @api public
|
|
* @api public
|
|
*/
|
|
*/
|
|
|
|
|
|
- page.show = function(path, state, dispatch, push) {
|
|
|
|
- var ctx = new Context(path, state),
|
|
|
|
- prev = prevContext;
|
|
|
|
- prevContext = ctx;
|
|
|
|
- page.current = ctx.path;
|
|
|
|
- if (false !== dispatch) page.dispatch(ctx, prev);
|
|
|
|
|
|
+ Page.prototype.show = function(path, state, dispatch, push) {
|
|
|
|
+ var ctx = new Context(path, state, this),
|
|
|
|
+ prev = this.prevContext;
|
|
|
|
+ this.prevContext = ctx;
|
|
|
|
+ this.current = ctx.path;
|
|
|
|
+ if (this._dispatch) this.dispatch(ctx, prev);
|
|
if (false !== ctx.handled && false !== push) ctx.pushState();
|
|
if (false !== ctx.handled && false !== push) ctx.pushState();
|
|
return ctx;
|
|
return ctx;
|
|
};
|
|
};
|
|
@@ -667,24 +614,25 @@ pathToRegexp_1.tokensToRegExp = tokensToRegExp_1;
|
|
* @api public
|
|
* @api public
|
|
*/
|
|
*/
|
|
|
|
|
|
- page.back = function(path, state) {
|
|
|
|
- if (page.len > 0) {
|
|
|
|
|
|
+ Page.prototype.back = function(path, state) {
|
|
|
|
+ var page = this;
|
|
|
|
+ if (this.len > 0) {
|
|
|
|
+ var window = this._window;
|
|
// this may need more testing to see if all browsers
|
|
// this may need more testing to see if all browsers
|
|
// wait for the next tick to go back in history
|
|
// wait for the next tick to go back in history
|
|
- hasHistory && pageWindow.history.back();
|
|
|
|
- page.len--;
|
|
|
|
|
|
+ hasHistory && window.history.back();
|
|
|
|
+ this.len--;
|
|
} else if (path) {
|
|
} else if (path) {
|
|
setTimeout(function() {
|
|
setTimeout(function() {
|
|
page.show(path, state);
|
|
page.show(path, state);
|
|
});
|
|
});
|
|
- }else{
|
|
|
|
|
|
+ } else {
|
|
setTimeout(function() {
|
|
setTimeout(function() {
|
|
- page.show(getBase(), state);
|
|
|
|
|
|
+ page.show(page._getBase(), state);
|
|
});
|
|
});
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
-
|
|
|
|
/**
|
|
/**
|
|
* Register route to redirect from one path to other
|
|
* Register route to redirect from one path to other
|
|
* or just redirect to another route
|
|
* or just redirect to another route
|
|
@@ -693,12 +641,14 @@ pathToRegexp_1.tokensToRegExp = tokensToRegExp_1;
|
|
* @param {string=} to
|
|
* @param {string=} to
|
|
* @api public
|
|
* @api public
|
|
*/
|
|
*/
|
|
- page.redirect = function(from, to) {
|
|
|
|
|
|
+ Page.prototype.redirect = function(from, to) {
|
|
|
|
+ var inst = this;
|
|
|
|
+
|
|
// Define route from a path to another
|
|
// Define route from a path to another
|
|
if ('string' === typeof from && 'string' === typeof to) {
|
|
if ('string' === typeof from && 'string' === typeof to) {
|
|
- page(from, function(e) {
|
|
|
|
|
|
+ page.call(this, from, function(e) {
|
|
setTimeout(function() {
|
|
setTimeout(function() {
|
|
- page.replace(/** @type {!string} */ (to));
|
|
|
|
|
|
+ inst.replace(/** @type {!string} */ (to));
|
|
}, 0);
|
|
}, 0);
|
|
});
|
|
});
|
|
}
|
|
}
|
|
@@ -706,7 +656,7 @@ pathToRegexp_1.tokensToRegExp = tokensToRegExp_1;
|
|
// Wait for the push state and replace it with another
|
|
// Wait for the push state and replace it with another
|
|
if ('string' === typeof from && 'undefined' === typeof to) {
|
|
if ('string' === typeof from && 'undefined' === typeof to) {
|
|
setTimeout(function() {
|
|
setTimeout(function() {
|
|
- page.replace(from);
|
|
|
|
|
|
+ inst.replace(from);
|
|
}, 0);
|
|
}, 0);
|
|
}
|
|
}
|
|
};
|
|
};
|
|
@@ -723,14 +673,14 @@ pathToRegexp_1.tokensToRegExp = tokensToRegExp_1;
|
|
*/
|
|
*/
|
|
|
|
|
|
|
|
|
|
- page.replace = function(path, state, init, dispatch) {
|
|
|
|
- var ctx = new Context(path, state),
|
|
|
|
- prev = prevContext;
|
|
|
|
- prevContext = ctx;
|
|
|
|
- page.current = ctx.path;
|
|
|
|
|
|
+ Page.prototype.replace = function(path, state, init, dispatch) {
|
|
|
|
+ var ctx = new Context(path, state, this),
|
|
|
|
+ prev = this.prevContext;
|
|
|
|
+ this.prevContext = ctx;
|
|
|
|
+ this.current = ctx.path;
|
|
ctx.init = init;
|
|
ctx.init = init;
|
|
ctx.save(); // save before dispatching, which may redirect
|
|
ctx.save(); // save before dispatching, which may redirect
|
|
- if (false !== dispatch) page.dispatch(ctx, prev);
|
|
|
|
|
|
+ if (false !== dispatch) this.dispatch(ctx, prev);
|
|
return ctx;
|
|
return ctx;
|
|
};
|
|
};
|
|
|
|
|
|
@@ -741,9 +691,8 @@ pathToRegexp_1.tokensToRegExp = tokensToRegExp_1;
|
|
* @api private
|
|
* @api private
|
|
*/
|
|
*/
|
|
|
|
|
|
- page.dispatch = function(ctx, prev) {
|
|
|
|
- var i = 0,
|
|
|
|
- j = 0;
|
|
|
|
|
|
+ Page.prototype.dispatch = function(ctx, prev) {
|
|
|
|
+ var i = 0, j = 0, page = this;
|
|
|
|
|
|
function nextExit() {
|
|
function nextExit() {
|
|
var fn = page.exits[j++];
|
|
var fn = page.exits[j++];
|
|
@@ -758,7 +707,7 @@ pathToRegexp_1.tokensToRegExp = tokensToRegExp_1;
|
|
ctx.handled = false;
|
|
ctx.handled = false;
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
- if (!fn) return unhandled(ctx);
|
|
|
|
|
|
+ if (!fn) return unhandled.call(page, ctx);
|
|
fn(ctx, nextEnter);
|
|
fn(ctx, nextEnter);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -770,44 +719,190 @@ pathToRegexp_1.tokensToRegExp = tokensToRegExp_1;
|
|
};
|
|
};
|
|
|
|
|
|
/**
|
|
/**
|
|
- * Unhandled `ctx`. When it's not the initial
|
|
|
|
- * popstate then redirect. If you wish to handle
|
|
|
|
- * 404s on your own use `page('*', callback)`.
|
|
|
|
- *
|
|
|
|
- * @param {Context} ctx
|
|
|
|
|
|
+ * Register an exit route on `path` with
|
|
|
|
+ * callback `fn()`, which will be called
|
|
|
|
+ * on the previous context when a new
|
|
|
|
+ * page is visited.
|
|
|
|
+ */
|
|
|
|
+ Page.prototype.exit = function(path, fn) {
|
|
|
|
+ if (typeof path === 'function') {
|
|
|
|
+ return this.exit('*', path);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var route = new Route(path, null, this);
|
|
|
|
+ for (var i = 1; i < arguments.length; ++i) {
|
|
|
|
+ this.exits.push(route.middleware(arguments[i]));
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Handle "click" events.
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+ /* jshint +W054 */
|
|
|
|
+ Page.prototype._onclick = function(e) {
|
|
|
|
+ if (1 !== this._which(e)) return;
|
|
|
|
+
|
|
|
|
+ if (e.metaKey || e.ctrlKey || e.shiftKey) return;
|
|
|
|
+ if (e.defaultPrevented) return;
|
|
|
|
+
|
|
|
|
+ // ensure link
|
|
|
|
+ // use shadow dom when available if not, fall back to composedPath()
|
|
|
|
+ // for browsers that only have shady
|
|
|
|
+ var el = e.target;
|
|
|
|
+ var eventPath = e.path || (e.composedPath ? e.composedPath() : null);
|
|
|
|
+
|
|
|
|
+ if(eventPath) {
|
|
|
|
+ for (var i = 0; i < eventPath.length; i++) {
|
|
|
|
+ if (!eventPath[i].nodeName) continue;
|
|
|
|
+ if (eventPath[i].nodeName.toUpperCase() !== 'A') continue;
|
|
|
|
+ if (!eventPath[i].href) continue;
|
|
|
|
+
|
|
|
|
+ el = eventPath[i];
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // continue ensure link
|
|
|
|
+ // el.nodeName for svg links are 'a' instead of 'A'
|
|
|
|
+ while (el && 'A' !== el.nodeName.toUpperCase()) el = el.parentNode;
|
|
|
|
+ if (!el || 'A' !== el.nodeName.toUpperCase()) return;
|
|
|
|
+
|
|
|
|
+ // check if link is inside an svg
|
|
|
|
+ // in this case, both href and target are always inside an object
|
|
|
|
+ var svg = (typeof el.href === 'object') && el.href.constructor.name === 'SVGAnimatedString';
|
|
|
|
+
|
|
|
|
+ // Ignore if tag has
|
|
|
|
+ // 1. "download" attribute
|
|
|
|
+ // 2. rel="external" attribute
|
|
|
|
+ if (el.hasAttribute('download') || el.getAttribute('rel') === 'external') return;
|
|
|
|
+
|
|
|
|
+ // ensure non-hash for the same path
|
|
|
|
+ var link = el.getAttribute('href');
|
|
|
|
+ if(!this._hashbang && this._samePath(el) && (el.hash || '#' === link)) return;
|
|
|
|
+
|
|
|
|
+ // Check for mailto: in the href
|
|
|
|
+ if (link && link.indexOf('mailto:') > -1) return;
|
|
|
|
+
|
|
|
|
+ // check target
|
|
|
|
+ // svg target is an object and its desired value is in .baseVal property
|
|
|
|
+ if (svg ? el.target.baseVal : el.target) return;
|
|
|
|
+
|
|
|
|
+ // x-origin
|
|
|
|
+ // note: svg links that are not relative don't call click events (and skip page.js)
|
|
|
|
+ // consequently, all svg links tested inside page.js are relative and in the same origin
|
|
|
|
+ if (!svg && !this.sameOrigin(el.href)) return;
|
|
|
|
+
|
|
|
|
+ // rebuild path
|
|
|
|
+ // There aren't .pathname and .search properties in svg links, so we use href
|
|
|
|
+ // Also, svg href is an object and its desired value is in .baseVal property
|
|
|
|
+ var path = svg ? el.href.baseVal : (el.pathname + el.search + (el.hash || ''));
|
|
|
|
+
|
|
|
|
+ path = path[0] !== '/' ? '/' + path : path;
|
|
|
|
+
|
|
|
|
+ // strip leading "/[drive letter]:" on NW.js on Windows
|
|
|
|
+ if (hasProcess && path.match(/^\/[a-zA-Z]:\//)) {
|
|
|
|
+ path = path.replace(/^\/[a-zA-Z]:\//, '/');
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // same page
|
|
|
|
+ var orig = path;
|
|
|
|
+ var pageBase = this._getBase();
|
|
|
|
+
|
|
|
|
+ if (path.indexOf(pageBase) === 0) {
|
|
|
|
+ path = path.substr(pageBase.length);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (this._hashbang) path = path.replace('#!', '');
|
|
|
|
+
|
|
|
|
+ if (pageBase && orig === path) return;
|
|
|
|
+
|
|
|
|
+ e.preventDefault();
|
|
|
|
+ this.show(orig);
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Handle "populate" events.
|
|
* @api private
|
|
* @api private
|
|
*/
|
|
*/
|
|
- function unhandled(ctx) {
|
|
|
|
- if (ctx.handled) return;
|
|
|
|
- var current;
|
|
|
|
|
|
|
|
- if (hashbang) {
|
|
|
|
- current = isLocation && getBase() + pageWindow.location.hash.replace('#!', '');
|
|
|
|
|
|
+ Page.prototype._onpopstate = (function () {
|
|
|
|
+ var loaded = false;
|
|
|
|
+ if ( ! hasWindow ) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ if (hasDocument && document.readyState === 'complete') {
|
|
|
|
+ loaded = true;
|
|
} else {
|
|
} else {
|
|
- current = isLocation && pageWindow.location.pathname + pageWindow.location.search;
|
|
|
|
|
|
+ window.addEventListener('load', function() {
|
|
|
|
+ setTimeout(function() {
|
|
|
|
+ loaded = true;
|
|
|
|
+ }, 0);
|
|
|
|
+ });
|
|
}
|
|
}
|
|
|
|
+ return function onpopstate(e) {
|
|
|
|
+ if (!loaded) return;
|
|
|
|
+ var page = this;
|
|
|
|
+ if (e.state) {
|
|
|
|
+ var path = e.state.path;
|
|
|
|
+ page.replace(path, e.state);
|
|
|
|
+ } else if (isLocation) {
|
|
|
|
+ var loc = page._window.location;
|
|
|
|
+ page.show(loc.pathname + loc.search + loc.hash, undefined, undefined, false);
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+ })();
|
|
|
|
|
|
- if (current === ctx.canonicalPath) return;
|
|
|
|
- page.stop();
|
|
|
|
- ctx.handled = false;
|
|
|
|
- isLocation && (pageWindow.location.href = ctx.canonicalPath);
|
|
|
|
- }
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Event button.
|
|
|
|
+ */
|
|
|
|
+ Page.prototype._which = function(e) {
|
|
|
|
+ e = e || (hasWindow && this._window.event);
|
|
|
|
+ return null == e.which ? e.button : e.which;
|
|
|
|
+ };
|
|
|
|
|
|
/**
|
|
/**
|
|
- * Register an exit route on `path` with
|
|
|
|
- * callback `fn()`, which will be called
|
|
|
|
- * on the previous context when a new
|
|
|
|
- * page is visited.
|
|
|
|
|
|
+ * Convert to a URL object
|
|
|
|
+ * @api private
|
|
*/
|
|
*/
|
|
- page.exit = function(path, fn) {
|
|
|
|
- if (typeof path === 'function') {
|
|
|
|
- return page.exit('*', path);
|
|
|
|
|
|
+ Page.prototype._toURL = function(href) {
|
|
|
|
+ var window = this._window;
|
|
|
|
+ if(typeof URL === 'function' && isLocation) {
|
|
|
|
+ return new URL(href, window.location.toString());
|
|
|
|
+ } else if (hasDocument) {
|
|
|
|
+ var anc = window.document.createElement('a');
|
|
|
|
+ anc.href = href;
|
|
|
|
+ return anc;
|
|
}
|
|
}
|
|
|
|
+ };
|
|
|
|
|
|
- var route = new Route(path);
|
|
|
|
- for (var i = 1; i < arguments.length; ++i) {
|
|
|
|
- page.exits.push(route.middleware(arguments[i]));
|
|
|
|
- }
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Check if `href` is the same origin.
|
|
|
|
+ * @param {string} href
|
|
|
|
+ * @api public
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+ Page.prototype.sameOrigin = function(href) {
|
|
|
|
+ if(!href || !isLocation) return false;
|
|
|
|
+
|
|
|
|
+ var url = this._toURL(href);
|
|
|
|
+ var window = this._window;
|
|
|
|
+
|
|
|
|
+ var loc = window.location;
|
|
|
|
+ return loc.protocol === url.protocol &&
|
|
|
|
+ loc.hostname === url.hostname &&
|
|
|
|
+ loc.port === url.port;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * @api private
|
|
|
|
+ */
|
|
|
|
+ Page.prototype._samePath = function(url) {
|
|
|
|
+ if(!isLocation) return false;
|
|
|
|
+ var window = this._window;
|
|
|
|
+ var loc = window.location;
|
|
|
|
+ return url.pathname === loc.pathname &&
|
|
|
|
+ url.search === loc.search;
|
|
};
|
|
};
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -816,10 +911,129 @@ pathToRegexp_1.tokensToRegExp = tokensToRegExp_1;
|
|
* and regular percent-encoded form.
|
|
* and regular percent-encoded form.
|
|
*
|
|
*
|
|
* @param {string} val - URL component to decode
|
|
* @param {string} val - URL component to decode
|
|
|
|
+ * @api private
|
|
*/
|
|
*/
|
|
- function decodeURLEncodedURIComponent(val) {
|
|
|
|
|
|
+ Page.prototype._decodeURLEncodedURIComponent = function(val) {
|
|
if (typeof val !== 'string') { return val; }
|
|
if (typeof val !== 'string') { return val; }
|
|
- return decodeURLComponents ? decodeURIComponent(val.replace(/\+/g, ' ')) : val;
|
|
|
|
|
|
+ return this._decodeURLComponents ? decodeURIComponent(val.replace(/\+/g, ' ')) : val;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Create a new `page` instance and function
|
|
|
|
+ */
|
|
|
|
+ function createPage(options) {
|
|
|
|
+ var pageInstance = new Page();
|
|
|
|
+
|
|
|
|
+ function pageFn(/* args */) {
|
|
|
|
+ return page.apply(pageInstance, arguments);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Copy all of the things over. In 2.0 maybe we use setPrototypeOf
|
|
|
|
+ pageFn.callbacks = pageInstance.callbacks;
|
|
|
|
+ pageFn.exits = pageInstance.exits;
|
|
|
|
+ pageFn.base = pageInstance.base.bind(pageInstance);
|
|
|
|
+ pageFn.strict = pageInstance.strict.bind(pageInstance);
|
|
|
|
+ pageFn.start = pageInstance.start.bind(pageInstance);
|
|
|
|
+ pageFn.stop = pageInstance.stop.bind(pageInstance);
|
|
|
|
+ pageFn.show = pageInstance.show.bind(pageInstance);
|
|
|
|
+ pageFn.back = pageInstance.back.bind(pageInstance);
|
|
|
|
+ pageFn.redirect = pageInstance.redirect.bind(pageInstance);
|
|
|
|
+ pageFn.replace = pageInstance.replace.bind(pageInstance);
|
|
|
|
+ pageFn.dispatch = pageInstance.dispatch.bind(pageInstance);
|
|
|
|
+ pageFn.exit = pageInstance.exit.bind(pageInstance);
|
|
|
|
+ pageFn.configure = pageInstance.configure.bind(pageInstance);
|
|
|
|
+ pageFn.sameOrigin = pageInstance.sameOrigin.bind(pageInstance);
|
|
|
|
+
|
|
|
|
+ pageFn.create = createPage;
|
|
|
|
+
|
|
|
|
+ Object.defineProperty(pageFn, 'len', {
|
|
|
|
+ get: function(){
|
|
|
|
+ return pageInstance.len;
|
|
|
|
+ },
|
|
|
|
+ set: function(val) {
|
|
|
|
+ pageInstance.len = val;
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ Object.defineProperty(pageFn, 'current', {
|
|
|
|
+ get: function(){
|
|
|
|
+ return pageInstance.current;
|
|
|
|
+ },
|
|
|
|
+ set: function(val) {
|
|
|
|
+ pageInstance.current = val;
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ // In 2.0 these can be named exports
|
|
|
|
+ pageFn.Context = Context;
|
|
|
|
+ pageFn.Route = Route;
|
|
|
|
+
|
|
|
|
+ return pageFn;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Register `path` with callback `fn()`,
|
|
|
|
+ * or route `path`, or redirection,
|
|
|
|
+ * or `page.start()`.
|
|
|
|
+ *
|
|
|
|
+ * page(fn);
|
|
|
|
+ * page('*', fn);
|
|
|
|
+ * page('/user/:id', load, user);
|
|
|
|
+ * page('/user/' + user.id, { some: 'thing' });
|
|
|
|
+ * page('/user/' + user.id);
|
|
|
|
+ * page('/from', '/to')
|
|
|
|
+ * page();
|
|
|
|
+ *
|
|
|
|
+ * @param {string|!Function|!Object} path
|
|
|
|
+ * @param {Function=} fn
|
|
|
|
+ * @api public
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+ function page(path, fn) {
|
|
|
|
+ // <callback>
|
|
|
|
+ if ('function' === typeof path) {
|
|
|
|
+ return page.call(this, '*', path);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // route <path> to <callback ...>
|
|
|
|
+ if ('function' === typeof fn) {
|
|
|
|
+ var route = new Route(/** @type {string} */ (path), null, this);
|
|
|
|
+ for (var i = 1; i < arguments.length; ++i) {
|
|
|
|
+ this.callbacks.push(route.middleware(arguments[i]));
|
|
|
|
+ }
|
|
|
|
+ // show <path> with [state]
|
|
|
|
+ } else if ('string' === typeof path) {
|
|
|
|
+ this['string' === typeof fn ? 'redirect' : 'show'](path, fn);
|
|
|
|
+ // start [options]
|
|
|
|
+ } else {
|
|
|
|
+ this.start(path);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Unhandled `ctx`. When it's not the initial
|
|
|
|
+ * popstate then redirect. If you wish to handle
|
|
|
|
+ * 404s on your own use `page('*', callback)`.
|
|
|
|
+ *
|
|
|
|
+ * @param {Context} ctx
|
|
|
|
+ * @api private
|
|
|
|
+ */
|
|
|
|
+ function unhandled(ctx) {
|
|
|
|
+ if (ctx.handled) return;
|
|
|
|
+ var current;
|
|
|
|
+ var page = this;
|
|
|
|
+ var window = page._window;
|
|
|
|
+
|
|
|
|
+ if (page._hashbang) {
|
|
|
|
+ current = isLocation && this._getBase() + window.location.hash.replace('#!', '');
|
|
|
|
+ } else {
|
|
|
|
+ current = isLocation && window.location.pathname + window.location.search;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (current === ctx.canonicalPath) return;
|
|
|
|
+ page.stop();
|
|
|
|
+ ctx.handled = false;
|
|
|
|
+ isLocation && (window.location.href = ctx.canonicalPath);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -832,8 +1046,12 @@ pathToRegexp_1.tokensToRegExp = tokensToRegExp_1;
|
|
* @api public
|
|
* @api public
|
|
*/
|
|
*/
|
|
|
|
|
|
- function Context(path, state) {
|
|
|
|
- var pageBase = getBase();
|
|
|
|
|
|
+ function Context(path, state, pageInstance) {
|
|
|
|
+ var _page = this.page = pageInstance || page;
|
|
|
|
+ var window = _page._window;
|
|
|
|
+ var hashbang = _page.hashbang;
|
|
|
|
+
|
|
|
|
+ var pageBase = _page._getBase();
|
|
if ('/' === path[0] && 0 !== path.indexOf(pageBase)) path = pageBase + (hashbang ? '#!' : '') + path;
|
|
if ('/' === path[0] && 0 !== path.indexOf(pageBase)) path = pageBase + (hashbang ? '#!' : '') + path;
|
|
var i = path.indexOf('?');
|
|
var i = path.indexOf('?');
|
|
|
|
|
|
@@ -841,11 +1059,11 @@ pathToRegexp_1.tokensToRegExp = tokensToRegExp_1;
|
|
this.path = path.replace(pageBase, '') || '/';
|
|
this.path = path.replace(pageBase, '') || '/';
|
|
if (hashbang) this.path = this.path.replace('#!', '') || '/';
|
|
if (hashbang) this.path = this.path.replace('#!', '') || '/';
|
|
|
|
|
|
- this.title = (hasDocument && pageWindow.document.title);
|
|
|
|
|
|
+ this.title = (hasDocument && window.document.title);
|
|
this.state = state || {};
|
|
this.state = state || {};
|
|
this.state.path = path;
|
|
this.state.path = path;
|
|
- this.querystring = ~i ? decodeURLEncodedURIComponent(path.slice(i + 1)) : '';
|
|
|
|
- this.pathname = decodeURLEncodedURIComponent(~i ? path.slice(0, i) : path);
|
|
|
|
|
|
+ this.querystring = ~i ? _page._decodeURLEncodedURIComponent(path.slice(i + 1)) : '';
|
|
|
|
+ this.pathname = _page._decodeURLEncodedURIComponent(~i ? path.slice(0, i) : path);
|
|
this.params = {};
|
|
this.params = {};
|
|
|
|
|
|
// fragment
|
|
// fragment
|
|
@@ -854,17 +1072,11 @@ pathToRegexp_1.tokensToRegExp = tokensToRegExp_1;
|
|
if (!~this.path.indexOf('#')) return;
|
|
if (!~this.path.indexOf('#')) return;
|
|
var parts = this.path.split('#');
|
|
var parts = this.path.split('#');
|
|
this.path = this.pathname = parts[0];
|
|
this.path = this.pathname = parts[0];
|
|
- this.hash = decodeURLEncodedURIComponent(parts[1]) || '';
|
|
|
|
|
|
+ this.hash = _page._decodeURLEncodedURIComponent(parts[1]) || '';
|
|
this.querystring = this.querystring.split('#')[0];
|
|
this.querystring = this.querystring.split('#')[0];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- /**
|
|
|
|
- * Expose `Context`.
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
- page.Context = Context;
|
|
|
|
-
|
|
|
|
/**
|
|
/**
|
|
* Push state.
|
|
* Push state.
|
|
*
|
|
*
|
|
@@ -872,9 +1084,13 @@ pathToRegexp_1.tokensToRegExp = tokensToRegExp_1;
|
|
*/
|
|
*/
|
|
|
|
|
|
Context.prototype.pushState = function() {
|
|
Context.prototype.pushState = function() {
|
|
|
|
+ var page = this.page;
|
|
|
|
+ var window = page._window;
|
|
|
|
+ var hashbang = page._hashbang;
|
|
|
|
+
|
|
page.len++;
|
|
page.len++;
|
|
if (hasHistory) {
|
|
if (hasHistory) {
|
|
- pageWindow.history.pushState(this.state, this.title,
|
|
|
|
|
|
+ window.history.pushState(this.state, this.title,
|
|
hashbang && this.path !== '/' ? '#!' + this.path : this.canonicalPath);
|
|
hashbang && this.path !== '/' ? '#!' + this.path : this.canonicalPath);
|
|
}
|
|
}
|
|
};
|
|
};
|
|
@@ -886,9 +1102,10 @@ pathToRegexp_1.tokensToRegExp = tokensToRegExp_1;
|
|
*/
|
|
*/
|
|
|
|
|
|
Context.prototype.save = function() {
|
|
Context.prototype.save = function() {
|
|
- if (hasHistory && pageWindow.location.protocol !== 'file:') {
|
|
|
|
- pageWindow.history.replaceState(this.state, this.title,
|
|
|
|
- hashbang && this.path !== '/' ? '#!' + this.path : this.canonicalPath);
|
|
|
|
|
|
+ var page = this.page;
|
|
|
|
+ if (hasHistory && page._window.location.protocol !== 'file:') {
|
|
|
|
+ page._window.history.replaceState(this.state, this.title,
|
|
|
|
+ page._hashbang && this.path !== '/' ? '#!' + this.path : this.canonicalPath);
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
@@ -907,22 +1124,15 @@ pathToRegexp_1.tokensToRegExp = tokensToRegExp_1;
|
|
* @api private
|
|
* @api private
|
|
*/
|
|
*/
|
|
|
|
|
|
- function Route(path, options) {
|
|
|
|
- options = options || {};
|
|
|
|
- options.strict = options.strict || strict;
|
|
|
|
|
|
+ function Route(path, options, page) {
|
|
|
|
+ var _page = this.page = page || globalPage;
|
|
|
|
+ var opts = options || {};
|
|
|
|
+ opts.strict = opts.strict || page._strict;
|
|
this.path = (path === '*') ? '(.*)' : path;
|
|
this.path = (path === '*') ? '(.*)' : path;
|
|
this.method = 'GET';
|
|
this.method = 'GET';
|
|
- this.regexp = pathToRegexp_1(this.path,
|
|
|
|
- this.keys = [],
|
|
|
|
- options);
|
|
|
|
|
|
+ this.regexp = pathToRegexp_1(this.path, this.keys = [], opts);
|
|
}
|
|
}
|
|
|
|
|
|
- /**
|
|
|
|
- * Expose `Route`.
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
- page.Route = Route;
|
|
|
|
-
|
|
|
|
/**
|
|
/**
|
|
* Return route middleware with
|
|
* Return route middleware with
|
|
* the given callback `fn()`.
|
|
* the given callback `fn()`.
|
|
@@ -960,7 +1170,7 @@ pathToRegexp_1.tokensToRegExp = tokensToRegExp_1;
|
|
|
|
|
|
for (var i = 1, len = m.length; i < len; ++i) {
|
|
for (var i = 1, len = m.length; i < len; ++i) {
|
|
var key = keys[i - 1];
|
|
var key = keys[i - 1];
|
|
- var val = decodeURLEncodedURIComponent(m[i]);
|
|
|
|
|
|
+ var val = this.page._decodeURLEncodedURIComponent(m[i]);
|
|
if (val !== undefined || !(hasOwnProperty.call(params, key.name))) {
|
|
if (val !== undefined || !(hasOwnProperty.call(params, key.name))) {
|
|
params[key.name] = val;
|
|
params[key.name] = val;
|
|
}
|
|
}
|
|
@@ -971,172 +1181,12 @@ pathToRegexp_1.tokensToRegExp = tokensToRegExp_1;
|
|
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
- * Handle "populate" events.
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
- var onpopstate = (function () {
|
|
|
|
- var loaded = false;
|
|
|
|
- if ( ! hasWindow ) {
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
- if (hasDocument && document.readyState === 'complete') {
|
|
|
|
- loaded = true;
|
|
|
|
- } else {
|
|
|
|
- window.addEventListener('load', function() {
|
|
|
|
- setTimeout(function() {
|
|
|
|
- loaded = true;
|
|
|
|
- }, 0);
|
|
|
|
- });
|
|
|
|
- }
|
|
|
|
- return function onpopstate(e) {
|
|
|
|
- if (!loaded) return;
|
|
|
|
- if (e.state) {
|
|
|
|
- var path = e.state.path;
|
|
|
|
- page.replace(path, e.state);
|
|
|
|
- } else if (isLocation) {
|
|
|
|
- var loc = pageWindow.location;
|
|
|
|
- page.show(loc.pathname + loc.hash, undefined, undefined, false);
|
|
|
|
- }
|
|
|
|
- };
|
|
|
|
- })();
|
|
|
|
- /**
|
|
|
|
- * Handle "click" events.
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
- /* jshint +W054 */
|
|
|
|
- function onclick(e) {
|
|
|
|
- if (1 !== which(e)) return;
|
|
|
|
-
|
|
|
|
- if (e.metaKey || e.ctrlKey || e.shiftKey) return;
|
|
|
|
- if (e.defaultPrevented) return;
|
|
|
|
-
|
|
|
|
- // ensure link
|
|
|
|
- // use shadow dom when available if not, fall back to composedPath() for browsers that only have shady
|
|
|
|
- var el = e.target;
|
|
|
|
- var eventPath = e.path || (e.composedPath ? e.composedPath() : null);
|
|
|
|
-
|
|
|
|
- if(eventPath) {
|
|
|
|
- for (var i = 0; i < eventPath.length; i++) {
|
|
|
|
- if (!eventPath[i].nodeName) continue;
|
|
|
|
- if (eventPath[i].nodeName.toUpperCase() !== 'A') continue;
|
|
|
|
- if (!eventPath[i].href) continue;
|
|
|
|
-
|
|
|
|
- el = eventPath[i];
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- // continue ensure link
|
|
|
|
- // el.nodeName for svg links are 'a' instead of 'A'
|
|
|
|
- while (el && 'A' !== el.nodeName.toUpperCase()) el = el.parentNode;
|
|
|
|
- if (!el || 'A' !== el.nodeName.toUpperCase()) return;
|
|
|
|
-
|
|
|
|
- // check if link is inside an svg
|
|
|
|
- // in this case, both href and target are always inside an object
|
|
|
|
- var svg = (typeof el.href === 'object') && el.href.constructor.name === 'SVGAnimatedString';
|
|
|
|
-
|
|
|
|
- // Ignore if tag has
|
|
|
|
- // 1. "download" attribute
|
|
|
|
- // 2. rel="external" attribute
|
|
|
|
- if (el.hasAttribute('download') || el.getAttribute('rel') === 'external') return;
|
|
|
|
-
|
|
|
|
- // ensure non-hash for the same path
|
|
|
|
- var link = el.getAttribute('href');
|
|
|
|
- if(!hashbang && samePath(el) && (el.hash || '#' === link)) return;
|
|
|
|
-
|
|
|
|
- // Check for mailto: in the href
|
|
|
|
- if (link && link.indexOf('mailto:') > -1) return;
|
|
|
|
-
|
|
|
|
- // check target
|
|
|
|
- // svg target is an object and its desired value is in .baseVal property
|
|
|
|
- if (svg ? el.target.baseVal : el.target) return;
|
|
|
|
-
|
|
|
|
- // x-origin
|
|
|
|
- // note: svg links that are not relative don't call click events (and skip page.js)
|
|
|
|
- // consequently, all svg links tested inside page.js are relative and in the same origin
|
|
|
|
- if (!svg && !sameOrigin(el.href)) return;
|
|
|
|
-
|
|
|
|
- // rebuild path
|
|
|
|
- // There aren't .pathname and .search properties in svg links, so we use href
|
|
|
|
- // Also, svg href is an object and its desired value is in .baseVal property
|
|
|
|
- var path = svg ? el.href.baseVal : (el.pathname + el.search + (el.hash || ''));
|
|
|
|
-
|
|
|
|
- path = path[0] !== '/' ? '/' + path : path;
|
|
|
|
-
|
|
|
|
- // strip leading "/[drive letter]:" on NW.js on Windows
|
|
|
|
- if (hasProcess && path.match(/^\/[a-zA-Z]:\//)) {
|
|
|
|
- path = path.replace(/^\/[a-zA-Z]:\//, '/');
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // same page
|
|
|
|
- var orig = path;
|
|
|
|
- var pageBase = getBase();
|
|
|
|
-
|
|
|
|
- if (path.indexOf(pageBase) === 0) {
|
|
|
|
- path = path.substr(base.length);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (hashbang) path = path.replace('#!', '');
|
|
|
|
-
|
|
|
|
- if (pageBase && orig === path) return;
|
|
|
|
-
|
|
|
|
- e.preventDefault();
|
|
|
|
- page.show(orig);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Event button.
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
- function which(e) {
|
|
|
|
- e = e || (hasWindow && window.event);
|
|
|
|
- return null == e.which ? e.button : e.which;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Convert to a URL object
|
|
|
|
- */
|
|
|
|
- function toURL(href) {
|
|
|
|
- if(typeof URL === 'function' && isLocation) {
|
|
|
|
- return new URL(href, location.toString());
|
|
|
|
- } else if (hasDocument) {
|
|
|
|
- var anc = document.createElement('a');
|
|
|
|
- anc.href = href;
|
|
|
|
- return anc;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Check if `href` is the same origin.
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
- function sameOrigin(href) {
|
|
|
|
- if(!href || !isLocation) return false;
|
|
|
|
- var url = toURL(href);
|
|
|
|
-
|
|
|
|
- var loc = pageWindow.location;
|
|
|
|
- return loc.protocol === url.protocol &&
|
|
|
|
- loc.hostname === url.hostname &&
|
|
|
|
- loc.port === url.port;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- function samePath(url) {
|
|
|
|
- if(!isLocation) return false;
|
|
|
|
- var loc = pageWindow.location;
|
|
|
|
- return url.pathname === loc.pathname &&
|
|
|
|
- url.search === loc.search;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Gets the `base`, which depends on whether we are using History or
|
|
|
|
- * hashbang routing.
|
|
|
|
|
|
+ * Module exports.
|
|
*/
|
|
*/
|
|
- function getBase() {
|
|
|
|
- if(!!base) return base;
|
|
|
|
- var loc = hasWindow && pageWindow && pageWindow.location;
|
|
|
|
- return (hasWindow && hashbang && loc && loc.protocol === 'file:') ? loc.pathname : base;
|
|
|
|
- }
|
|
|
|
|
|
|
|
- page.sameOrigin = sameOrigin;
|
|
|
|
|
|
+ var globalPage = createPage();
|
|
|
|
+ var page_js = globalPage;
|
|
|
|
+ page.default = globalPage;
|
|
|
|
|
|
return page_js;
|
|
return page_js;
|
|
|
|
|