can
Version:
MIT-licensed, client-side, JavaScript framework that makes building rich web applications easy.
133 lines (132 loc) • 6.09 kB
JavaScript
/*!
* CanJS - 2.3.34
* http://canjs.com/
* Copyright (c) 2018 Bitovi
* Mon, 30 Apr 2018 20:56:51 GMT
* Licensed MIT
*/
/*can@2.3.34#route/pushstate/pushstate*/
define([
'can/util/library',
'can/route'
], function (can) {
'use strict';
var hasPushstate = window.history && window.history.pushState;
var isFileProtocol = window.location && window.location.protocol === 'file:';
if (!isFileProtocol && hasPushstate) {
can.route.bindings.pushstate = {
root: '/',
matchSlashes: false,
paramsMatcher: /^\?(?:[^=]+=[^&]*&)*[^=]+=[^&]*/,
querySeparator: '?',
bind: function () {
if (can.isNode) {
return;
}
can.delegate.call(can.$(document.documentElement), 'a', 'click', anchorClickHandler);
can.each(methodsToOverwrite, function (method) {
originalMethods[method] = window.history[method];
window.history[method] = function (state, title, url) {
var absolute = url.indexOf('http') === 0;
var searchHash = window.location.search + window.location.hash;
if (!absolute && url !== window.location.pathname + searchHash || absolute && url !== window.location.href + searchHash) {
originalMethods[method].apply(window.history, arguments);
can.route.setState();
}
};
});
can.bind.call(window, 'popstate', can.route.setState);
},
unbind: function () {
can.undelegate.call(can.$(document.documentElement), 'click', 'a', anchorClickHandler);
can.each(methodsToOverwrite, function (method) {
window.history[method] = originalMethods[method];
});
can.unbind.call(window, 'popstate', can.route.setState);
},
matchingPartOfURL: function () {
var root = cleanRoot(), loc = location.pathname + location.search, index = loc.indexOf(root);
return loc.substr(index + root.length);
},
setURL: function (path, changed) {
var method = 'pushState';
if (includeHash && path.indexOf('#') === -1 && window.location.hash) {
path += window.location.hash;
}
if (replaceStateAttrs.length > 0) {
var toRemove = [];
for (var i = 0, l = changed.length; i < l; i++) {
if (can.inArray(changed[i], replaceStateAttrs) !== -1) {
method = 'replaceState';
}
if (can.inArray(changed[i], replaceStateAttrs.once) !== -1) {
toRemove.push(changed[i]);
}
}
if (toRemove.length > 0) {
removeAttrs(replaceStateAttrs, toRemove);
removeAttrs(replaceStateAttrs.once, toRemove);
}
}
window.history[method](null, null, can.route._call('root') + path);
}
};
var anchorClickHandler = function (e) {
if (!(e.isDefaultPrevented ? e.isDefaultPrevented() : e.defaultPrevented === true)) {
var node = this._node || this;
var linksHost = node.host || window.location.host;
if (node.href === 'javascript://') {
return;
}
if (window.location.host === linksHost) {
var root = cleanRoot();
if (node.pathname.indexOf(root) === 0) {
var url = (node.pathname + node.search).substr(root.length);
var curParams = can.route.deparam(url);
if (curParams.hasOwnProperty('route')) {
includeHash = true;
window.history.pushState(null, null, node.href);
if (e.preventDefault) {
e.preventDefault();
}
}
}
}
}
}, cleanRoot = function () {
var domain = location.protocol + '//' + location.host, root = can.route._call('root'), index = root.indexOf(domain);
if (index === 0) {
return root.substr(domain.length);
}
return root;
}, removeAttrs = function (arr, attrs) {
var index;
for (var i = attrs.length - 1; i >= 0; i--) {
if ((index = can.inArray(attrs[i], arr)) !== -1) {
arr.splice(index, 1);
}
}
}, methodsToOverwrite = [
'pushState',
'replaceState'
], originalMethods = {}, includeHash = false, replaceStateAttrs = [];
can.route.defaultBinding = 'pushstate';
can.extend(can.route, {
replaceStateOn: function () {
var attrs = can.makeArray(arguments);
Array.prototype.push.apply(replaceStateAttrs, attrs);
},
replaceStateOnce: function () {
var attrs = can.makeArray(arguments);
replaceStateAttrs.once = can.makeArray(replaceStateAttrs.once);
Array.prototype.push.apply(replaceStateAttrs.once, attrs);
can.route.replaceStateOn.apply(this, arguments);
},
replaceStateOff: function () {
var attrs = can.makeArray(arguments);
removeAttrs(replaceStateAttrs, attrs);
}
});
}
return can;
});