spa-history
Version:
A HTML5 history library for single-page application.
744 lines (598 loc) • 17.6 kB
JavaScript
function _inheritsLoose(subClass, superClass) {
subClass.prototype = Object.create(superClass.prototype);
subClass.prototype.constructor = subClass;
subClass.__proto__ = superClass;
}
function _int(s, _temp) {
var _ref = _temp === void 0 ? {} : _temp,
radix = _ref.radix,
defaults = _ref.defaults,
_throws = _ref["throws"];
if (s == null) {
return defaults;
}
s = parseInt(s, radix);
if (isNaN(s)) {
if (_throws) {
throw _throws;
} else {
return defaults;
}
} else {
return s;
}
}
function _float(s, _temp2) {
var _ref2 = _temp2 === void 0 ? {} : _temp2,
defaults = _ref2.defaults,
_throws2 = _ref2["throws"];
if (s == null) {
return defaults;
}
s = parseFloat(s);
if (isNaN(s)) {
if (_throws2) {
throw _throws2;
} else {
return defaults;
}
} else {
return s;
}
}
function _number(s, _temp3) {
var _ref3 = _temp3 === void 0 ? {} : _temp3,
defaults = _ref3.defaults,
_throws3 = _ref3["throws"];
if (s == null) {
return defaults;
}
s = Number(s);
if (isNaN(s)) {
if (_throws3) {
throw _throws3;
} else {
return defaults;
}
} else {
return s;
}
}
function _bool(s, _temp4) {
var _ref4 = _temp4 === void 0 ? {} : _temp4,
_ref4$empty = _ref4.empty,
empty = _ref4$empty === void 0 ? true : _ref4$empty,
defaults = _ref4.defaults,
_throws4 = _ref4["throws"];
if (s == null) {
return defaults;
}
var truthy = ['1', 'true', 'yes'];
var falsy = ['0', 'false', 'no'];
if (empty === true) {
truthy.push('');
} else if (empty === false) {
falsy.push('');
}
if (truthy.includes(s)) {
return true;
} else if (falsy.includes(s)) {
return false;
} else {
if (_throws4) {
throw _throws4;
} else {
return defaults;
}
}
}
function _string(v, _temp5) {
var _ref5 = _temp5 === void 0 ? {} : _temp5,
defaults = _ref5.defaults;
return v == null ? defaults : String(v);
}
function _arrayOfInt(a, _temp6) {
var _ref6 = _temp6 === void 0 ? {} : _temp6,
radix = _ref6.radix,
defaults = _ref6.defaults,
dedup = _ref6.dedup,
splitComma = _ref6.splitComma,
_throws5 = _ref6["throws"];
return trimArray(toArray(a, splitComma).map(function (s) {
return _int(s, {
radix: radix,
"throws": _throws5
});
}), defaults, dedup);
}
function _arrayOfFloat(a, _temp7) {
var _ref7 = _temp7 === void 0 ? {} : _temp7,
defaults = _ref7.defaults,
dedup = _ref7.dedup,
splitComma = _ref7.splitComma,
_throws6 = _ref7["throws"];
return trimArray(toArray(a, splitComma).map(function (s) {
return _float(s, {
"throws": _throws6
});
}), defaults, dedup);
}
function _arrayOfNumber(a, _temp8) {
var _ref8 = _temp8 === void 0 ? {} : _temp8,
defaults = _ref8.defaults,
dedup = _ref8.dedup,
splitComma = _ref8.splitComma,
_throws7 = _ref8["throws"];
return trimArray(toArray(a, splitComma).map(function (s) {
return _number(s, {
"throws": _throws7
});
}), defaults, dedup);
}
function _arrayOfString(a, _temp9) {
var _ref9 = _temp9 === void 0 ? {} : _temp9,
defaults = _ref9.defaults,
dedup = _ref9.dedup,
splitComma = _ref9.splitComma;
return trimArray(toArray(a, splitComma).map(function (v) {
return _string(v);
}), defaults, dedup);
}
function toArray(a, splitComma) {
if (splitComma === void 0) {
splitComma = false;
}
if (a == null) {
return [];
}
if (a.constructor === String) {
a = [a];
}
if (splitComma) {
a = a.join(',').split(',');
}
return a;
}
function trimArray(a, defaults, dedup) {
if (dedup === void 0) {
dedup = true;
}
a = a.filter(function (v, i) {
return v != null && (dedup ? a.indexOf(v) === i : true);
});
return a.length ? a : defaults;
}
var StringCaster =
/*#__PURE__*/
function () {
function StringCaster(source) {
this.source = source;
}
var _proto = StringCaster.prototype;
_proto._getValue = function _getValue(key, isArray) {
if (isArray === void 0) {
isArray = false;
}
var src = this.source;
if (this.source instanceof Function) {
src = this.source();
}
if (src instanceof URLSearchParams) {
return isArray ? src.getAll(key) : src.get(key);
} else {
return src[key];
}
};
_proto["int"] = function int(key, _temp10) {
var _ref10 = _temp10 === void 0 ? {} : _temp10,
defaults = _ref10.defaults,
radix = _ref10.radix,
_throws8 = _ref10["throws"];
return _int(this._getValue(key), {
defaults: defaults,
radix: radix,
"throws": _throws8
});
};
_proto["float"] = function float(key, _temp11) {
var _ref11 = _temp11 === void 0 ? {} : _temp11,
defaults = _ref11.defaults,
_throws9 = _ref11["throws"];
return _float(this._getValue(key), {
defaults: defaults,
"throws": _throws9
});
};
_proto.number = function number(key, _temp12) {
var _ref12 = _temp12 === void 0 ? {} : _temp12,
defaults = _ref12.defaults,
_throws10 = _ref12["throws"];
return _number(this._getValue(key), {
defaults: defaults,
"throws": _throws10
});
};
_proto.bool = function bool(key, _temp13) {
var _ref13 = _temp13 === void 0 ? {} : _temp13,
empty = _ref13.empty,
defaults = _ref13.defaults,
_throws11 = _ref13["throws"];
return _bool(this._getValue(key), {
empty: empty,
defaults: defaults,
"throws": _throws11
});
};
_proto.string = function string(key, _temp14) {
var _ref14 = _temp14 === void 0 ? {} : _temp14,
defaults = _ref14.defaults;
return _string(this._getValue(key), {
defaults: defaults
});
};
_proto.arrayOfInt = function arrayOfInt(key, _temp15) {
var _ref15 = _temp15 === void 0 ? {} : _temp15,
radix = _ref15.radix,
defaults = _ref15.defaults,
dedup = _ref15.dedup,
splitComma = _ref15.splitComma,
_throws12 = _ref15["throws"];
return _arrayOfInt(this._getValue(key, true), {
radix: radix,
defaults: defaults,
dedup: dedup,
splitComma: splitComma,
"throws": _throws12
});
};
_proto.arrayOfFloat = function arrayOfFloat(key, _temp16) {
var _ref16 = _temp16 === void 0 ? {} : _temp16,
defaults = _ref16.defaults,
dedup = _ref16.dedup,
splitComma = _ref16.splitComma,
_throws13 = _ref16["throws"];
return _arrayOfFloat(this._getValue(key, true), {
defaults: defaults,
dedup: dedup,
splitComma: splitComma,
"throws": _throws13
});
};
_proto.arrayOfNumber = function arrayOfNumber(key, _temp17) {
var _ref17 = _temp17 === void 0 ? {} : _temp17,
defaults = _ref17.defaults,
dedup = _ref17.dedup,
splitComma = _ref17.splitComma,
_throws14 = _ref17["throws"];
return _arrayOfNumber(this._getValue(key, true), {
defaults: defaults,
dedup: dedup,
splitComma: splitComma,
"throws": _throws14
});
};
_proto.arrayOfString = function arrayOfString(key, _temp18) {
var _ref18 = _temp18 === void 0 ? {} : _temp18,
defaults = _ref18.defaults,
dedup = _ref18.dedup,
splitComma = _ref18.splitComma,
_throws15 = _ref18["throws"];
return _arrayOfString(this._getValue(key, true), {
defaults: defaults,
dedup: dedup,
splitComma: splitComma,
"throws": _throws15
});
};
return StringCaster;
}();
function appendSearchParams(searchParams, q) {
switch (q.constructor) {
case Object:
Object.entries(q).forEach(function (_ref) {
var key = _ref[0],
val = _ref[1];
if (val != null) {
if (val.constructor === Array) {
val.forEach(function (v) {
return searchParams.append(key, v);
});
} else {
searchParams.append(key, val);
}
}
});
break;
case String:
q = new URLSearchParams(q);
// falls through
case URLSearchParams:
q.forEach(function (val, key) {
return searchParams.append(key, val);
});
break;
case Array:
q.forEach(function (_ref2) {
var key = _ref2[0],
val = _ref2[1];
return searchParams.append(key, val);
});
break;
}
}
var SUPPORT_HISTORY_API = typeof window === 'object' && window.history && window.history.pushState;
var SUPPORT_HISTORY_ERR = 'Current environment doesn\'t support History API';
var _default =
/*#__PURE__*/
function () {
function _default(_ref) {
var _ref$beforeChange = _ref.beforeChange,
beforeChange = _ref$beforeChange === void 0 ? function () {} : _ref$beforeChange,
afterChange = _ref.afterChange;
this.beforeChange = beforeChange;
this.afterChange = afterChange;
this.current = null;
}
var _proto = _default.prototype;
_proto.start = function start(loc) {
var _this = this;
if (!loc && SUPPORT_HISTORY_API) {
loc = this._getCurrentLocationFromBrowser();
} else {
loc = this.normalize(loc);
}
if (SUPPORT_HISTORY_API) {
if (!history.state || !history.state.__position__) {
this.setState({});
}
this._onpopstate = function () {
_this._beforeChange('pop', _this._getCurrentLocationFromBrowser());
};
window.addEventListener('popstate', this._onpopstate);
}
this._beforeChange('init', loc);
};
_proto.url = function url(loc) {
return this.normalize(loc).url;
};
_proto.normalize = function normalize(loc) {
if (loc.constructor === String) {
loc = {
path: loc
};
} else {
loc = Object.assign({}, loc);
}
var hasOrigin = /^\w+:\/\//.test(loc.path);
if (loc.external || hasOrigin) {
loc.path = this._extractPathFromExternalURL(new URL(hasOrigin ? loc.path : 'http://a.a' + loc.path));
delete loc.external;
}
var url = new URL('http://a.a' + loc.path);
if (loc.query) {
appendSearchParams(url.searchParams, loc.query instanceof StringCaster ? loc.query.source : loc.query);
}
if (loc.hash) {
url.hash = loc.hash;
}
Object.assign(loc, {
path: url.pathname,
query: new StringCaster(url.searchParams),
hash: url.hash,
fullPath: url.pathname + url.search + url.hash,
state: loc.state ? JSON.parse(JSON.stringify(loc.state)) : {} // dereferencing
});
loc.url = this._url(loc);
return loc;
};
_proto._getCurrentLocationFromBrowser = function _getCurrentLocationFromBrowser() {
var state = Object.assign({}, window.history.state);
var loc = this.normalize(state.__path__ || this._extractPathFromExternalURL(window.location));
loc.state = state;
if (state.__path__) {
loc.hidden = true;
}
return loc;
};
_proto._beforeChange = function _beforeChange(action, to) {
var _this2 = this;
var position = history.state && history.state.__position__ || history.length;
to.state.__position__ = action === 'push' ? position + 1 : position;
Promise.resolve(this.beforeChange(to, this.current, action)).then(function (ret) {
if (ret === undefined || ret === true) {
if (action === 'push' || action === 'replace') {
_this2.__changeHistory(action, to);
}
var from = _this2.current;
_this2.current = to;
_this2.afterChange(to, from, action);
} else if (ret === false) {
if (action === 'pop') {
_this2.go(_this2.current.state.__position__ - to.state.__position__, {
silent: true
});
}
} // do nothing if returns null
else if (ret === null) {
return;
} else if (ret.constructor === String || ret.constructor === Object) {
if (ret.action) {
action = ret.action;
} else if (action === 'init') {
action = 'replace';
} else if (action === 'pop') {
action = 'push';
}
_this2._beforeChange(action, _this2.normalize(ret));
}
});
};
_proto.dispatch = function dispatch(to) {
to = this.normalize(to);
this._beforeChange('dispatch', to);
}
/*
{
path,
query,
hash,
state,
hidden
}
*/
;
_proto.push = function push(to) {
this._changeHistory('push', to);
};
_proto.replace = function replace(to) {
this._changeHistory('replace', to);
};
_proto.setState = function setState(state) {
var s = Object.assign({}, history.state, JSON.parse(JSON.stringify(state))); // dereferencing
this.__changeHistory('replace', {
state: s
});
if (this.current) {
Object.assign(this.current.state, s);
}
};
_proto._changeHistory = function _changeHistory(action, to) {
to = this.normalize(to);
if (to.silent) {
this.__changeHistory(action, to);
this.current = to;
} else {
this._beforeChange(action, to);
}
};
_proto.__changeHistory = function __changeHistory(action, to) {
if (!SUPPORT_HISTORY_API) {
return;
}
var state = to.state;
var url = to.url || null;
if (to.hidden) {
state.__path__ = to.fullPath;
url = to.appearPath && this.url(to.appearPath);
}
var position = history.state && history.state.__position__ || history.length;
state.__position__ = action === 'push' ? position + 1 : position;
window.history[action + 'State'](Object.keys(state).length ? state : null, '', url);
};
_proto.go = function go(n, _temp) {
var _this3 = this;
var _ref2 = _temp === void 0 ? {} : _temp,
_ref2$state = _ref2.state,
state = _ref2$state === void 0 ? null : _ref2$state,
_ref2$silent = _ref2.silent,
silent = _ref2$silent === void 0 ? false : _ref2$silent;
return new Promise(function (resolve, reject) {
if (!SUPPORT_HISTORY_API) {
return reject(new Error(SUPPORT_HISTORY_ERR));
}
var onpopstate = function onpopstate() {
window.removeEventListener('popstate', onpopstate);
window.addEventListener('popstate', _this3._onpopstate);
var to = _this3._getCurrentLocationFromBrowser();
if (state) {
Object.assign(to.state, state);
_this3.__changeHistory('replace', to);
}
if (silent) {
_this3.current = to;
} else {
_this3._beforeChange('pop', to);
}
resolve();
};
window.removeEventListener('popstate', _this3._onpopstate);
window.addEventListener('popstate', onpopstate);
window.history.go(n);
});
};
_proto.back = function back(opts) {
return this.go(-1, opts);
};
_proto.forward = function forward(opts) {
return this.go(1, opts);
};
_proto.captureLinkClickEvent = function captureLinkClickEvent(e) {
if (e.defaultPrevented) {
return;
}
var a = e.target.closest('a');
if (!a) {
return;
} // open new window
var target = a.target;
if (target && (target === '_blank' || target === '_parent' && window.parent !== window || target === '_top' && window.top !== window || !(target in {
_self: 1,
_blank: 1,
_parent: 1,
_top: 1
}) && target !== window.name)) {
return;
} // outside of the app
if (!a.href.startsWith(location.origin + this.url('/'))) {
return;
}
var to = this.normalize(a.href);
e.preventDefault(); // same url
if (to.path === this.current.path && to.query.source.toString() === this.current.query.source.toString() && to.hash === this.current.hash) {
this.replace(to);
} else {
this.push(to);
}
};
return _default;
}();
var _default$1 =
/*#__PURE__*/
function (_Base) {
_inheritsLoose(_default, _Base);
function _default(args) {
var _this;
_this = _Base.call(this, args) || this;
_this.base = args.base || '';
return _this;
}
var _proto = _default.prototype;
_proto._extractPathFromExternalURL = function _extractPathFromExternalURL(url) {
var path = url.pathname;
if (this.base && this.base !== '/' && path.startsWith(this.base)) {
path = path.replace(this.base, '');
if (!path) {
path = '/';
} else if (this.base.endsWith('/')) {
path = '/' + path;
}
}
return path + url.search + url.hash;
};
_proto._url = function _url(loc) {
// if base is not end with /
// do not append / if is the root path
if (loc.path === '/' && this.base && !this.base.endsWith('/')) {
return this.base + loc.fullPath.slice(1);
}
return (this.base && this.base.endsWith('/') ? this.base.slice(0, -1) : this.base) + loc.fullPath;
};
return _default;
}(_default);
var _default$2 =
/*#__PURE__*/
function (_Base) {
_inheritsLoose(_default, _Base);
function _default() {
return _Base.apply(this, arguments) || this;
}
var _proto = _default.prototype;
_proto._extractPathFromExternalURL = function _extractPathFromExternalURL(url) {
return url.hash.slice(1) || '/';
};
_proto._url = function _url(loc) {
return loc.fullPath === '/' ? location.pathname + location.search : '#' + loc.fullPath;
};
return _default;
}(_default);
export { _default$2 as HashHistory, _default$1 as PathHistory };