/*! Sea.js 2.2.3 | seajs.org/LICENSE.md */ !function(a,b){function c(a){return function(b){return{}.toString.call(b)=="[object "+a+"]"}}function d(){return B++}function e(a){return a.match(E)[0]}function f(a){for(a=a.replace(F,"/");a.match(G);)a=a.replace(G,"/");return a=a.replace(H,"$1/")}function g(a){var b=a.length-1,c=a.charAt(b);return"#"===c?a.substring(0,b):".js"===a.substring(b-2)||a.indexOf("?")>0||".css"===a.substring(b-3)||"/"===c?a:a+".js"}function h(a){var b=v.alias;return b&&x(b[a])?b[a]:a}function i(a){var b=v.paths,c;return b&&(c=a.match(I))&&x(b[c[1]])&&(a=b[c[1]]+c[2]),a}function j(a){var b=v.vars;return b&&a.indexOf("{")>-1&&(a=a.replace(J,function(a,c){return x(b[c])?b[c]:a})),a}function k(a){var b=v.map,c=a;if(b)for(var d=0,e=b.length;e>d;d++){var f=b[d];if(c=z(f)?f(a)||a:a.replace(f[0],f[1]),c!==a)break}return c}function l(a,b){var c,d=a.charAt(0);if(K.test(a))c=a;else if("."===d)c=f((b?e(b):v.cwd)+a);else if("/"===d){var g=v.cwd.match(L);c=g?g[0]+a.substring(1):a}else c=v.base+a;return 0===c.indexOf("//")&&(c=location.protocol+c),c}function m(a,b){if(!a)return"";a=h(a),a=i(a),a=j(a),a=g(a);var c=l(a,b);return c=k(c)}function n(a){return a.hasAttribute?a.src:a.getAttribute("src",4)}function o(a,b,c,d){var e=T.test(a),f=M.createElement(e?"link":"script");c&&(f.charset=c),A(d)||f.setAttribute("crossorigin",d),p(f,b,e,a),e?(f.rel="stylesheet",f.href=a):(f.async=!0,f.src=a),U=f,S?R.insertBefore(f,S):R.appendChild(f),U=null}function p(a,c,d,e){function f(){a.onload=a.onerror=a.onreadystatechange=null,d||v.debug||R.removeChild(a),a=null,c()}var g="onload"in a;return!d||!W&&g?(g?(a.onload=f,a.onerror=function(){D("error",{uri:e,node:a}),f()}):a.onreadystatechange=function(){/loaded|complete/.test(a.readyState)&&f()},b):(setTimeout(function(){q(a,c)},1),b)}function q(a,b){var c=a.sheet,d;if(W)c&&(d=!0);else if(c)try{c.cssRules&&(d=!0)}catch(e){"NS_ERROR_DOM_SECURITY_ERR"===e.name&&(d=!0)}setTimeout(function(){d?b():q(a,b)},20)}function r(){if(U)return U;if(V&&"interactive"===V.readyState)return V;for(var a=R.getElementsByTagName("script"),b=a.length-1;b>=0;b--){var c=a[b];if("interactive"===c.readyState)return V=c}}function s(a){var b=[];return a.replace(Y,"").replace(X,function(a,c,d){d&&b.push(d)}),b}function t(a,b){this.uri=a,this.dependencies=b||[],this.exports=null,this.status=0,this._waitings={},this._remain=0}if(!a.seajs){var u=a.seajs={version:"2.2.3"},v=u.data={},w=c("Object"),x=c("String"),y=Array.isArray||c("Array"),z=c("Function"),A=c("Undefined"),B=0,C=v.events={};u.on=function(a,b){var c=C[a]||(C[a]=[]);return c.push(b),u},u.off=function(a,b){if(!a&&!b)return C=v.events={},u;var c=C[a];if(c)if(b)for(var d=c.length-1;d>=0;d--)c[d]===b&&c.splice(d,1);else delete C[a];return u};var D=u.emit=function(a,b){var c=C[a],d;if(c)for(c=c.slice();d=c.shift();)d(b);return u},E=/[^?#]*\//,F=/\/\.\//g,G=/\/[^/]+\/\.\.\//,H=/([^:/])\/\//g,I=/^([^/:]+)(\/.+)$/,J=/{([^{]+)}/g,K=/^\/\/.|:\//,L=/^.*?\/\/.*?\//,M=document,N=e(M.URL),O=M.scripts,P=M.getElementById("seajsnode")||O[O.length-1],Q=e(n(P)||N);u.resolve=m;var R=M.head||M.getElementsByTagName("head")[0]||M.documentElement,S=R.getElementsByTagName("base")[0],T=/\.css(?:\?|$)/i,U,V,W=+navigator.userAgent.replace(/.*(?:AppleWebKit|AndroidWebKit)\/(\d+).*/,"$1")<536;u.request=o;var X=/"(?:\\"|[^"])*"|'(?:\\'|[^'])*'|\/\*[\S\s]*?\*\/|\/(?:\\\/|[^\/\r\n])+\/(?=[^\/])|\/\/.*|\.\s*require|(?:^|[^$])\brequire\s*\(\s*(["'])(.+?)\1\s*\)/g,Y=/\\\\/g,Z=u.cache={},$,_={},ab={},bb={},cb=t.STATUS={FETCHING:1,SAVED:2,LOADING:3,LOADED:4,EXECUTING:5,EXECUTED:6};t.prototype.resolve=function(){for(var a=this,b=a.dependencies,c=[],d=0,e=b.length;e>d;d++)c[d]=t.resolve(b[d],a.uri);return c},t.prototype.load=function(){var a=this;if(!(a.status>=cb.LOADING)){a.status=cb.LOADING;var c=a.resolve();D("load",c);for(var d=a._remain=c.length,e,f=0;d>f;f++)e=t.get(c[f]),e.statusf;f++)e=Z[c[f]],e.status=cb.EXECUTING)return c.exports;c.status=cb.EXECUTING;var e=c.uri;a.resolve=function(a){return t.resolve(a,e)},a.async=function(b,c){return t.use(b,c,e+"_async_"+d()),a};var f=c.factory,g=z(f)?f(a,c.exports={},c):f;return g===b&&(g=c.exports),delete c.factory,c.exports=g,c.status=cb.EXECUTED,D("exec",c),g},t.resolve=function(a,b){var c={id:a,refUri:b};return D("resolve",c),c.uri||u.resolve(c.id,b)},t.define=function(a,c,d){var e=arguments.length;1===e?(d=a,a=b):2===e&&(d=c,y(a)?(c=a,a=b):c=b),!y(c)&&z(d)&&(c=s(""+d));var f={id:a,uri:t.resolve(a),deps:c,factory:d};if(!f.uri&&M.attachEvent){var g=r();g&&(f.uri=g.src)}D("define",f),f.uri?t.save(f.uri,f):$=f},t.save=function(a,b){var c=t.get(a);c.statusf;f++)b[f]=Z[d[f]].exec();c&&c.apply(a,b),delete e.callback},e.load()},t.preload=function(a){var b=v.preload,c=b.length;c?t.use(b,function(){b.splice(0,c),t.preload(a)},v.cwd+"_preload_"+d()):a()},u.use=function(a,b){return t.preload(function(){t.use(a,b,v.cwd+"_use_"+d())}),u},t.define.cmd={},a.define=t.define,u.Module=t,v.fetchedList=ab,v.cid=d,u.require=function(a){var b=t.get(t.resolve(a));return b.status 1 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey ) return; // Ignore cross origin links if ( location.protocol !== link.protocol || location.hostname !== link.hostname ) return; // Ignore case when a hash is being tacked on the current URL if ( link.href.indexOf('#') > -1 && stripHash(link) == stripHash(location) ) return; // Ignore event with default prevented if (event.isDefaultPrevented()) return; var defaults = { url: link.href, container: $link.attr('data-pjax'), target: link }; var opts = $.extend({}, defaults, options); var clickEvent = $.Event('pjax:click'); $link.trigger(clickEvent, [opts]); if (!clickEvent.isDefaultPrevented()) { pjax(opts); event.preventDefault(); $link.trigger('pjax:clicked', [opts]) } } // Public: pjax on form submit handler // // Exported as $.pjax.submit // // event - "click" jQuery.Event // options - pjax options // // Examples // // $(document).on('submit', 'form', function(event) { // $.pjax.submit(event, '[data-pjax-container]') // }) // // Returns nothing. function handleSubmit(event, container, options) { options = optionsFor(container, options); var form = event.currentTarget; var $form = $(form); if (form.tagName.toUpperCase() !== 'FORM') throw "$.pjax.submit requires a form element"; var defaults = { type: ($form.attr('method') || 'GET').toUpperCase(), url: $form.attr('action'), container: $form.attr('data-pjax'), target: form }; if (defaults.type !== 'GET' && window.FormData !== undefined) { defaults.data = new FormData(form); defaults.processData = false; defaults.contentType = false; } else { // Can't handle file uploads, exit if ($form.find(':file').length) { return } // Fallback to manually serializing the fields defaults.data = $form.serializeArray() } pjax($.extend({}, defaults, options)); event.preventDefault() } // Loads a URL with ajax, puts the response body inside a container, // then pushState()'s the loaded URL. // // Works just like $.ajax in that it accepts a jQuery ajax // settings object (with keys like url, type, data, etc). // // Accepts these extra keys: // // container - String selector for where to stick the response body. // push - Whether to pushState the URL. Defaults to true (of course). // replace - Want to use replaceState instead? That's cool. // // Use it just like $.ajax: // // var xhr = $.pjax({ url: this.href, container: '#main' }) // console.log( xhr.readyState ) // // Returns whatever $.ajax returns. function pjax(options) { options = $.extend(true, {}, $.ajaxSettings, pjax.defaults, options); if ($.isFunction(options.url)) { options.url = options.url() } var hash = parseURL(options.url).hash; var containerType = $.type(options.container); if (containerType !== 'string') { throw "expected string value for 'container' option; got " + containerType } var context = options.context = $(options.container); if (!context.length) { throw "the container selector '" + options.container + "' did not match anything" } // We want the browser to maintain two separate internal caches: one // for pjax'd partial page loads and one for normal page loads. // Without adding this secret parameter, some browsers will often // confuse the two. if (!options.data) options.data = {}; if ($.isArray(options.data)) { options.data.push({name: '_pjax', value: options.container}) } else { options.data._pjax = options.container } function fire(type, args, props) { if (!props) props = {}; props.relatedTarget = options.target; var event = $.Event(type, props); context.trigger(event, args); return !event.isDefaultPrevented() } var timeoutTimer; options.beforeSend = function(xhr, settings) { // No timeout for non-GET requests // Its not safe to request the resource again with a fallback method. if (settings.type !== 'GET') { settings.timeout = 0 } xhr.setRequestHeader('X-PJAX', 'true'); xhr.setRequestHeader('X-PJAX-Container', options.container); if (!fire('pjax:beforeSend', [xhr, settings])) return false; if (settings.timeout > 0) { timeoutTimer = setTimeout(function() { if (fire('pjax:timeout', [xhr, options])) xhr.abort('timeout') }, settings.timeout); // Clear timeout setting so jquerys internal timeout isn't invoked settings.timeout = 0 } var url = parseURL(settings.url); if (hash) url.hash = hash; options.requestUrl = stripInternalParams(url) }; options.complete = function(xhr, textStatus) { if (timeoutTimer) clearTimeout(timeoutTimer); fire('pjax:complete', [xhr, textStatus, options]); fire('pjax:end', [xhr, options]) }; options.error = function(xhr, textStatus, errorThrown) { var container = extractContainer("", xhr, options); var allowed = fire('pjax:error', [xhr, textStatus, errorThrown, options]); if (options.type == 'GET' && textStatus !== 'abort' && allowed) { locationReplace(container.url) } }; options.success = function(data, status, xhr) { var previousState = pjax.state; // If $.pjax.defaults.version is a function, invoke it first. // Otherwise it can be a static string. var currentVersion = typeof $.pjax.defaults.version === 'function' ? $.pjax.defaults.version() : $.pjax.defaults.version; var latestVersion = xhr.getResponseHeader('X-PJAX-Version'); var container = extractContainer(data, xhr, options); var url = parseURL(container.url); if (hash) { url.hash = hash; container.url = url.href } // If there is a layout version mismatch, hard load the new url if (currentVersion && latestVersion && currentVersion !== latestVersion) { locationReplace(container.url); return } // If the new response is missing a body, hard load the page if (!container.contents) { locationReplace(container.url); return } pjax.state = { id: options.id || uniqueId(), url: container.url, title: container.title, container: options.container, fragment: options.fragment, timeout: options.timeout }; if (options.push || options.replace) { window.history.replaceState(pjax.state, container.title, container.url) } // Only blur the focus if the focused element is within the container. var blurFocus = $.contains(context, document.activeElement); // Clear out any focused controls before inserting new page contents. if (blurFocus) { try { document.activeElement.blur() } catch (e) { /* ignore */ } } if (container.title) document.title = container.title; fire('pjax:beforeReplace', [container.contents, options], { state: pjax.state, previousState: previousState }); context.html(container.contents); // FF bug: Won't autofocus fields that are inserted via JS. // This behavior is incorrect. So if theres no current focus, autofocus // the last field. // // http://www.w3.org/html/wg/drafts/html/master/forms.html var autofocusEl = context.find('input[autofocus], textarea[autofocus]').last()[0]; if (autofocusEl && document.activeElement !== autofocusEl) { autofocusEl.focus() } executeStyleTags(container.styles); executeScriptTags(container.scripts); var scrollTo = options.scrollTo; // Ensure browser scrolls to the element referenced by the URL anchor if (hash) { var name = decodeURIComponent(hash.slice(1)); var target = document.getElementById(name) || document.getElementsByName(name)[0]; if (target) scrollTo = $(target).offset().top } if (typeof scrollTo == 'number') $(window).scrollTop(scrollTo); fire('pjax:success', [data, status, xhr, options]) }; function executeScriptTags(scripts) { if (!scripts) return; var urls = []; scripts.each(function() { urls.unshift($(this).attr('src')); }); loadAssets(urls, true); } function executeStyleTags(styles) { if (!styles) return; var hrefs = []; styles.each(function() { hrefs.unshift($(this).attr('href')); }); loadAssets(hrefs); } // 按顺序加载静态资源 function loadAssets(urls, trigger) { if (urls.length < 1) { if (trigger) { fire('pjax:loaded'); } return; } seajs.use([urls.pop()], function () { loadAssets(urls, trigger); }); } // Initialize pjax.state for the initial page load. Assume we're // using the container and options of the link we're loading for the // back button to the initial page. This ensures good back button // behavior. if (!pjax.state) { pjax.state = { id: uniqueId(), url: window.location.href, title: document.title, container: options.container, fragment: options.fragment, timeout: options.timeout }; window.history.replaceState(pjax.state, document.title) } // Cancel the current request if we're already pjaxing abortXHR(pjax.xhr); pjax.options = options; var xhr = pjax.xhr = $.ajax(options); if (xhr.readyState > 0) { if (options.push && !options.replace) { // Cache current container element before replacing it cachePush(pjax.state.id, [options.container, cloneContents(context)]); window.history.pushState(null, "", options.requestUrl) } fire('pjax:start', [xhr, options]); fire('pjax:send', [xhr, options]) } return pjax.xhr } // Public: Reload current page with pjax. // // Returns whatever $.pjax returns. function pjaxReload(container, options) { var defaults = { url: window.location.href, push: false, replace: true, scrollTo: false }; return pjax($.extend(defaults, optionsFor(container, options))) } // Internal: Hard replace current state with url. // // Work for around WebKit // https://bugs.webkit.org/show_bug.cgi?id=93506 // // Returns nothing. function locationReplace(url) { window.history.replaceState(null, "", pjax.state.url); window.location.replace(url) } var initialPop = true; var initialURL = window.location.href; var initialState = window.history.state; // Initialize $.pjax.state if possible // Happens when reloading a page and coming forward from a different // session history. if (initialState && initialState.container) { pjax.state = initialState } // Non-webkit browsers don't fire an initial popstate event if ('state' in window.history) { initialPop = false } // popstate handler takes care of the back and forward buttons // // You probably shouldn't use pjax on pages with other pushState // stuff yet. function onPjaxPopstate(event) { // Hitting back or forward should override any pending PJAX request. if (!initialPop) { abortXHR(pjax.xhr) } var previousState = pjax.state; var state = event.state; var direction; if (state && state.container) { // When coming forward from a separate history session, will get an // initial pop with a state we are already at. Skip reloading the current // page. if (initialPop && initialURL == state.url) return; if (previousState) { // If popping back to the same state, just skip. // Could be clicking back from hashchange rather than a pushState. if (previousState.id === state.id) return; // Since state IDs always increase, we can deduce the navigation direction direction = previousState.id < state.id ? 'forward' : 'back' } var cache = cacheMapping[state.id] || []; var containerSelector = cache[0] || state.container; var container = $(containerSelector), contents = cache[1]; if (container.length) { if (previousState) { // Cache current container before replacement and inform the // cache which direction the history shifted. cachePop(direction, previousState.id, [containerSelector, cloneContents(container)]) } var popstateEvent = $.Event('pjax:popstate', { state: state, direction: direction }); container.trigger(popstateEvent); var options = { id: state.id, url: state.url, container: containerSelector, push: false, fragment: state.fragment, timeout: state.timeout, scrollTo: false }; if (contents) { container.trigger('pjax:start', [null, options]); pjax.state = state; if (state.title) document.title = state.title; var beforeReplaceEvent = $.Event('pjax:beforeReplace', { state: state, previousState: previousState }); container.trigger(beforeReplaceEvent, [contents, options]); container.html(contents); container.trigger('pjax:end', [null, options]) } else { pjax(options) } // Force reflow/relayout before the browser tries to restore the // scroll position. container[0].offsetHeight // eslint-disable-line no-unused-expressions } else { locationReplace(location.href) } } initialPop = false } // Fallback version of main pjax function for browsers that don't // support pushState. // // Returns nothing since it retriggers a hard form submission. function fallbackPjax(options) { var url = $.isFunction(options.url) ? options.url() : options.url, method = options.type ? options.type.toUpperCase() : 'GET'; var form = $('
', { method: method === 'GET' ? 'GET' : 'POST', action: url, style: 'display:none' }); if (method !== 'GET' && method !== 'POST') { form.append($('', { type: 'hidden', name: '_method', value: method.toLowerCase() })) } var data = options.data; if (typeof data === 'string') { $.each(data.split('&'), function(index, value) { var pair = value.split('='); form.append($('', {type: 'hidden', name: pair[0], value: pair[1]})) }) } else if ($.isArray(data)) { $.each(data, function(index, value) { form.append($('', {type: 'hidden', name: value.name, value: value.value})) }) } else if (typeof data === 'object') { var key; for (key in data) form.append($('', {type: 'hidden', name: key, value: data[key]})) } $(document.body).append(form); form.submit() } // Internal: Abort an XmlHttpRequest if it hasn't been completed, // also removing its event handlers. function abortXHR(xhr) { if ( xhr && xhr.readyState < 4) { xhr.onreadystatechange = $.noop; xhr.abort() } } // Internal: Generate unique id for state object. // // Use a timestamp instead of a counter since ids should still be // unique across page loads. // // Returns Number. function uniqueId() { return (new Date).getTime() } function cloneContents(container) { var cloned = container.clone(); // Unmark script tags as already being eval'd so they can get executed again // when restored from cache. HAXX: Uses jQuery internal method. cloned.find('script').each(function(){ if (!this.src) $._data(this, 'globalEval', false) }); return cloned.contents() } // Internal: Strip internal query params from parsed URL. // // Returns sanitized url.href String. function stripInternalParams(url) { url.search = url.search.replace(/([?&])(_pjax|_)=[^&]*/g, '').replace(/^&/, ''); return url.href.replace(/\?($|#)/, '$1') } // Internal: Parse URL components and returns a Locationish object. // // url - String URL // // Returns HTMLAnchorElement that acts like Location. function parseURL(url) { var a = document.createElement('a'); a.href = url; return a } // Internal: Return the `href` component of given URL object with the hash // portion removed. // // location - Location or HTMLAnchorElement // // Returns String function stripHash(location) { return location.href.replace(/#.*/, '') } // Internal: Build options Object for arguments. // // For convenience the first parameter can be either the container or // the options object. // // Examples // // optionsFor('#container') // // => {container: '#container'} // // optionsFor('#container', {push: true}) // // => {container: '#container', push: true} // // optionsFor({container: '#container', push: true}) // // => {container: '#container', push: true} // // Returns options Object. function optionsFor(container, options) { if (container && options) { options = $.extend({}, options); options.container = container; return options } else if ($.isPlainObject(container)) { return container } else { return {container: container} } } // Internal: Filter and find all elements matching the selector. // // Where $.fn.find only matches descendants, findAll will test all the // top level elements in the jQuery object as well. // // elems - jQuery object of Elements // selector - String selector to match // // Returns a jQuery object. function findAll(elems, selector) { return elems.filter(selector).add(elems.find(selector)) } function parseHTML(html) { return $.parseHTML(html, document, true) } // Internal: Extracts container and metadata from response. // // 1. Extracts X-PJAX-URL header if set // 2. Extracts inline tags // 3. Builds response Element and extracts fragment if set // // data - String response data // xhr - XHR response // options - pjax options Object // // Returns an Object with url, title, and contents keys. function extractContainer(data, xhr, options) { var obj = {}, fullDocument = /<html/i.test(data); // Prefer X-PJAX-URL header if it was set, otherwise fallback to // using the original requested url. var serverUrl = xhr.getResponseHeader('X-PJAX-URL'); obj.url = serverUrl ? stripInternalParams(parseURL(serverUrl)) : options.requestUrl; var $head, $body; // Attempt to parse response html into elements if (fullDocument) { $body = $(parseHTML(data.match(/<body[^>]*>([\s\S.]*)<\/body>/i)[0])); var head = data.match(/<head[^>]*>([\s\S.]*)<\/head>/i); $head = head != null ? $(parseHTML(head[0])) : $body } else { $head = $body = $(parseHTML(data)) } // If response data is empty, return fast if ($body.length === 0) return obj; // If there's a <title> tag in the header, use it as // the page's title. obj.title = findAll($head, 'title').last().text(); if (options.fragment) { var $fragment = $body; // If they specified a fragment, look for it in the response // and pull it out. if (options.fragment !== 'body') { $fragment = findAll($fragment, options.fragment).first() } if ($fragment.length) { obj.contents = options.fragment === 'body' ? $fragment : $fragment.contents(); // If there's no title, look for data-title and title attributes // on the fragment if (!obj.title) obj.title = $fragment.attr('title') || $fragment.data('title') } } else if (!fullDocument) { obj.contents = $body } // Clean up any <title> tags if (obj.contents) { // Remove any parent title elements obj.contents = obj.contents.not(function() { return $(this).is('title') }); // Then scrub any titles from their descendants obj.contents.find('title').remove(); // Gather all script[src] elements obj.scripts = findAll(obj.contents, 'script[src]').remove(); obj.contents = obj.contents.not(obj.scripts); // Collect all link[type="text/css"] elements obj.styles = findAll(obj.contents, 'link[type="text/css"]').remove(); obj.contents = obj.contents.not(obj.styles); } // Trim any whitespace off the title if (obj.title) obj.title = $.trim(obj.title); return obj } // Internal: History DOM caching class. var cacheMapping = {}; var cacheForwardStack = []; var cacheBackStack = []; // Push previous state id and container contents into the history // cache. Should be called in conjunction with `pushState` to save the // previous container contents. // // id - State ID Number // value - DOM Element to cache // // Returns nothing. function cachePush(id, value) { cacheMapping[id] = value; cacheBackStack.push(id); // Remove all entries in forward history stack after pushing a new page. trimCacheStack(cacheForwardStack, 0); // Trim back history stack to max cache length. trimCacheStack(cacheBackStack, pjax.defaults.maxCacheLength) } // Shifts cache from directional history cache. Should be // called on `popstate` with the previous state id and container // contents. // // direction - "forward" or "back" String // id - State ID Number // value - DOM Element to cache // // Returns nothing. function cachePop(direction, id, value) { var pushStack, popStack; cacheMapping[id] = value; if (direction === 'forward') { pushStack = cacheBackStack; popStack = cacheForwardStack } else { pushStack = cacheForwardStack; popStack = cacheBackStack } pushStack.push(id); id = popStack.pop(); if (id) delete cacheMapping[id]; // Trim whichever stack we just pushed to to max cache length. trimCacheStack(pushStack, pjax.defaults.maxCacheLength) } // Trim a cache stack (either cacheBackStack or cacheForwardStack) to be no // longer than the specified length, deleting cached DOM elements as necessary. // // stack - Array of state IDs // length - Maximum length to trim to // // Returns nothing. function trimCacheStack(stack, length) { while (stack.length > length) delete cacheMapping[stack.shift()] } // Public: Find version identifier for the initial page load. // // Returns String version or undefined. function findVersion() { return $('meta').filter(function() { var name = $(this).attr('http-equiv'); return name && name.toUpperCase() === 'X-PJAX-VERSION' }).attr('content') } // Install pjax functions on $.pjax to enable pushState behavior. // // Does nothing if already enabled. // // Examples // // $.pjax.enable() // // Returns nothing. function enable() { $.fn.pjax = fnPjax; $.pjax = pjax; $.pjax.enable = $.noop; $.pjax.disable = disable; $.pjax.click = handleClick; $.pjax.submit = handleSubmit; $.pjax.reload = pjaxReload; $.pjax.defaults = { timeout: 650, push: true, replace: false, type: 'GET', dataType: 'html', scrollTo: 0, maxCacheLength: 20, version: findVersion }; $(window).on('popstate.pjax', onPjaxPopstate) } // Disable pushState behavior. // // This is the case when a browser doesn't support pushState. It is // sometimes useful to disable pushState for debugging on a modern // browser. // // Examples // // $.pjax.disable() // // Returns nothing. function disable() { $.fn.pjax = function() { return this }; $.pjax = fallbackPjax; $.pjax.enable = enable; $.pjax.disable = $.noop; $.pjax.click = $.noop; $.pjax.submit = $.noop; $.pjax.reload = function() { window.location.reload() }; $(window).off('popstate.pjax', onPjaxPopstate) } // Add the state property to jQuery's event object so we can use it in // $(window).bind('popstate') if ($.event.props && $.inArray('state', $.event.props) < 0) { $.event.props.push('state') } else if (!('state' in $.Event.prototype)) { $.event.addProp('state') } // Is pjax supported by this browser? $.support.pjax = window.history && window.history.pushState && window.history.replaceState && // pushState isn't reliable on iOS until 5. !navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]\D|WebApps\/.+CFNetwork)/); if ($.support.pjax) { enable() } else { disable() } })(jQuery);