UNPKG

todomvc

Version:

> Helping you select an MV\* framework

138 lines (128 loc) 4.53 kB
/*! * CanJS - 2.0.3 * http://canjs.us/ * Copyright (c) 2013 Bitovi * Tue, 26 Nov 2013 18:21:22 GMT * Licensed MIT * Includes: CanJS default build * Download from: http://canjs.us/ */ define(["can/util/library", "can/route"], function(can) { "use strict"; if(window.history && history.pushState) { can.route.bindings.pushstate = { /** * @property {String} can.route.pushstate.root * @parent can.route.pushstate * * @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: * * can.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: * * can.route.pushstate.root = "/filemanager/" * can.route("file-:fileId"); * can.route("folder-:fileId") * * Which matches urls like: * * http://domain.com/filemanager/file-34234 * */ root: "/", paramsMatcher: /^\?(?:[^=]+=[^&]*&)*[^=]+=[^&]*/, querySeparator: '?', bind: function() { // intercept routable links can.delegate.call(can.$(document.documentElement),'a', 'click', anchorClickFix); // popstate only fires on back/forward. // To detect when someone calls push/replaceState, we need to wrap each method. can.each(['pushState','replaceState'],function(method) { originalMethods[method] = window.history[method]; window.history[method] = function(state) { var result = originalMethods[method].apply(window.history, arguments); can.route.setState(); return result; }; }); // Bind to popstate for back/forward can.bind.call(window, 'popstate', can.route.setState); }, unbind: function(){ can.undelegate.call(can.$(document.documentElement),'click', 'a', anchorClickFix); can.each(['pushState','replaceState'],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) { // keep hash if not in path, but in if( includeHash && path.indexOf("#") == -1 && window.location.hash) { path += window.location.hash } window.history.pushState(null, null, can.route._call("root")+path); } } var anchorClickFix = 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. var linksHost = node.host || window.location.host; // if link is within the same domain if( window.location.host == linksHost ) { var curParams = can.route.deparam(node.pathname+node.search); // if a route matches if(curParams.hasOwnProperty('route')) { // make it possible to have a link with a hash includeHash = true; // update the data window.history.pushState(null, null, node.href); // test if you can preventDefault // our tests can't call .click() b/c this // freezes phantom 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 can.route.root.substr(domain.length) } return root }, // a collection of methods on history that we are overwriting originalMethods = {}, // used to tell setURL to include the hash because // we clicked on a link includeHash = false; can.route.defaultBinding = "pushstate"; } return can; });