Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

can-route-pushstate

Package Overview
Dependencies
Maintainers
5
Versions
60
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

can-route-pushstate - npm Package Compare versions

Comparing version 3.2.0 to 4.0.0-pre.1

10

can-route-pushstate_test.js
/* jshint asi:true,scripturl:true */
var QUnit = require('steal-qunit');
var extend = require('can-util/js/assign/assign');
var canEvent = require('can-event');
var domEvents = require('can-util/dom/events/events');
require("can-util/dom/events/delegate/delegate");
var route = require('./can-route-pushstate');

@@ -513,2 +514,3 @@ var domDispatch = require('can-util/dom/dispatch/');

test("clicked hashes work (#259)", function () {

@@ -518,3 +520,3 @@

window.routeTestReady = function (iCanRoute, loc, hist, win) {
//win.queues.log("flush");
iCanRoute(win.location.pathname, {

@@ -533,7 +535,5 @@ page: "index"

win.document.body.appendChild(link);
domDispatch.call(link, "click");
setTimeout(function () {
deepEqual(extend({}, iCanRoute.attr()), {

@@ -688,3 +688,3 @@ type: "articles",

// kill the click b/c phantom doesn't like it.
canEvent.on.call(info.window.document, "click", clickKiller);
domEvents.addEventListener.call(info.window.document, "click", clickKiller);

@@ -691,0 +691,0 @@ info.history.pushState = function () {

@@ -16,8 +16,7 @@ // # can/route/pushstate/pushstate.js

var each = require('can-util/js/each/each');
var makeArray = require('can-util/js/make-array/make-array');
var diffObject = require('can-util/js/diff-object/diff-object');
var namespace = require('can-util/namespace');
var LOCATION = require('can-globals/location/location');
var canEvent = require('can-event');
var domEvents = require('can-util/dom/events/events');
require("can-util/dom/events/delegate/delegate");
var route = require('can-route');

@@ -29,147 +28,115 @@

var usePushStateRouting = hasPushstate && loc && validProtocols[loc.protocol];
var canReflect = require("can-reflect");
var KeyTree = require("can-key-tree");
var bindingProxy = require("can-route/src/binding-proxy");
var queues = require("can-queues");
var SimpleObservable = require("can-simple-observable");
// Initialize plugin only if browser supports pushstate.
if (usePushStateRouting) {
// Registers itself within `route.bindings`.
route.bindings.pushstate = {
/**
* @property {String} can-route-pushstate.root root
* @parent can-route-pushstate.static
*
* @description Configure the base url that will not be modified.
*
* @option {String} Represents the base url that pushstate will prepend to all
* routes. `root` defaults to: `"/"`.
*
* @body
*
* ## Use
*
* By default, a route like:
*
* route(":type/:id")
*
* Matches urls like:
*
* http://domain.com/contact/5
*
* But sometimes, you only want to match pages within a certain directory. For
* example, an application that is a filemanager. You might want to
* specify root and routes like:
*
* route.pushstate.root = "/filemanager/"
* route("file-:fileId");
* route("folder-:fileId")
*
* Which matches urls like:
*
* http://domain.com/filemanager/file-34234
*
*/
// Start of `location.pathname` is the root.
// (Can be configured via `route.bindings.pushstate.root`)
root: "/",
// don't greedily match slashes in routing rules
matchSlashes: false,
paramsMatcher: /^\?(?:[^=]+=[^&]*&)*[^=]+=[^&]*/,
querySeparator: '?',
// Original methods on `history` that will be overwritten
var methodsToOverwrite = ['pushState', 'replaceState'];
// ## bind
// Always returns clean root, without domain.
var cleanRoot = function () {
var domain = location.protocol + "//" + location.host,
root = bindingProxy.call("root"),
index = root.indexOf(domain);
if (index === 0) {
return root.substr(domain.length);
}
return root;
};
// Intercepts clicks on `<a>` elements and rewrites original `history` methods.
bind: function () {
if(isNode()) {
return;
}
// Handler function for `click` events.
// Checks if a route is matched, if one is, calls `.pushState`
// Intercept routable links.
canEvent.on.call(document.documentElement, 'click', 'a', anchorClickHandler);
// gets the current url after the root
function getCurrentUrl(){
var root = cleanRoot(),
location = LOCATION(),
loc = (location.pathname + location.search),
index = loc.indexOf(root);
// Rewrites original `pushState`/`replaceState` methods on `history` and keeps pointer to original methods
each(methodsToOverwrite, function (method) {
originalMethods[method] = window.history[method];
window.history[method] = function (state, title, url) {
// Avoid doubled history states (with pushState).
var absolute = url.indexOf("http") === 0;
var loc = LOCATION();
var searchHash = loc.search + loc.hash;
// If url differs from current call original histoy method and update `route` state.
if ((!absolute && url !== loc.pathname + searchHash) ||
(absolute && url !== loc.href + searchHash)) {
originalMethods[method].apply(window.history, arguments);
route.setState();
}
};
});
return loc.substr(index + root.length);
}
// Bind to `popstate` event, fires on back/forward.
canEvent.on.call(window, 'popstate', route.setState);
},
// ## unbind
// Unbinds and restores original `history` methods
unbind: function () {
canEvent.off.call(document.documentElement, 'click', 'a', anchorClickHandler);
each(methodsToOverwrite, function (method) {
window.history[method] = originalMethods[method];
});
canEvent.off.call(window, 'popstate', route.setState);
},
// ## matchingPartOfURL
// Returns matching part of url without root.
matchingPartOfURL: function () {
var root = cleanRoot(),
location = LOCATION(),
loc = (location.pathname + location.search),
index = loc.indexOf(root);
return loc.substr(index + root.length);
},
// ## setURL
// Updates URL by calling `pushState`.
setURL: function (path, newProps, oldProps) {
var method = "pushState";
var changed;
// Keeps hash if not in path.
if (includeHash && path.indexOf("#") === -1 && window.location.hash) {
path += window.location.hash;
}
changed = diffObject(oldProps, newProps)
.map(function (d) {
return d.property;
});
if(replaceStateAttrs.length > 0) {
var toRemove = [];
for(var i = 0, l = changed.length; i < l; i++) {
if(replaceStateAttrs.indexOf(changed[i]) !== -1) {
method = "replaceState";
}
if(replaceStateAttrs.once && (replaceStateAttrs.once.indexOf(changed[i]) !== -1)) {
toRemove.push(changed[i]);
}
}
if(toRemove.length > 0) {
removeAttrs(replaceStateAttrs, toRemove);
removeAttrs(replaceStateAttrs.once, toRemove);
}
}
window.history[method](null, null, route._call("root") + path);
}
function PushstateObservable(options) {
/*
* - replaceStateKeys
* - replaceStateOnceKeys
*/
this.options = options;
this.dispatchHandlers = this.dispatchHandlers.bind(this);
var self = this;
this.anchorClickHandler = function(event){
PushstateObservable.prototype.anchorClickHandler.call(self, this, event);
};
this.handlers = new KeyTree([Object,Array],{
onFirst: this.setup.bind(this),
onEmpty: this.teardown.bind(this)
});
}
PushstateObservable.prototype = Object.create(SimpleObservable.prototype);
PushstateObservable.constructor = PushstateObservable;
canReflect.assign(PushstateObservable.prototype,{
/**
* @property {String} can-route-pushstate.root root
* @parent can-route-pushstate.static
*
* @description Configure the base url that will not be modified.
*
* @option {String} Represents the base url that pushstate will prepend to all
* routes. `root` defaults to: `"/"`.
*
* @body
*
* ## Use
*
* By default, a route like:
*
* route(":type/:id")
*
* Matches urls like:
*
* http://domain.com/contact/5
*
* But sometimes, you only want to match pages within a certain directory. For
* example, an application that is a filemanager. You might want to
* specify root and routes like:
*
* route.pushstate.root = "/filemanager/"
* route("file-:fileId");
* route("folder-:fileId")
*
* Which matches urls like:
*
* http://domain.com/filemanager/file-34234
*
*/
// ## anchorClickHandler
// Start of `location.pathname` is the root.
// (Can be configured via `route.bindings.pushstate.root`)
root: "/",
// don't greedily match slashes in routing rules
matchSlashes: false,
paramsMatcher: /^\?(?:[^=]+=[^&]*&)*[^=]+=[^&]*/,
querySeparator: '?',
dispatchHandlers: function() {
var old = this.value;
this.value = getCurrentUrl();
if(old !== this.value) {
queues.enqueueByQueue(this.handlers.getNode([]), this, [this.value, old]
//!steal-remove-start
/* jshint laxcomma: true */
, null
, [ canReflect.getName(this), "changed to", this.value, "from", old ]
/* jshint laxcomma: false */
//!steal-remove-end
);
}
},
anchorClickHandler: function (node, e) {
// Handler function for `click` events.
var anchorClickHandler = function (e) {
if (!(e.isDefaultPrevented ? e.isDefaultPrevented() : e.defaultPrevented === true)) {
// YUI calls back events triggered with this as a wrapped object.
var node = this._node || this;
// Fix for IE showing blank host, but blank host means current host.

@@ -185,2 +152,3 @@ var linksHost = node.host || window.location.host;

var root = cleanRoot();
// And the link is within the `root`
if (node.pathname.indexOf(root) === 0) {

@@ -192,5 +160,12 @@

var curParams = route.deparam(url);
// If we've matched a route
if (curParams.hasOwnProperty('route')) {
// Makes it possible to have a link with a hash.
includeHash = true;
// Calling .pushState will dispatch events, causing
// `can-route` to update its data, and then try to set back
// the url without the hash. We need to retain that.
if(node.href.indexOf("#") >= 0 ) {
this.keepHash = true;
}
window.history.pushState(null, null, node.href);

@@ -209,32 +184,109 @@

},
setup: function(){
if(isNode()) {
return;
}
this.value = getCurrentUrl();
// Intercept routable links.
domEvents.addDelegateListener.call(document.documentElement, 'click', 'a', this.anchorClickHandler);
var originalMethods = this.originalMethods = {};
var dispatchHandlers = this.dispatchHandlers;
// Rewrites original `pushState`/`replaceState` methods on `history` and keeps pointer to original methods
each(methodsToOverwrite, function (method) {
this.originalMethods[method] = window.history[method];
window.history[method] = function (state, title, url) {
// Avoid doubled history states (with pushState).
var absolute = url.indexOf("http") === 0;
var loc = LOCATION();
var searchHash = loc.search + loc.hash;
// If url differs from current call original histoy method and update `route` state.
if ((!absolute && url !== loc.pathname + searchHash) ||
(absolute && url !== loc.href + searchHash)) {
originalMethods[method].apply(window.history, arguments);
dispatchHandlers();
}
};
}, this);
// ## cleanRoot
// Bind to `popstate` event, fires on back/forward.
domEvents.addEventListener.call(window, 'popstate', this.dispatchHandlers);
},
teardown: function(){
domEvents.removeEventListener.call(document.documentElement, 'click', 'a', this.anchorClickHandler);
// Always returns clean root, without domain.
cleanRoot = function () {
var domain = location.protocol + "//" + location.host,
root = 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 = arr.indexOf(attrs[i])) !== -1) {
arr.splice(index, 1);
each(methodsToOverwrite, function (method) {
window.history[method] = this.originalMethods[method];
}, this);
domEvents.removeEventListener.call(window, 'popstate', this.dispatchHandlers);
},
get: getCurrentUrl,
set: function(path){
var newProps = route.deparam(path);
var oldProps = route.deparam(getCurrentUrl());
var method = "pushState";
var changed;
// Keeps hash if not in path.
if (this.keepHash && path.indexOf("#") === -1 && window.location.hash) {
path += window.location.hash;
}
changed = {};
diffObject(oldProps, newProps)
.forEach(function (patch) {
return changed[patch.property] = true;
});
// check if we should call replaceState or pushState
if( this.options.replaceStateKeys.length ) {
this.options.replaceStateKeys.forEach(function(replaceKey){
if(changed[replaceKey]) {
method = "replaceState";
}
});
}
if( this.options.replaceStateOnceKeys.length ) {
for(var i = this.options.replaceStateOnceKeys.length - 1; i >= 0; i--) {
var replaceOnceKey = this.options.replaceStateOnceKeys[i];
if(changed[replaceOnceKey]) {
method = "replaceState";
// remove so we don't do this again
this.options.replaceStateOnceKeys.splice(i,1);
}
}
},
// Original methods on `history` that will be overwritten
methodsToOverwrite = ['pushState', 'replaceState'],
// A place to store pointers to original `history` methods.
originalMethods = {},
// Used to tell setURL to include the hash because we clicked on a link.
includeHash = false,
// Attributes that will cause replaceState to be called
replaceStateAttrs = [];
}
window.history[method](null, null, bindingProxy.call("root") + path);
}
});
canReflect.assignSymbols(PushstateObservable.prototype,{
"can.getValue": PushstateObservable.prototype.get,
"can.setValue": PushstateObservable.prototype.set,
"can.onValue": PushstateObservable.prototype.on,
"can.offValue": PushstateObservable.prototype.off,
"can.isMapLike": false,
"can.valueHasDependencies": function(){
return true;
},
//!steal-remove-start
"can.getName": function() {
return "PushstateObservable<" + this.value + ">";
},
//!steal-remove-end
});
// Initialize plugin only if browser supports pushstate.
if (usePushStateRouting) {
var options = {
replaceStateOnceKeys: [],
replaceStateKeys: []
};
var pushstateBinding = new PushstateObservable(options);
// Registers itself within `route.bindings`.
route.bindings.pushstate = pushstateBinding;
// Enables plugin, by default `hashchange` binding is used.

@@ -245,15 +297,10 @@ route.defaultBinding = "pushstate";

replaceStateOn: function() {
var attrs = makeArray(arguments);
Array.prototype.push.apply(replaceStateAttrs, attrs);
canReflect.addValues( options.replaceStateKeys, canReflect.toArray(arguments) );
},
replaceStateOnce: function() {
var attrs = makeArray(arguments);
replaceStateAttrs.once = makeArray(replaceStateAttrs.once);
Array.prototype.push.apply(replaceStateAttrs.once, attrs);
route.replaceStateOn.apply(this, arguments);
canReflect.addValues( options.replaceStateOnceKeys, canReflect.toArray(arguments) );
},
replaceStateOff: function() {
var attrs = makeArray(arguments);
removeAttrs(replaceStateAttrs, attrs);
canReflect.removeValues( options.replaceStateKeys, canReflect.toArray(arguments) );
canReflect.removeValues( options.replaceStateOnceKeys, canReflect.toArray(arguments) );
}

@@ -263,2 +310,2 @@ });

module.exports = namespace.route = route;
module.exports = route;
{
"name": "can-route-pushstate",
"version": "3.2.0",
"version": "4.0.0-pre.1",
"description": "Pushstate for can-route",

@@ -16,5 +16,4 @@ "homepage": "https://canjs.com",

"scripts": {
"preversion": "npm test && npm run build",
"version": "git commit -am \"Update dist for release\" && git checkout -b release && git add -f dist/",
"postversion": "git push --tags && git checkout master && git branch -D release && git push",
"preversion": "npm test",
"postpublish": "git push --tags && git push",
"testee": "testee test/test.html --browsers firefox",

@@ -37,11 +36,14 @@ "test": "npm run detect-cycle && npm run jshint && npm run testee",

"dependencies": {
"can-event": "^3.5.0",
"can-globals": "^0.2.3",
"can-route": "^3.1.0",
"can-dom-events": "^1.0.0",
"can-globals": "<2.0.0",
"can-key-tree": "<2.0.0",
"can-queues": "<2.0.0",
"can-reflect": "^1.8.0",
"can-route": "^4.0.0-pre.5",
"can-simple-observable": "^2.0.0-pre.21",
"can-util": "^3.9.0"
},
"devDependencies": {
"bit-docs": "0.0.8-0",
"can-define": "^1.2.0",
"can-map": "^3.1.0",
"can-define": "^2.0.0-pre.0",
"can-map": "^4.0.0-pre.0",
"detect-cyclic-packages": "^1.1.0",

@@ -48,0 +50,0 @@ "jshint": "^2.9.1",

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc