/*jshint maxdepth:6, scripturl:true*/
steal('can/util', 'can/route', function (can) {
	"use strict";
	var hasPushstate = window.history && window.history.pushState;
	var isFileProtocol = window.location && window.location.protocol === 'file:';Plugin for can.route which uses browser history.pushState support
to update window’s pathname instead of the hash.
It registers itself as binding on can.route, intercepts click events
on <a> elements across document and accordingly updates can.route state
and window’s pathname.
/*jshint maxdepth:6, scripturl:true*/
steal('can/util', 'can/route', function (can) {
	"use strict";
	var hasPushstate = window.history && window.history.pushState;
	var isFileProtocol = window.location && window.location.protocol === 'file:';Initialize plugin only if browser supports pushstate.
	if (!isFileProtocol && hasPushstate) {Registers itself within can.route.bindings.
		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
			 *
			 */Start of location.pathname is the root.
(Can be configured via can.route.bindings.pushstate.root)
			root: "/",don’t greedily match slashes in routing rules
			matchSlashes: false,
			paramsMatcher: /^\?(?:[^=]+=[^&]*&)*[^=]+=[^&]*/,
			querySeparator: '?',Intercepts clicks on <a> elements and rewrites original history methods.
			bind: function () {
				if(can.isNode) {
					return;
				}Intercept routable links.
				can.delegate.call(can.$(document.documentElement), 'a', 'click', anchorClickHandler);Rewrites original pushState/replaceState methods on history and keeps pointer to original methods
				can.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 searchHash = window.location.search + window.location.hash;If url differs from current call original histoy method and update can.route state.
						if ((!absolute && url !== window.location.pathname + searchHash) || (absolute && url !== window.location.href + searchHash)) {
							originalMethods[method].apply(window.history, arguments);
							can.route.setState();
						}
					};
				});Bind to popstate event, fires on back/forward.
				can.bind.call(window, 'popstate', can.route.setState);
			},Unbinds and restores original history methods
			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);
			},Returns matching part of url without root.
			matchingPartOfURL: function () {
				var root = cleanRoot(),
					loc = (location.pathname + location.search),
					index = loc.indexOf(root);
				return loc.substr(index + root.length);
			},Updates URL by calling pushState.
			setURL: function (path, changed) {
				var method = "pushState";Keeps hash if not in path.
				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);
			}
		};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.
				var linksHost = node.host || window.location.host;
				if(node.href === "javascript://") {
					return;
				}If link is within the same domain and descendant of root
				if (window.location.host === linksHost) {
					var root = cleanRoot();
					if (node.pathname.indexOf(root) === 0) {Removes root from url.
						var url = (node.pathname + node.search).substr(root.length);If a route matches update the data.
						var curParams = can.route.deparam(url);
						if (curParams.hasOwnProperty('route')) {Makes it possible to have a link with a hash.
							includeHash = true;
							window.history.pushState(null, null, node.href);Test if you can preventDefault our tests can’t call .click() b/c this freezes phantom.
							if (e.preventDefault) {
								e.preventDefault();
							}
						}
					}
				}
			}
		},Always returns clean root, without domain.
			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);
					}
				}
			},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 = [];Enables plugin, by default hashchange binding is used.
		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;
});