123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916 |
- /*! 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.status<cb.LOADED?e._waitings[a.uri]=(e._waitings[a.uri]||0)+1:a._remain--;if(0===a._remain)return a.onload(),b;var g={};for(f=0;d>f;f++)e=Z[c[f]],e.status<cb.FETCHING?e.fetch(g):e.status===cb.SAVED&&e.load();for(var h in g)g.hasOwnProperty(h)&&g[h]()}},t.prototype.onload=function(){var a=this;a.status=cb.LOADED,a.callback&&a.callback();var b=a._waitings,c,d;for(c in b)b.hasOwnProperty(c)&&(d=Z[c],d._remain-=b[c],0===d._remain&&d.onload());delete a._waitings,delete a._remain},t.prototype.fetch=function(a){function c(){u.request(g.requestUri,g.onRequest,g.charset,g.crossorigin)}function d(){delete _[h],ab[h]=!0,$&&(t.save(f,$),$=null);var a,b=bb[h];for(delete bb[h];a=b.shift();)a.load()}var e=this,f=e.uri;e.status=cb.FETCHING;var g={uri:f};D("fetch",g);var h=g.requestUri||f;return!h||ab[h]?(e.load(),b):_[h]?(bb[h].push(e),b):(_[h]=!0,bb[h]=[e],D("request",g={uri:f,requestUri:h,onRequest:d,charset:z(v.charset)?v.charset(h):v.charset,crossorigin:z(v.crossorigin)?v.crossorigin(h):v.crossorigin}),g.requested||(a?a[g.requestUri]=c:c()),b)},t.prototype.exec=function(){function a(b){return t.get(a.resolve(b)).exec()}var c=this;if(c.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.status<cb.SAVED&&(c.id=b.id||a,c.dependencies=b.deps||[],c.factory=b.factory,c.status=cb.SAVED)},t.get=function(a,b){return Z[a]||(Z[a]=new t(a,b))},t.use=function(b,c,d){var e=t.get(d,y(b)?b:[b]);e.callback=function(){for(var b=[],d=e.resolve(),f=0,g=d.length;g>f;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<cb.EXECUTING&&(b.onload(),b.exec()),b.exports};var db=/^(.+?\/)(\?\?)?(seajs\/)+/;v.base=(Q.match(db)||["",Q])[1],v.dir=Q,v.cwd=N,v.charset="utf-8",v.preload=function(){var a=[],b=location.search.replace(/(seajs-\w+)(&|$)/g,"$1=1$2");return b+=" "+M.cookie,b.replace(/(seajs-\w+)=1/g,function(b,c){a.push(c)}),a}(),u.config=function(a){for(var b in a){var c=a[b],d=v[b];if(d&&w(d))for(var e in c)d[e]=c[e];else y(d)?c=d.concat(c):"base"===b&&("/"!==c.slice(-1)&&(c+="/"),c=l(c)),v[b]=c}return D("config",a),u}}}(this);
- window.require = window.define = window.exports = window.module = undefined;
- /*!
- * v2.0.1
- *
- * Copyright 2012, Chris Wanstrath
- * Released under the MIT License
- * https://github.com/defunkt/jquery-pjax
- */
- (function($){
- // When called on a container with a selector, fetches the href with
- // ajax into the container or with the data-pjax attribute on the link
- // itself.
- //
- // Tries to make sure the back button and ctrl+click work the way
- // you'd expect.
- //
- // Exported as $.fn.pjax
- //
- // Accepts a jQuery ajax options object that may include these
- // pjax specific options:
- //
- //
- // container - String selector for the element where to place the response body.
- // push - Whether to pushState the URL. Defaults to true (of course).
- // replace - Want to use replaceState instead? That's cool.
- //
- // For convenience the second parameter can be either the container or
- // the options object.
- //
- // Returns the jQuery object
- function fnPjax(selector, container, options) {
- options = optionsFor(container, options);
- return this.on('click.pjax', selector, function(event) {
- var opts = options;
- if (!opts.container) {
- opts = $.extend({}, options);
- opts.container = $(this).attr('data-pjax')
- }
- handleClick(event, opts)
- })
- }
- // Public: pjax on click handler
- //
- // Exported as $.pjax.click.
- //
- // event - "click" jQuery.Event
- // options - pjax options
- //
- // Examples
- //
- // $(document).on('click', 'a', $.pjax.click)
- // // is the same as
- // $(document).pjax('a')
- //
- // Returns nothing.
- function handleClick(event, container, options) {
- options = optionsFor(container, options);
- var link = event.currentTarget;
- var $link = $(link);
- if (link.tagName.toUpperCase() !== 'A')
- throw "$.fn.pjax or $.pjax.click requires an anchor element";
- // Middle click, cmd click, and ctrl click should open
- // links in a new tab as normal.
- if ( event.which > 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 = $('<form>', {
- method: method === 'GET' ? 'GET' : 'POST',
- action: url,
- style: 'display:none'
- });
- if (method !== 'GET' && method !== 'POST') {
- form.append($('<input>', {
- 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($('<input>', {type: 'hidden', name: pair[0], value: pair[1]}))
- })
- } else if ($.isArray(data)) {
- $.each(data, function(index, value) {
- form.append($('<input>', {type: 'hidden', name: value.name, value: value.value}))
- })
- } else if (typeof data === 'object') {
- var key;
- for (key in data)
- form.append($('<input>', {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 <title> 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);
|