/** * URL State manipulation utility * * Usage Example: * //urlState.strictMode = (function() { return !this; })(); * var queryString = urlState.search(); * queryString.name = 'sujeet'; * urlState.pathname('/result-page').search(queryString).update(); */ (function (win) { 'use strict'; var toType = function (val) { var type = Object.prototype.toString.call(val); return type.replace(/\[(\w+)\s(\w+)\]/, '$2').toLowerCase(); }; var parseQstr = function (queryString, coerce) { queryString = ('' + queryString).replace(/^\?/, ''); var re = /([^&=]+)=?([^&]*)/g, m, params = {}, coerceTypes = {'true': !0, 'false': !1, 'null': null}, decode = function (str) { return decodeURIComponent(str.replace(/\+/g, '%20')); }; if (queryString) { while (m = re.exec(queryString)) { var k = decode(m[1]), v = decode(m[2]), curr = params; if (coerce) { v = !isNaN(v) ? +v : (v === 'undefined' ? undefined : (coerceTypes[v] ? coerceTypes[v] : v)); } var keys = k.split(']['); var keysLast = keys.length - 1; if (/\[/.test(keys[0]) && /\]$/.test(keys[keysLast])) { keys[keysLast] = keys[keysLast].replace(/\]$/, ''); keys = keys.shift().split('[').concat(keys); keysLast = keys.length - 1; } if (keysLast) { for (var i = 0; i <= keysLast; i++) { k = (keys[i] === '') ? curr.length : keys[i]; curr = curr[k] = (i < keysLast) ? curr[k] || (keys[i+1] && isNaN(keys[i+1]) ? {} : []) : v; } } else if (params[k]) { if (toType(params[k]) === 'array') { params[k].push(v); } else { params[k] = [params[k], v]; } } else { params[k] = v; } } } return params; }; var createQstr = function (obj) { var key, s = []; var add = function (k, v) { v = typeof v === 'function' ? v() : v; v = (v === null || v === undefined) ? '' : v === undefined ? '' : v; s[s.length] = encodeURIComponent(k) + '=' + encodeURIComponent(v); }; var buildParams = function (prefix, obj) { var i, len, key, objectType = toType(obj); if (objectType === 'array') { for (i = 0, len = obj.length; i < len; i++) { buildParams(prefix + '[' + (typeof obj[i] === 'object' && obj[i] ? i : '') + ']', obj[i]); } } else if (objectType === 'object') { for (key in obj) { buildParams(prefix + '[' + key + ']', obj[key]); } } else { add(prefix, obj); } }; for (key in obj) { buildParams(key, obj[key]); } return s.join('&'); }; win.urlState = (function () { var init_state = win.history.state, init_title = win.document.title, init_loc = win.document.createElement('a'), wloc = win.document.createElement('a'); init_loc.href = win.location.href; wloc.href = win.location.href; return { strictMode: true, get protocol() { return wloc.protocol; }, set protocol(value) { if (this.strictMode) { throw new TypeError('"protocol" is read-only'); } }, get host() { return wloc.host; }, set host(value) { if (this.strictMode) { throw new TypeError('"host" is read-only'); } }, get hostname() { return wloc.hostname; }, set hostname(value) { if (this.strictMode) { throw new TypeError('"hostname" is read-only'); } }, get port() { return wloc.port; }, set port(value) { if (this.strictMode) { throw new TypeError('"port" is read-only'); } }, pathname: function (pathname) { if (pathname === undefined) { return wloc.pathname; } else { wloc.pathname = pathname; return this; } }, search: function (search) { if (search === undefined) { return parseQstr(wloc.search); } else { wloc.search = createQstr(search); return this; } }, hash: function (hash) { if (hash === undefined) { return wloc.hash; } else { wloc.hash = hash; return this; } }, update: function (title, replace) { win.history[replace?'replaceState':'pushState'](init_state, '', this.toString()); win.document.title = title || init_title; return this; }, reset: function () { wloc = init_loc; win.history.replaceState(init_state, '', this.toString()); win.document.title = init_title; return this; }, toString: function () { return wloc.toString(); }, }; })(); })(this);