Commit dd411f29dd0c1b9e3d752bfec8bd22045b463151

Authored by 潘钊
1 parent 44c7bbef

删除debugger

src/main/resources/static/assets/plugins/jquery.pjax.js
1 -/*!  
2 - * Copyright 2012, Chris Wanstrath  
3 - * Released under the MIT License  
4 - * https://github.com/defunkt/jquery-pjax  
5 - */  
6 -  
7 -(function($){  
8 -  
9 -// When called on a container with a selector, fetches the href with  
10 -// ajax into the container or with the data-pjax attribute on the link  
11 -// itself.  
12 -//  
13 -// Tries to make sure the back button and ctrl+click work the way  
14 -// you'd expect.  
15 -//  
16 -// Exported as $.fn.pjax  
17 -//  
18 -// Accepts a jQuery ajax options object that may include these  
19 -// pjax specific options:  
20 -//  
21 -//  
22 -// container - Where to stick the response body. Usually a String selector.  
23 -// $(container).html(xhr.responseBody)  
24 -// (default: current jquery context)  
25 -// push - Whether to pushState the URL. Defaults to true (of course).  
26 -// replace - Want to use replaceState instead? That's cool.  
27 -//  
28 -// For convenience the second parameter can be either the container or  
29 -// the options object.  
30 -//  
31 -// Returns the jQuery object  
32 -function fnPjax(selector, container, options) {  
33 - var context = this  
34 - return this.on('click.pjax', selector, function(event) {  
35 - var opts = $.extend({}, optionsFor(container, options))  
36 - if (!opts.container)  
37 - opts.container = $(this).attr('data-pjax') || context  
38 - handleClick(event, opts)  
39 - })  
40 -}  
41 -  
42 -// Public: pjax on click handler  
43 -//  
44 -// Exported as $.pjax.click.  
45 -//  
46 -// event - "click" jQuery.Event  
47 -// options - pjax options  
48 -//  
49 -// Examples  
50 -//  
51 -// $(document).on('click', 'a', $.pjax.click)  
52 -// // is the same as  
53 -// $(document).pjax('a')  
54 -//  
55 -// $(document).on('click', 'a', function(event) {  
56 -// var container = $(this).closest('[data-pjax-container]')  
57 -// $.pjax.click(event, container)  
58 -// })  
59 -//  
60 -// Returns nothing.  
61 -function handleClick(event, container, options) {  
62 - options = optionsFor(container, options)  
63 -  
64 - var link = event.currentTarget  
65 -  
66 - if (link.tagName.toUpperCase() !== 'A')  
67 - throw "$.fn.pjax or $.pjax.click requires an anchor element"  
68 -  
69 - // Middle click, cmd click, and ctrl click should open  
70 - // links in a new tab as normal.  
71 - if ( event.which > 1 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey )  
72 - return  
73 -  
74 - // Ignore cross origin links  
75 - if ( location.protocol !== link.protocol || location.hostname !== link.hostname )  
76 - return  
77 -  
78 - // Ignore case when a hash is being tacked on the current URL  
79 - if ( link.href.indexOf('#') > -1 && stripHash(link) == stripHash(location) )  
80 - return  
81 -  
82 - // Ignore event with default prevented  
83 - if (event.isDefaultPrevented())  
84 - return  
85 -  
86 - var defaults = {  
87 - url: link.href,  
88 - container: $(link).attr('data-pjax'),  
89 - target: link  
90 - }  
91 -  
92 - var opts = $.extend({}, defaults, options)  
93 - var clickEvent = $.Event('pjax:click')  
94 - $(link).trigger(clickEvent, [opts])  
95 -  
96 - if (!clickEvent.isDefaultPrevented()) {  
97 - pjax(opts)  
98 - event.preventDefault()  
99 - $(link).trigger('pjax:clicked', [opts])  
100 - }  
101 -}  
102 -  
103 -// Public: pjax on form submit handler  
104 -//  
105 -// Exported as $.pjax.submit  
106 -//  
107 -// event - "click" jQuery.Event  
108 -// options - pjax options  
109 -//  
110 -// Examples  
111 -//  
112 -// $(document).on('submit', 'form', function(event) {  
113 -// var container = $(this).closest('[data-pjax-container]')  
114 -// $.pjax.submit(event, container)  
115 -// })  
116 -//  
117 -// Returns nothing.  
118 -function handleSubmit(event, container, options) {  
119 - options = optionsFor(container, options)  
120 -  
121 - var form = event.currentTarget  
122 -  
123 - if (form.tagName.toUpperCase() !== 'FORM')  
124 - throw "$.pjax.submit requires a form element"  
125 -  
126 - var defaults = {  
127 - type: form.method.toUpperCase(),  
128 - url: form.action,  
129 - container: $(form).attr('data-pjax'),  
130 - target: form  
131 - }  
132 -  
133 - if (defaults.type !== 'GET' && window.FormData !== undefined) {  
134 - defaults.data = new FormData(form);  
135 - defaults.processData = false;  
136 - defaults.contentType = false;  
137 - } else {  
138 - // Can't handle file uploads, exit  
139 - if ($(form).find(':file').length) {  
140 - return;  
141 - }  
142 -  
143 - // Fallback to manually serializing the fields  
144 - defaults.data = $(form).serializeArray();  
145 - }  
146 -  
147 - pjax($.extend({}, defaults, options))  
148 -  
149 - event.preventDefault()  
150 -}  
151 -  
152 -// Loads a URL with ajax, puts the response body inside a container,  
153 -// then pushState()'s the loaded URL.  
154 -//  
155 -// Works just like $.ajax in that it accepts a jQuery ajax  
156 -// settings object (with keys like url, type, data, etc).  
157 -//  
158 -// Accepts these extra keys:  
159 -//  
160 -// container - Where to stick the response body.  
161 -// $(container).html(xhr.responseBody)  
162 -// push - Whether to pushState the URL. Defaults to true (of course).  
163 -// replace - Want to use replaceState instead? That's cool.  
164 -//  
165 -// Use it just like $.ajax:  
166 -//  
167 -// var xhr = $.pjax({ url: this.href, container: '#main' })  
168 -// console.log( xhr.readyState )  
169 -//  
170 -// Returns whatever $.ajax returns.  
171 -function pjax(options) {  
172 - options = $.extend(true, {}, $.ajaxSettings, pjax.defaults, options)  
173 -  
174 - if ($.isFunction(options.url)) {  
175 - options.url = options.url()  
176 - }  
177 -  
178 - var target = options.target  
179 -  
180 - var hash = parseURL(options.url).hash  
181 -  
182 - var context = options.context = findContainerFor(options.container)  
183 -  
184 - // We want the browser to maintain two separate internal caches: one  
185 - // for pjax'd partial page loads and one for normal page loads.  
186 - // Without adding this secret parameter, some browsers will often  
187 - // confuse the two.  
188 - if (!options.data) options.data = {}  
189 - if ($.isArray(options.data)) {  
190 - options.data.push({name: '_pjax', value: context.selector})  
191 - } else {  
192 - options.data._pjax = context.selector  
193 - }  
194 -  
195 - function fire(type, args, props) {  
196 - if (!props) props = {}  
197 - props.relatedTarget = target  
198 - var event = $.Event(type, props)  
199 - context.trigger(event, args)  
200 - return !event.isDefaultPrevented()  
201 - }  
202 -  
203 - var timeoutTimer  
204 -  
205 - options.beforeSend = function(xhr, settings) {  
206 - // No timeout for non-GET requests  
207 - // Its not safe to request the resource again with a fallback method.  
208 - if (settings.type !== 'GET') {  
209 - settings.timeout = 0  
210 - }  
211 -  
212 - xhr.setRequestHeader('X-PJAX', 'true')  
213 - xhr.setRequestHeader('X-PJAX-Container', context.selector)  
214 -  
215 - if (!fire('pjax:beforeSend', [xhr, settings]))  
216 - return false  
217 -  
218 - if (settings.timeout > 0) {  
219 - timeoutTimer = setTimeout(function() {  
220 - if (fire('pjax:timeout', [xhr, options]))  
221 - xhr.abort('timeout')  
222 - }, settings.timeout)  
223 -  
224 - // Clear timeout setting so jquerys internal timeout isn't invoked  
225 - settings.timeout = 0  
226 - }  
227 -  
228 - var url = parseURL(settings.url)  
229 - if (hash) url.hash = hash  
230 - options.requestUrl = stripInternalParams(url)  
231 - }  
232 -  
233 - options.complete = function(xhr, textStatus) {  
234 - if (timeoutTimer)  
235 - clearTimeout(timeoutTimer)  
236 -  
237 - fire('pjax:complete', [xhr, textStatus, options])  
238 -  
239 - fire('pjax:end', [xhr, options])  
240 - }  
241 -  
242 - options.error = function(xhr, textStatus, errorThrown) {  
243 - var container = extractContainer("", xhr, options)  
244 -  
245 - var allowed = fire('pjax:error', [xhr, textStatus, errorThrown, options])  
246 - if (options.type == 'GET' && textStatus !== 'abort' && allowed) {  
247 - locationReplace(container.url)  
248 - }  
249 - }  
250 -  
251 - options.success = function(data, status, xhr) {  
252 - var previousState = pjax.state;  
253 -  
254 - // If $.pjax.defaults.version is a function, invoke it first.  
255 - // Otherwise it can be a static string.  
256 - var currentVersion = (typeof $.pjax.defaults.version === 'function') ?  
257 - $.pjax.defaults.version() :  
258 - $.pjax.defaults.version  
259 -  
260 - var latestVersion = xhr.getResponseHeader('X-PJAX-Version')  
261 -  
262 - var container = extractContainer(data, xhr, options)  
263 -  
264 - var url = parseURL(container.url)  
265 - if (hash) {  
266 - url.hash = hash  
267 - container.url = url.href  
268 - }  
269 -  
270 - // If there is a layout version mismatch, hard load the new url  
271 - if (currentVersion && latestVersion && currentVersion !== latestVersion) {  
272 - locationReplace(container.url)  
273 - return  
274 - }  
275 -  
276 - // If the new response is missing a body, hard load the page  
277 - if (!container.contents) {  
278 - locationReplace(container.url)  
279 - return  
280 - }  
281 -  
282 - pjax.state = {  
283 - id: options.id || uniqueId(),  
284 - url: container.url,  
285 - title: container.title,  
286 - container: context.selector,  
287 - fragment: options.fragment,  
288 - timeout: options.timeout  
289 - /**  
290 - * 2016年05月18 潘钊修改  
291 - * 添加一个pjax的标记,供angular.js路由判断  
292 - */  
293 - ,pjax: true  
294 - }  
295 -  
296 - if (options.push || options.replace) {  
297 - window.history.replaceState(pjax.state, container.title, container.url)  
298 - }  
299 -  
300 - // Only blur the focus if the focused element is within the container.  
301 - var blurFocus = $.contains(options.container, document.activeElement)  
302 -  
303 - // Clear out any focused controls before inserting new page contents.  
304 - if (blurFocus) {  
305 - try {  
306 - document.activeElement.blur()  
307 - } catch (e) { }  
308 - }  
309 -  
310 - if (container.title) document.title = container.title  
311 -  
312 - fire('pjax:beforeReplace', [container.contents, options], {  
313 - state: pjax.state,  
314 - previousState: previousState  
315 - })  
316 - context.html(container.contents)  
317 -  
318 - // FF bug: Won't autofocus fields that are inserted via JS.  
319 - // This behavior is incorrect. So if theres no current focus, autofocus  
320 - // the last field.  
321 - //  
322 - // http://www.w3.org/html/wg/drafts/html/master/forms.html  
323 - var autofocusEl = context.find('input[autofocus], textarea[autofocus]').last()[0]  
324 - if (autofocusEl && document.activeElement !== autofocusEl) {  
325 - autofocusEl.focus();  
326 - }  
327 -  
328 - executeScriptTags(container.scripts)  
329 -  
330 - var scrollTo = options.scrollTo  
331 -  
332 - // Ensure browser scrolls to the element referenced by the URL anchor  
333 - if (hash) {  
334 - var name = decodeURIComponent(hash.slice(1))  
335 - var target = document.getElementById(name) || document.getElementsByName(name)[0]  
336 - if (target) scrollTo = $(target).offset().top  
337 - }  
338 -  
339 - if (typeof scrollTo == 'number') $(window).scrollTop(scrollTo)  
340 -  
341 - fire('pjax:success', [data, status, xhr, options])  
342 - }  
343 -  
344 -  
345 - // Initialize pjax.state for the initial page load. Assume we're  
346 - // using the container and options of the link we're loading for the  
347 - // back button to the initial page. This ensures good back button  
348 - // behavior.  
349 - if (!pjax.state) {  
350 - pjax.state = {  
351 - id: uniqueId(),  
352 - url: window.location.href,  
353 - title: document.title,  
354 - container: context.selector,  
355 - fragment: options.fragment,  
356 - timeout: options.timeout  
357 - }  
358 - window.history.replaceState(pjax.state, document.title)  
359 - }  
360 -  
361 - // Cancel the current request if we're already pjaxing  
362 - abortXHR(pjax.xhr)  
363 -  
364 - pjax.options = options  
365 - var xhr = pjax.xhr = $.ajax(options)  
366 -  
367 - if (xhr.readyState > 0) {  
368 - if (options.push && !options.replace) {  
369 - // Cache current container element before replacing it  
370 - cachePush(pjax.state.id, cloneContents(context))  
371 -  
372 - //window.history.pushState(null, "", options.requestUrl)  
373 - window.history.pushState(null, "", options.requestUrl)  
374 - }  
375 -  
376 - fire('pjax:start', [xhr, options])  
377 - fire('pjax:send', [xhr, options])  
378 - }  
379 -  
380 - return pjax.xhr  
381 -}  
382 -  
383 -// Public: Reload current page with pjax.  
384 -//  
385 -// Returns whatever $.pjax returns.  
386 -function pjaxReload(container, options) {  
387 - var defaults = {  
388 - url: window.location.href,  
389 - push: false,  
390 - replace: true,  
391 - scrollTo: false  
392 - }  
393 -  
394 - return pjax($.extend(defaults, optionsFor(container, options)))  
395 -}  
396 -  
397 -// Internal: Hard replace current state with url.  
398 -//  
399 -// Work for around WebKit  
400 -// https://bugs.webkit.org/show_bug.cgi?id=93506  
401 -//  
402 -// Returns nothing.  
403 -function locationReplace(url) {  
404 - window.history.replaceState(null, "", pjax.state.url)  
405 - window.location.replace(url)  
406 -}  
407 -  
408 -  
409 -var initialPop = true  
410 -var initialURL = window.location.href  
411 -var initialState = window.history.state  
412 -  
413 -// Initialize $.pjax.state if possible  
414 -// Happens when reloading a page and coming forward from a different  
415 -// session history.  
416 -if (initialState && initialState.container) {  
417 - pjax.state = initialState  
418 -}  
419 -  
420 -// Non-webkit browsers don't fire an initial popstate event  
421 -if ('state' in window.history) {  
422 - initialPop = false  
423 -}  
424 -  
425 -// popstate handler takes care of the back and forward buttons  
426 -//  
427 -// You probably shouldn't use pjax on pages with other pushState  
428 -// stuff yet.  
429 -function onPjaxPopstate(event) {  
430 - debugger  
431 -  
432 -/**  
433 - * 2016年05月18 潘钊修改  
434 - */  
435 -if(document.location.hash){  
436 - event.stopPropagation();  
437 -  
438 - var oldUrl = $.url()  
439 - ,newUrl = oldUrl.attr('protocol') + '://' + oldUrl.attr('host') + ':' + oldUrl.attr('port');  
440 - history.replaceState({}, document.title, newUrl + '/' + document.location.hash);  
441 -  
442 - return;  
443 -}  
444 -/**  
445 - * -----end------  
446 - */  
447 -  
448 - // Hitting back or forward should override any pending PJAX request.  
449 - if (!initialPop) {  
450 - abortXHR(pjax.xhr)  
451 - }  
452 -  
453 - var previousState = pjax.state  
454 - var state = event.state  
455 - var direction  
456 -  
457 - if (state && state.container) {  
458 - // When coming forward from a separate history session, will get an  
459 - // initial pop with a state we are already at. Skip reloading the current  
460 - // page.  
461 - if (initialPop && initialURL == state.url) return  
462 -  
463 - if (previousState) {  
464 - // If popping back to the same state, just skip.  
465 - // Could be clicking back from hashchange rather than a pushState.  
466 - if (previousState.id === state.id) return  
467 -  
468 - // Since state IDs always increase, we can deduce the navigation direction  
469 - direction = previousState.id < state.id ? 'forward' : 'back'  
470 - }  
471 -  
472 - var cache = cacheMapping[state.id] || []  
473 - var container = $(cache[0] || state.container), contents = cache[1]  
474 -  
475 - if (container.length) {  
476 - if (previousState) {  
477 - // Cache current container before replacement and inform the  
478 - // cache which direction the history shifted.  
479 - cachePop(direction, previousState.id, cloneContents(container))  
480 - }  
481 -  
482 - var popstateEvent = $.Event('pjax:popstate', {  
483 - state: state,  
484 - direction: direction  
485 - })  
486 - container.trigger(popstateEvent)  
487 -  
488 - var options = {  
489 - id: state.id,  
490 - url: state.url,  
491 - container: container,  
492 - push: false,  
493 - fragment: state.fragment,  
494 - timeout: state.timeout,  
495 - scrollTo: false  
496 - }  
497 -  
498 - if (contents) {  
499 - container.trigger('pjax:start', [null, options])  
500 -  
501 - pjax.state = state  
502 - if (state.title) document.title = state.title  
503 - var beforeReplaceEvent = $.Event('pjax:beforeReplace', {  
504 - state: state,  
505 - previousState: previousState  
506 - })  
507 - container.trigger(beforeReplaceEvent, [contents, options])  
508 - container.html(contents)  
509 -  
510 - container.trigger('pjax:end', [null, options])  
511 - } else {  
512 - pjax(options)  
513 - }  
514 -  
515 - // Force reflow/relayout before the browser tries to restore the  
516 - // scroll position.  
517 - container[0].offsetHeight  
518 - } else {  
519 - locationReplace(location.href)  
520 - }  
521 - }  
522 - initialPop = false  
523 -}  
524 -  
525 -// Fallback version of main pjax function for browsers that don't  
526 -// support pushState.  
527 -//  
528 -// Returns nothing since it retriggers a hard form submission.  
529 -function fallbackPjax(options) {  
530 - var url = $.isFunction(options.url) ? options.url() : options.url,  
531 - method = options.type ? options.type.toUpperCase() : 'GET'  
532 -  
533 - var form = $('<form>', {  
534 - method: method === 'GET' ? 'GET' : 'POST',  
535 - action: url,  
536 - style: 'display:none'  
537 - })  
538 -  
539 - if (method !== 'GET' && method !== 'POST') {  
540 - form.append($('<input>', {  
541 - type: 'hidden',  
542 - name: '_method',  
543 - value: method.toLowerCase()  
544 - }))  
545 - }  
546 -  
547 - var data = options.data  
548 - if (typeof data === 'string') {  
549 - $.each(data.split('&'), function(index, value) {  
550 - var pair = value.split('=')  
551 - form.append($('<input>', {type: 'hidden', name: pair[0], value: pair[1]}))  
552 - })  
553 - } else if ($.isArray(data)) {  
554 - $.each(data, function(index, value) {  
555 - form.append($('<input>', {type: 'hidden', name: value.name, value: value.value}))  
556 - })  
557 - } else if (typeof data === 'object') {  
558 - var key  
559 - for (key in data)  
560 - form.append($('<input>', {type: 'hidden', name: key, value: data[key]}))  
561 - }  
562 -  
563 - $(document.body).append(form)  
564 - form.submit()  
565 -}  
566 -  
567 -// Internal: Abort an XmlHttpRequest if it hasn't been completed,  
568 -// also removing its event handlers.  
569 -function abortXHR(xhr) {  
570 - if ( xhr && xhr.readyState < 4) {  
571 - xhr.onreadystatechange = $.noop  
572 - xhr.abort()  
573 - }  
574 -}  
575 -  
576 -// Internal: Generate unique id for state object.  
577 -//  
578 -// Use a timestamp instead of a counter since ids should still be  
579 -// unique across page loads.  
580 -//  
581 -// Returns Number.  
582 -function uniqueId() {  
583 - return (new Date).getTime()  
584 -}  
585 -  
586 -function cloneContents(container) {  
587 - var cloned = container.clone()  
588 - // Unmark script tags as already being eval'd so they can get executed again  
589 - // when restored from cache. HAXX: Uses jQuery internal method.  
590 - /**  
591 - * 2016年05月19 潘钊修改  
592 - * 在缓存里打一个标记  
593 - * ##################  
594 - */  
595 - cloned.append('<input type="hidden" id="historyCache" value="1"/>')  
596 - /**  
597 - * ##################  
598 - */  
599 - cloned.find('script').each(function(){  
600 - if (!this.src) jQuery._data(this, 'globalEval', false)  
601 - })  
602 - return [container.selector, cloned.contents()]  
603 -}  
604 -  
605 -// Internal: Strip internal query params from parsed URL.  
606 -//  
607 -// Returns sanitized url.href String.  
608 -function stripInternalParams(url) {  
609 - url.search = url.search.replace(/([?&])(_pjax|_)=[^&]*/g, '')  
610 - return url.href.replace(/\?($|#)/, '$1')  
611 -}  
612 -  
613 -// Internal: Parse URL components and returns a Locationish object.  
614 -//  
615 -// url - String URL  
616 -//  
617 -// Returns HTMLAnchorElement that acts like Location.  
618 -function parseURL(url) {  
619 - var a = document.createElement('a')  
620 - a.href = url  
621 - return a  
622 -}  
623 -  
624 -// Internal: Return the `href` component of given URL object with the hash  
625 -// portion removed.  
626 -//  
627 -// location - Location or HTMLAnchorElement  
628 -//  
629 -// Returns String  
630 -function stripHash(location) {  
631 - return location.href.replace(/#.*/, '')  
632 -}  
633 -  
634 -// Internal: Build options Object for arguments.  
635 -//  
636 -// For convenience the first parameter can be either the container or  
637 -// the options object.  
638 -//  
639 -// Examples  
640 -//  
641 -// optionsFor('#container')  
642 -// // => {container: '#container'}  
643 -//  
644 -// optionsFor('#container', {push: true})  
645 -// // => {container: '#container', push: true}  
646 -//  
647 -// optionsFor({container: '#container', push: true})  
648 -// // => {container: '#container', push: true}  
649 -//  
650 -// Returns options Object.  
651 -function optionsFor(container, options) {  
652 - // Both container and options  
653 - if ( container && options )  
654 - options.container = container  
655 -  
656 - // First argument is options Object  
657 - else if ( $.isPlainObject(container) )  
658 - options = container  
659 -  
660 - // Only container  
661 - else  
662 - options = {container: container}  
663 -  
664 - // Find and validate container  
665 - if (options.container)  
666 - options.container = findContainerFor(options.container)  
667 -  
668 - return options  
669 -}  
670 -  
671 -// Internal: Find container element for a variety of inputs.  
672 -//  
673 -// Because we can't persist elements using the history API, we must be  
674 -// able to find a String selector that will consistently find the Element.  
675 -//  
676 -// container - A selector String, jQuery object, or DOM Element.  
677 -//  
678 -// Returns a jQuery object whose context is `document` and has a selector.  
679 -function findContainerFor(container) {  
680 - container = $(container)  
681 -  
682 - if ( !container.length ) {  
683 - throw "no pjax container for " + container.selector  
684 - } else if ( container.selector !== '' && container.context === document ) {  
685 - return container  
686 - } else if ( container.attr('id') ) {  
687 - return $('#' + container.attr('id'))  
688 - } else {  
689 - throw "cant get selector for pjax container!"  
690 - }  
691 -}  
692 -  
693 -// Internal: Filter and find all elements matching the selector.  
694 -//  
695 -// Where $.fn.find only matches descendants, findAll will test all the  
696 -// top level elements in the jQuery object as well.  
697 -//  
698 -// elems - jQuery object of Elements  
699 -// selector - String selector to match  
700 -//  
701 -// Returns a jQuery object.  
702 -function findAll(elems, selector) {  
703 - return elems.filter(selector).add(elems.find(selector));  
704 -}  
705 -  
706 -function parseHTML(html) {  
707 - return $.parseHTML(html, document, true)  
708 -}  
709 -  
710 -// Internal: Extracts container and metadata from response.  
711 -//  
712 -// 1. Extracts X-PJAX-URL header if set  
713 -// 2. Extracts inline <title> tags  
714 -// 3. Builds response Element and extracts fragment if set  
715 -//  
716 -// data - String response data  
717 -// xhr - XHR response  
718 -// options - pjax options Object  
719 -//  
720 -// Returns an Object with url, title, and contents keys.  
721 -function extractContainer(data, xhr, options) {  
722 - var obj = {}, fullDocument = /<html/i.test(data)  
723 -  
724 - // Prefer X-PJAX-URL header if it was set, otherwise fallback to  
725 - // using the original requested url.  
726 - var serverUrl = xhr.getResponseHeader('X-PJAX-URL')  
727 - obj.url = serverUrl ? stripInternalParams(parseURL(serverUrl)) : options.requestUrl  
728 -  
729 - // Attempt to parse response html into elements  
730 - if (fullDocument) {  
731 - var $head = $(parseHTML(data.match(/<head[^>]*>([\s\S.]*)<\/head>/i)[0]))  
732 - var $body = $(parseHTML(data.match(/<body[^>]*>([\s\S.]*)<\/body>/i)[0]))  
733 - } else {  
734 - var $head = $body = $(parseHTML(data))  
735 - }  
736 -  
737 - // If response data is empty, return fast  
738 - if ($body.length === 0)  
739 - return obj  
740 -  
741 - // If there's a <title> tag in the header, use it as  
742 - // the page's title.  
743 - obj.title = findAll($head, 'title').last().text()  
744 -  
745 - if (options.fragment) {  
746 - // If they specified a fragment, look for it in the response  
747 - // and pull it out.  
748 - if (options.fragment === 'body') {  
749 - var $fragment = $body  
750 - } else {  
751 - var $fragment = findAll($body, options.fragment).first()  
752 - }  
753 -  
754 - if ($fragment.length) {  
755 - obj.contents = options.fragment === 'body' ? $fragment : $fragment.contents()  
756 -  
757 - // If there's no title, look for data-title and title attributes  
758 - // on the fragment  
759 - if (!obj.title)  
760 - obj.title = $fragment.attr('title') || $fragment.data('title')  
761 - }  
762 -  
763 - } else if (!fullDocument) {  
764 - obj.contents = $body  
765 - }  
766 -  
767 - // Clean up any <title> tags  
768 - if (obj.contents) {  
769 - // Remove any parent title elements  
770 - obj.contents = obj.contents.not(function() { return $(this).is('title') })  
771 -  
772 - // Then scrub any titles from their descendants  
773 - obj.contents.find('title').remove()  
774 -  
775 - // Gather all script[src] elements  
776 - obj.scripts = findAll(obj.contents, 'script[src]').remove()  
777 - obj.contents = obj.contents.not(obj.scripts)  
778 - }  
779 -  
780 - // Trim any whitespace off the title  
781 - if (obj.title) obj.title = $.trim(obj.title)  
782 -  
783 - return obj  
784 -}  
785 -  
786 -// Load an execute scripts using standard script request.  
787 -//  
788 -// Avoids jQuery's traditional $.getScript which does a XHR request and  
789 -// globalEval.  
790 -//  
791 -// scripts - jQuery object of script Elements  
792 -//  
793 -// Returns nothing.  
794 -function executeScriptTags(scripts) {  
795 - if (!scripts) return  
796 -  
797 - var existingScripts = $('script[src]')  
798 -  
799 - scripts.each(function() {  
800 - var src = this.src  
801 - var matchedScripts = existingScripts.filter(function() {  
802 - return this.src === src  
803 - })  
804 - if (matchedScripts.length) return  
805 -  
806 - var script = document.createElement('script')  
807 - var type = $(this).attr('type')  
808 - if (type) script.type = type  
809 - script.src = $(this).attr('src')  
810 - document.head.appendChild(script)  
811 - })  
812 -}  
813 -  
814 -// Internal: History DOM caching class.  
815 -var cacheMapping = {}  
816 -var cacheForwardStack = []  
817 -var cacheBackStack = []  
818 -  
819 -// Push previous state id and container contents into the history  
820 -// cache. Should be called in conjunction with `pushState` to save the  
821 -// previous container contents.  
822 -//  
823 -// id - State ID Number  
824 -// value - DOM Element to cache  
825 -//  
826 -// Returns nothing.  
827 -function cachePush(id, value) {  
828 - cacheMapping[id] = value  
829 - cacheBackStack.push(id)  
830 -  
831 - // Remove all entries in forward history stack after pushing a new page.  
832 - trimCacheStack(cacheForwardStack, 0)  
833 -  
834 - // Trim back history stack to max cache length.  
835 - trimCacheStack(cacheBackStack, pjax.defaults.maxCacheLength)  
836 -}  
837 -  
838 -// Shifts cache from directional history cache. Should be  
839 -// called on `popstate` with the previous state id and container  
840 -// contents.  
841 -//  
842 -// direction - "forward" or "back" String  
843 -// id - State ID Number  
844 -// value - DOM Element to cache  
845 -//  
846 -// Returns nothing.  
847 -function cachePop(direction, id, value) {  
848 - var pushStack, popStack  
849 - cacheMapping[id] = value  
850 -  
851 - if (direction === 'forward') {  
852 - pushStack = cacheBackStack  
853 - popStack = cacheForwardStack  
854 - } else {  
855 - pushStack = cacheForwardStack  
856 - popStack = cacheBackStack  
857 - }  
858 -  
859 - pushStack.push(id)  
860 - if (id = popStack.pop())  
861 - delete cacheMapping[id]  
862 -  
863 - // Trim whichever stack we just pushed to to max cache length.  
864 - trimCacheStack(pushStack, pjax.defaults.maxCacheLength)  
865 -}  
866 -  
867 -// Trim a cache stack (either cacheBackStack or cacheForwardStack) to be no  
868 -// longer than the specified length, deleting cached DOM elements as necessary.  
869 -//  
870 -// stack - Array of state IDs  
871 -// length - Maximum length to trim to  
872 -//  
873 -// Returns nothing.  
874 -function trimCacheStack(stack, length) {  
875 - while (stack.length > length)  
876 - delete cacheMapping[stack.shift()]  
877 -}  
878 -  
879 -// Public: Find version identifier for the initial page load.  
880 -//  
881 -// Returns String version or undefined.  
882 -function findVersion() {  
883 - return $('meta').filter(function() {  
884 - var name = $(this).attr('http-equiv')  
885 - return name && name.toUpperCase() === 'X-PJAX-VERSION'  
886 - }).attr('content')  
887 -}  
888 -  
889 -// Install pjax functions on $.pjax to enable pushState behavior.  
890 -//  
891 -// Does nothing if already enabled.  
892 -//  
893 -// Examples  
894 -//  
895 -// $.pjax.enable()  
896 -//  
897 -// Returns nothing.  
898 -function enable() {  
899 - $.fn.pjax = fnPjax  
900 - $.pjax = pjax  
901 - $.pjax.enable = $.noop  
902 - $.pjax.disable = disable  
903 - $.pjax.click = handleClick  
904 - $.pjax.submit = handleSubmit  
905 - $.pjax.reload = pjaxReload  
906 - $.pjax.defaults = {  
907 - timeout: 650,  
908 - push: true,  
909 - replace: false,  
910 - type: 'GET',  
911 - dataType: 'html',  
912 - scrollTo: 0,  
913 - maxCacheLength: 20,  
914 - version: findVersion  
915 - }  
916 - $(window).on('popstate.pjax', onPjaxPopstate)  
917 -}  
918 -  
919 -// Disable pushState behavior.  
920 -//  
921 -// This is the case when a browser doesn't support pushState. It is  
922 -// sometimes useful to disable pushState for debugging on a modern  
923 -// browser.  
924 -//  
925 -// Examples  
926 -//  
927 -// $.pjax.disable()  
928 -//  
929 -// Returns nothing.  
930 -function disable() {  
931 - $.fn.pjax = function() { return this }  
932 - $.pjax = fallbackPjax  
933 - $.pjax.enable = enable  
934 - $.pjax.disable = $.noop  
935 - $.pjax.click = $.noop  
936 - $.pjax.submit = $.noop  
937 - $.pjax.reload = function() { window.location.reload() }  
938 -  
939 - $(window).off('popstate.pjax', onPjaxPopstate)  
940 -}  
941 -  
942 -  
943 -// Add the state property to jQuery's event object so we can use it in  
944 -// $(window).bind('popstate')  
945 -if ( $.inArray('state', $.event.props) < 0 )  
946 - $.event.props.push('state')  
947 -  
948 -// Is pjax supported by this browser?  
949 -$.support.pjax =  
950 - window.history && window.history.pushState && window.history.replaceState &&  
951 - // pushState isn't reliable on iOS until 5.  
952 - !navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]\D|WebApps\/.+CFNetwork)/)  
953 -  
954 -$.support.pjax ? enable() : disable()  
955 -  
956 -})(jQuery); 1 +/*!
  2 + * Copyright 2012, Chris Wanstrath
  3 + * Released under the MIT License
  4 + * https://github.com/defunkt/jquery-pjax
  5 + */
  6 +
  7 +(function($){
  8 +
  9 +// When called on a container with a selector, fetches the href with
  10 +// ajax into the container or with the data-pjax attribute on the link
  11 +// itself.
  12 +//
  13 +// Tries to make sure the back button and ctrl+click work the way
  14 +// you'd expect.
  15 +//
  16 +// Exported as $.fn.pjax
  17 +//
  18 +// Accepts a jQuery ajax options object that may include these
  19 +// pjax specific options:
  20 +//
  21 +//
  22 +// container - Where to stick the response body. Usually a String selector.
  23 +// $(container).html(xhr.responseBody)
  24 +// (default: current jquery context)
  25 +// push - Whether to pushState the URL. Defaults to true (of course).
  26 +// replace - Want to use replaceState instead? That's cool.
  27 +//
  28 +// For convenience the second parameter can be either the container or
  29 +// the options object.
  30 +//
  31 +// Returns the jQuery object
  32 +function fnPjax(selector, container, options) {
  33 + var context = this
  34 + return this.on('click.pjax', selector, function(event) {
  35 + var opts = $.extend({}, optionsFor(container, options))
  36 + if (!opts.container)
  37 + opts.container = $(this).attr('data-pjax') || context
  38 + handleClick(event, opts)
  39 + })
  40 +}
  41 +
  42 +// Public: pjax on click handler
  43 +//
  44 +// Exported as $.pjax.click.
  45 +//
  46 +// event - "click" jQuery.Event
  47 +// options - pjax options
  48 +//
  49 +// Examples
  50 +//
  51 +// $(document).on('click', 'a', $.pjax.click)
  52 +// // is the same as
  53 +// $(document).pjax('a')
  54 +//
  55 +// $(document).on('click', 'a', function(event) {
  56 +// var container = $(this).closest('[data-pjax-container]')
  57 +// $.pjax.click(event, container)
  58 +// })
  59 +//
  60 +// Returns nothing.
  61 +function handleClick(event, container, options) {
  62 + options = optionsFor(container, options)
  63 +
  64 + var link = event.currentTarget
  65 +
  66 + if (link.tagName.toUpperCase() !== 'A')
  67 + throw "$.fn.pjax or $.pjax.click requires an anchor element"
  68 +
  69 + // Middle click, cmd click, and ctrl click should open
  70 + // links in a new tab as normal.
  71 + if ( event.which > 1 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey )
  72 + return
  73 +
  74 + // Ignore cross origin links
  75 + if ( location.protocol !== link.protocol || location.hostname !== link.hostname )
  76 + return
  77 +
  78 + // Ignore case when a hash is being tacked on the current URL
  79 + if ( link.href.indexOf('#') > -1 && stripHash(link) == stripHash(location) )
  80 + return
  81 +
  82 + // Ignore event with default prevented
  83 + if (event.isDefaultPrevented())
  84 + return
  85 +
  86 + var defaults = {
  87 + url: link.href,
  88 + container: $(link).attr('data-pjax'),
  89 + target: link
  90 + }
  91 +
  92 + var opts = $.extend({}, defaults, options)
  93 + var clickEvent = $.Event('pjax:click')
  94 + $(link).trigger(clickEvent, [opts])
  95 +
  96 + if (!clickEvent.isDefaultPrevented()) {
  97 + pjax(opts)
  98 + event.preventDefault()
  99 + $(link).trigger('pjax:clicked', [opts])
  100 + }
  101 +}
  102 +
  103 +// Public: pjax on form submit handler
  104 +//
  105 +// Exported as $.pjax.submit
  106 +//
  107 +// event - "click" jQuery.Event
  108 +// options - pjax options
  109 +//
  110 +// Examples
  111 +//
  112 +// $(document).on('submit', 'form', function(event) {
  113 +// var container = $(this).closest('[data-pjax-container]')
  114 +// $.pjax.submit(event, container)
  115 +// })
  116 +//
  117 +// Returns nothing.
  118 +function handleSubmit(event, container, options) {
  119 + options = optionsFor(container, options)
  120 +
  121 + var form = event.currentTarget
  122 +
  123 + if (form.tagName.toUpperCase() !== 'FORM')
  124 + throw "$.pjax.submit requires a form element"
  125 +
  126 + var defaults = {
  127 + type: form.method.toUpperCase(),
  128 + url: form.action,
  129 + container: $(form).attr('data-pjax'),
  130 + target: form
  131 + }
  132 +
  133 + if (defaults.type !== 'GET' && window.FormData !== undefined) {
  134 + defaults.data = new FormData(form);
  135 + defaults.processData = false;
  136 + defaults.contentType = false;
  137 + } else {
  138 + // Can't handle file uploads, exit
  139 + if ($(form).find(':file').length) {
  140 + return;
  141 + }
  142 +
  143 + // Fallback to manually serializing the fields
  144 + defaults.data = $(form).serializeArray();
  145 + }
  146 +
  147 + pjax($.extend({}, defaults, options))
  148 +
  149 + event.preventDefault()
  150 +}
  151 +
  152 +// Loads a URL with ajax, puts the response body inside a container,
  153 +// then pushState()'s the loaded URL.
  154 +//
  155 +// Works just like $.ajax in that it accepts a jQuery ajax
  156 +// settings object (with keys like url, type, data, etc).
  157 +//
  158 +// Accepts these extra keys:
  159 +//
  160 +// container - Where to stick the response body.
  161 +// $(container).html(xhr.responseBody)
  162 +// push - Whether to pushState the URL. Defaults to true (of course).
  163 +// replace - Want to use replaceState instead? That's cool.
  164 +//
  165 +// Use it just like $.ajax:
  166 +//
  167 +// var xhr = $.pjax({ url: this.href, container: '#main' })
  168 +// console.log( xhr.readyState )
  169 +//
  170 +// Returns whatever $.ajax returns.
  171 +function pjax(options) {
  172 + options = $.extend(true, {}, $.ajaxSettings, pjax.defaults, options)
  173 +
  174 + if ($.isFunction(options.url)) {
  175 + options.url = options.url()
  176 + }
  177 +
  178 + var target = options.target
  179 +
  180 + var hash = parseURL(options.url).hash
  181 +
  182 + var context = options.context = findContainerFor(options.container)
  183 +
  184 + // We want the browser to maintain two separate internal caches: one
  185 + // for pjax'd partial page loads and one for normal page loads.
  186 + // Without adding this secret parameter, some browsers will often
  187 + // confuse the two.
  188 + if (!options.data) options.data = {}
  189 + if ($.isArray(options.data)) {
  190 + options.data.push({name: '_pjax', value: context.selector})
  191 + } else {
  192 + options.data._pjax = context.selector
  193 + }
  194 +
  195 + function fire(type, args, props) {
  196 + if (!props) props = {}
  197 + props.relatedTarget = target
  198 + var event = $.Event(type, props)
  199 + context.trigger(event, args)
  200 + return !event.isDefaultPrevented()
  201 + }
  202 +
  203 + var timeoutTimer
  204 +
  205 + options.beforeSend = function(xhr, settings) {
  206 + // No timeout for non-GET requests
  207 + // Its not safe to request the resource again with a fallback method.
  208 + if (settings.type !== 'GET') {
  209 + settings.timeout = 0
  210 + }
  211 +
  212 + xhr.setRequestHeader('X-PJAX', 'true')
  213 + xhr.setRequestHeader('X-PJAX-Container', context.selector)
  214 +
  215 + if (!fire('pjax:beforeSend', [xhr, settings]))
  216 + return false
  217 +
  218 + if (settings.timeout > 0) {
  219 + timeoutTimer = setTimeout(function() {
  220 + if (fire('pjax:timeout', [xhr, options]))
  221 + xhr.abort('timeout')
  222 + }, settings.timeout)
  223 +
  224 + // Clear timeout setting so jquerys internal timeout isn't invoked
  225 + settings.timeout = 0
  226 + }
  227 +
  228 + var url = parseURL(settings.url)
  229 + if (hash) url.hash = hash
  230 + options.requestUrl = stripInternalParams(url)
  231 + }
  232 +
  233 + options.complete = function(xhr, textStatus) {
  234 + if (timeoutTimer)
  235 + clearTimeout(timeoutTimer)
  236 +
  237 + fire('pjax:complete', [xhr, textStatus, options])
  238 +
  239 + fire('pjax:end', [xhr, options])
  240 + }
  241 +
  242 + options.error = function(xhr, textStatus, errorThrown) {
  243 + var container = extractContainer("", xhr, options)
  244 +
  245 + var allowed = fire('pjax:error', [xhr, textStatus, errorThrown, options])
  246 + if (options.type == 'GET' && textStatus !== 'abort' && allowed) {
  247 + locationReplace(container.url)
  248 + }
  249 + }
  250 +
  251 + options.success = function(data, status, xhr) {
  252 + var previousState = pjax.state;
  253 +
  254 + // If $.pjax.defaults.version is a function, invoke it first.
  255 + // Otherwise it can be a static string.
  256 + var currentVersion = (typeof $.pjax.defaults.version === 'function') ?
  257 + $.pjax.defaults.version() :
  258 + $.pjax.defaults.version
  259 +
  260 + var latestVersion = xhr.getResponseHeader('X-PJAX-Version')
  261 +
  262 + var container = extractContainer(data, xhr, options)
  263 +
  264 + var url = parseURL(container.url)
  265 + if (hash) {
  266 + url.hash = hash
  267 + container.url = url.href
  268 + }
  269 +
  270 + // If there is a layout version mismatch, hard load the new url
  271 + if (currentVersion && latestVersion && currentVersion !== latestVersion) {
  272 + locationReplace(container.url)
  273 + return
  274 + }
  275 +
  276 + // If the new response is missing a body, hard load the page
  277 + if (!container.contents) {
  278 + locationReplace(container.url)
  279 + return
  280 + }
  281 +
  282 + pjax.state = {
  283 + id: options.id || uniqueId(),
  284 + url: container.url,
  285 + title: container.title,
  286 + container: context.selector,
  287 + fragment: options.fragment,
  288 + timeout: options.timeout
  289 + /**
  290 + * 2016年05月18 潘钊修改
  291 + * 添加一个pjax的标记,供angular.js路由判断
  292 + */
  293 + ,pjax: true
  294 + }
  295 +
  296 + if (options.push || options.replace) {
  297 + window.history.replaceState(pjax.state, container.title, container.url)
  298 + }
  299 +
  300 + // Only blur the focus if the focused element is within the container.
  301 + var blurFocus = $.contains(options.container, document.activeElement)
  302 +
  303 + // Clear out any focused controls before inserting new page contents.
  304 + if (blurFocus) {
  305 + try {
  306 + document.activeElement.blur()
  307 + } catch (e) { }
  308 + }
  309 +
  310 + if (container.title) document.title = container.title
  311 +
  312 + fire('pjax:beforeReplace', [container.contents, options], {
  313 + state: pjax.state,
  314 + previousState: previousState
  315 + })
  316 + context.html(container.contents)
  317 +
  318 + // FF bug: Won't autofocus fields that are inserted via JS.
  319 + // This behavior is incorrect. So if theres no current focus, autofocus
  320 + // the last field.
  321 + //
  322 + // http://www.w3.org/html/wg/drafts/html/master/forms.html
  323 + var autofocusEl = context.find('input[autofocus], textarea[autofocus]').last()[0]
  324 + if (autofocusEl && document.activeElement !== autofocusEl) {
  325 + autofocusEl.focus();
  326 + }
  327 +
  328 + executeScriptTags(container.scripts)
  329 +
  330 + var scrollTo = options.scrollTo
  331 +
  332 + // Ensure browser scrolls to the element referenced by the URL anchor
  333 + if (hash) {
  334 + var name = decodeURIComponent(hash.slice(1))
  335 + var target = document.getElementById(name) || document.getElementsByName(name)[0]
  336 + if (target) scrollTo = $(target).offset().top
  337 + }
  338 +
  339 + if (typeof scrollTo == 'number') $(window).scrollTop(scrollTo)
  340 +
  341 + fire('pjax:success', [data, status, xhr, options])
  342 + }
  343 +
  344 +
  345 + // Initialize pjax.state for the initial page load. Assume we're
  346 + // using the container and options of the link we're loading for the
  347 + // back button to the initial page. This ensures good back button
  348 + // behavior.
  349 + if (!pjax.state) {
  350 + pjax.state = {
  351 + id: uniqueId(),
  352 + url: window.location.href,
  353 + title: document.title,
  354 + container: context.selector,
  355 + fragment: options.fragment,
  356 + timeout: options.timeout
  357 + }
  358 + window.history.replaceState(pjax.state, document.title)
  359 + }
  360 +
  361 + // Cancel the current request if we're already pjaxing
  362 + abortXHR(pjax.xhr)
  363 +
  364 + pjax.options = options
  365 + var xhr = pjax.xhr = $.ajax(options)
  366 +
  367 + if (xhr.readyState > 0) {
  368 + if (options.push && !options.replace) {
  369 + // Cache current container element before replacing it
  370 + cachePush(pjax.state.id, cloneContents(context))
  371 +
  372 + //window.history.pushState(null, "", options.requestUrl)
  373 + window.history.pushState(null, "", options.requestUrl)
  374 + }
  375 +
  376 + fire('pjax:start', [xhr, options])
  377 + fire('pjax:send', [xhr, options])
  378 + }
  379 +
  380 + return pjax.xhr
  381 +}
  382 +
  383 +// Public: Reload current page with pjax.
  384 +//
  385 +// Returns whatever $.pjax returns.
  386 +function pjaxReload(container, options) {
  387 + var defaults = {
  388 + url: window.location.href,
  389 + push: false,
  390 + replace: true,
  391 + scrollTo: false
  392 + }
  393 +
  394 + return pjax($.extend(defaults, optionsFor(container, options)))
  395 +}
  396 +
  397 +// Internal: Hard replace current state with url.
  398 +//
  399 +// Work for around WebKit
  400 +// https://bugs.webkit.org/show_bug.cgi?id=93506
  401 +//
  402 +// Returns nothing.
  403 +function locationReplace(url) {
  404 + window.history.replaceState(null, "", pjax.state.url)
  405 + window.location.replace(url)
  406 +}
  407 +
  408 +
  409 +var initialPop = true
  410 +var initialURL = window.location.href
  411 +var initialState = window.history.state
  412 +
  413 +// Initialize $.pjax.state if possible
  414 +// Happens when reloading a page and coming forward from a different
  415 +// session history.
  416 +if (initialState && initialState.container) {
  417 + pjax.state = initialState
  418 +}
  419 +
  420 +// Non-webkit browsers don't fire an initial popstate event
  421 +if ('state' in window.history) {
  422 + initialPop = false
  423 +}
  424 +
  425 +// popstate handler takes care of the back and forward buttons
  426 +//
  427 +// You probably shouldn't use pjax on pages with other pushState
  428 +// stuff yet.
  429 +function onPjaxPopstate(event) {
  430 +/**
  431 + * 2016年05月18 潘钊修改
  432 + */
  433 +if(document.location.hash){
  434 + event.stopPropagation();
  435 +
  436 + var oldUrl = $.url()
  437 + ,newUrl = oldUrl.attr('protocol') + '://' + oldUrl.attr('host') + ':' + oldUrl.attr('port');
  438 + history.replaceState({}, document.title, newUrl + '/' + document.location.hash);
  439 +
  440 + return;
  441 +}
  442 +/**
  443 + * -----end------
  444 + */
  445 +
  446 + // Hitting back or forward should override any pending PJAX request.
  447 + if (!initialPop) {
  448 + abortXHR(pjax.xhr)
  449 + }
  450 +
  451 + var previousState = pjax.state
  452 + var state = event.state
  453 + var direction
  454 +
  455 + if (state && state.container) {
  456 + // When coming forward from a separate history session, will get an
  457 + // initial pop with a state we are already at. Skip reloading the current
  458 + // page.
  459 + if (initialPop && initialURL == state.url) return
  460 +
  461 + if (previousState) {
  462 + // If popping back to the same state, just skip.
  463 + // Could be clicking back from hashchange rather than a pushState.
  464 + if (previousState.id === state.id) return
  465 +
  466 + // Since state IDs always increase, we can deduce the navigation direction
  467 + direction = previousState.id < state.id ? 'forward' : 'back'
  468 + }
  469 +
  470 + var cache = cacheMapping[state.id] || []
  471 + var container = $(cache[0] || state.container), contents = cache[1]
  472 +
  473 + if (container.length) {
  474 + if (previousState) {
  475 + // Cache current container before replacement and inform the
  476 + // cache which direction the history shifted.
  477 + cachePop(direction, previousState.id, cloneContents(container))
  478 + }
  479 +
  480 + var popstateEvent = $.Event('pjax:popstate', {
  481 + state: state,
  482 + direction: direction
  483 + })
  484 + container.trigger(popstateEvent)
  485 +
  486 + var options = {
  487 + id: state.id,
  488 + url: state.url,
  489 + container: container,
  490 + push: false,
  491 + fragment: state.fragment,
  492 + timeout: state.timeout,
  493 + scrollTo: false
  494 + }
  495 +
  496 + if (contents) {
  497 + container.trigger('pjax:start', [null, options])
  498 +
  499 + pjax.state = state
  500 + if (state.title) document.title = state.title
  501 + var beforeReplaceEvent = $.Event('pjax:beforeReplace', {
  502 + state: state,
  503 + previousState: previousState
  504 + })
  505 + container.trigger(beforeReplaceEvent, [contents, options])
  506 + container.html(contents)
  507 +
  508 + container.trigger('pjax:end', [null, options])
  509 + } else {
  510 + pjax(options)
  511 + }
  512 +
  513 + // Force reflow/relayout before the browser tries to restore the
  514 + // scroll position.
  515 + container[0].offsetHeight
  516 + } else {
  517 + locationReplace(location.href)
  518 + }
  519 + }
  520 + initialPop = false
  521 +}
  522 +
  523 +// Fallback version of main pjax function for browsers that don't
  524 +// support pushState.
  525 +//
  526 +// Returns nothing since it retriggers a hard form submission.
  527 +function fallbackPjax(options) {
  528 + var url = $.isFunction(options.url) ? options.url() : options.url,
  529 + method = options.type ? options.type.toUpperCase() : 'GET'
  530 +
  531 + var form = $('<form>', {
  532 + method: method === 'GET' ? 'GET' : 'POST',
  533 + action: url,
  534 + style: 'display:none'
  535 + })
  536 +
  537 + if (method !== 'GET' && method !== 'POST') {
  538 + form.append($('<input>', {
  539 + type: 'hidden',
  540 + name: '_method',
  541 + value: method.toLowerCase()
  542 + }))
  543 + }
  544 +
  545 + var data = options.data
  546 + if (typeof data === 'string') {
  547 + $.each(data.split('&'), function(index, value) {
  548 + var pair = value.split('=')
  549 + form.append($('<input>', {type: 'hidden', name: pair[0], value: pair[1]}))
  550 + })
  551 + } else if ($.isArray(data)) {
  552 + $.each(data, function(index, value) {
  553 + form.append($('<input>', {type: 'hidden', name: value.name, value: value.value}))
  554 + })
  555 + } else if (typeof data === 'object') {
  556 + var key
  557 + for (key in data)
  558 + form.append($('<input>', {type: 'hidden', name: key, value: data[key]}))
  559 + }
  560 +
  561 + $(document.body).append(form)
  562 + form.submit()
  563 +}
  564 +
  565 +// Internal: Abort an XmlHttpRequest if it hasn't been completed,
  566 +// also removing its event handlers.
  567 +function abortXHR(xhr) {
  568 + if ( xhr && xhr.readyState < 4) {
  569 + xhr.onreadystatechange = $.noop
  570 + xhr.abort()
  571 + }
  572 +}
  573 +
  574 +// Internal: Generate unique id for state object.
  575 +//
  576 +// Use a timestamp instead of a counter since ids should still be
  577 +// unique across page loads.
  578 +//
  579 +// Returns Number.
  580 +function uniqueId() {
  581 + return (new Date).getTime()
  582 +}
  583 +
  584 +function cloneContents(container) {
  585 + var cloned = container.clone()
  586 + // Unmark script tags as already being eval'd so they can get executed again
  587 + // when restored from cache. HAXX: Uses jQuery internal method.
  588 + /**
  589 + * 2016年05月19 潘钊修改
  590 + * 在缓存里打一个标记
  591 + * ##################
  592 + */
  593 + cloned.append('<input type="hidden" id="historyCache" value="1"/>')
  594 + /**
  595 + * ##################
  596 + */
  597 + cloned.find('script').each(function(){
  598 + if (!this.src) jQuery._data(this, 'globalEval', false)
  599 + })
  600 + return [container.selector, cloned.contents()]
  601 +}
  602 +
  603 +// Internal: Strip internal query params from parsed URL.
  604 +//
  605 +// Returns sanitized url.href String.
  606 +function stripInternalParams(url) {
  607 + url.search = url.search.replace(/([?&])(_pjax|_)=[^&]*/g, '')
  608 + return url.href.replace(/\?($|#)/, '$1')
  609 +}
  610 +
  611 +// Internal: Parse URL components and returns a Locationish object.
  612 +//
  613 +// url - String URL
  614 +//
  615 +// Returns HTMLAnchorElement that acts like Location.
  616 +function parseURL(url) {
  617 + var a = document.createElement('a')
  618 + a.href = url
  619 + return a
  620 +}
  621 +
  622 +// Internal: Return the `href` component of given URL object with the hash
  623 +// portion removed.
  624 +//
  625 +// location - Location or HTMLAnchorElement
  626 +//
  627 +// Returns String
  628 +function stripHash(location) {
  629 + return location.href.replace(/#.*/, '')
  630 +}
  631 +
  632 +// Internal: Build options Object for arguments.
  633 +//
  634 +// For convenience the first parameter can be either the container or
  635 +// the options object.
  636 +//
  637 +// Examples
  638 +//
  639 +// optionsFor('#container')
  640 +// // => {container: '#container'}
  641 +//
  642 +// optionsFor('#container', {push: true})
  643 +// // => {container: '#container', push: true}
  644 +//
  645 +// optionsFor({container: '#container', push: true})
  646 +// // => {container: '#container', push: true}
  647 +//
  648 +// Returns options Object.
  649 +function optionsFor(container, options) {
  650 + // Both container and options
  651 + if ( container && options )
  652 + options.container = container
  653 +
  654 + // First argument is options Object
  655 + else if ( $.isPlainObject(container) )
  656 + options = container
  657 +
  658 + // Only container
  659 + else
  660 + options = {container: container}
  661 +
  662 + // Find and validate container
  663 + if (options.container)
  664 + options.container = findContainerFor(options.container)
  665 +
  666 + return options
  667 +}
  668 +
  669 +// Internal: Find container element for a variety of inputs.
  670 +//
  671 +// Because we can't persist elements using the history API, we must be
  672 +// able to find a String selector that will consistently find the Element.
  673 +//
  674 +// container - A selector String, jQuery object, or DOM Element.
  675 +//
  676 +// Returns a jQuery object whose context is `document` and has a selector.
  677 +function findContainerFor(container) {
  678 + container = $(container)
  679 +
  680 + if ( !container.length ) {
  681 + throw "no pjax container for " + container.selector
  682 + } else if ( container.selector !== '' && container.context === document ) {
  683 + return container
  684 + } else if ( container.attr('id') ) {
  685 + return $('#' + container.attr('id'))
  686 + } else {
  687 + throw "cant get selector for pjax container!"
  688 + }
  689 +}
  690 +
  691 +// Internal: Filter and find all elements matching the selector.
  692 +//
  693 +// Where $.fn.find only matches descendants, findAll will test all the
  694 +// top level elements in the jQuery object as well.
  695 +//
  696 +// elems - jQuery object of Elements
  697 +// selector - String selector to match
  698 +//
  699 +// Returns a jQuery object.
  700 +function findAll(elems, selector) {
  701 + return elems.filter(selector).add(elems.find(selector));
  702 +}
  703 +
  704 +function parseHTML(html) {
  705 + return $.parseHTML(html, document, true)
  706 +}
  707 +
  708 +// Internal: Extracts container and metadata from response.
  709 +//
  710 +// 1. Extracts X-PJAX-URL header if set
  711 +// 2. Extracts inline <title> tags
  712 +// 3. Builds response Element and extracts fragment if set
  713 +//
  714 +// data - String response data
  715 +// xhr - XHR response
  716 +// options - pjax options Object
  717 +//
  718 +// Returns an Object with url, title, and contents keys.
  719 +function extractContainer(data, xhr, options) {
  720 + var obj = {}, fullDocument = /<html/i.test(data)
  721 +
  722 + // Prefer X-PJAX-URL header if it was set, otherwise fallback to
  723 + // using the original requested url.
  724 + var serverUrl = xhr.getResponseHeader('X-PJAX-URL')
  725 + obj.url = serverUrl ? stripInternalParams(parseURL(serverUrl)) : options.requestUrl
  726 +
  727 + // Attempt to parse response html into elements
  728 + if (fullDocument) {
  729 + var $head = $(parseHTML(data.match(/<head[^>]*>([\s\S.]*)<\/head>/i)[0]))
  730 + var $body = $(parseHTML(data.match(/<body[^>]*>([\s\S.]*)<\/body>/i)[0]))
  731 + } else {
  732 + var $head = $body = $(parseHTML(data))
  733 + }
  734 +
  735 + // If response data is empty, return fast
  736 + if ($body.length === 0)
  737 + return obj
  738 +
  739 + // If there's a <title> tag in the header, use it as
  740 + // the page's title.
  741 + obj.title = findAll($head, 'title').last().text()
  742 +
  743 + if (options.fragment) {
  744 + // If they specified a fragment, look for it in the response
  745 + // and pull it out.
  746 + if (options.fragment === 'body') {
  747 + var $fragment = $body
  748 + } else {
  749 + var $fragment = findAll($body, options.fragment).first()
  750 + }
  751 +
  752 + if ($fragment.length) {
  753 + obj.contents = options.fragment === 'body' ? $fragment : $fragment.contents()
  754 +
  755 + // If there's no title, look for data-title and title attributes
  756 + // on the fragment
  757 + if (!obj.title)
  758 + obj.title = $fragment.attr('title') || $fragment.data('title')
  759 + }
  760 +
  761 + } else if (!fullDocument) {
  762 + obj.contents = $body
  763 + }
  764 +
  765 + // Clean up any <title> tags
  766 + if (obj.contents) {
  767 + // Remove any parent title elements
  768 + obj.contents = obj.contents.not(function() { return $(this).is('title') })
  769 +
  770 + // Then scrub any titles from their descendants
  771 + obj.contents.find('title').remove()
  772 +
  773 + // Gather all script[src] elements
  774 + obj.scripts = findAll(obj.contents, 'script[src]').remove()
  775 + obj.contents = obj.contents.not(obj.scripts)
  776 + }
  777 +
  778 + // Trim any whitespace off the title
  779 + if (obj.title) obj.title = $.trim(obj.title)
  780 +
  781 + return obj
  782 +}
  783 +
  784 +// Load an execute scripts using standard script request.
  785 +//
  786 +// Avoids jQuery's traditional $.getScript which does a XHR request and
  787 +// globalEval.
  788 +//
  789 +// scripts - jQuery object of script Elements
  790 +//
  791 +// Returns nothing.
  792 +function executeScriptTags(scripts) {
  793 + if (!scripts) return
  794 +
  795 + var existingScripts = $('script[src]')
  796 +
  797 + scripts.each(function() {
  798 + var src = this.src
  799 + var matchedScripts = existingScripts.filter(function() {
  800 + return this.src === src
  801 + })
  802 + if (matchedScripts.length) return
  803 +
  804 + var script = document.createElement('script')
  805 + var type = $(this).attr('type')
  806 + if (type) script.type = type
  807 + script.src = $(this).attr('src')
  808 + document.head.appendChild(script)
  809 + })
  810 +}
  811 +
  812 +// Internal: History DOM caching class.
  813 +var cacheMapping = {}
  814 +var cacheForwardStack = []
  815 +var cacheBackStack = []
  816 +
  817 +// Push previous state id and container contents into the history
  818 +// cache. Should be called in conjunction with `pushState` to save the
  819 +// previous container contents.
  820 +//
  821 +// id - State ID Number
  822 +// value - DOM Element to cache
  823 +//
  824 +// Returns nothing.
  825 +function cachePush(id, value) {
  826 + cacheMapping[id] = value
  827 + cacheBackStack.push(id)
  828 +
  829 + // Remove all entries in forward history stack after pushing a new page.
  830 + trimCacheStack(cacheForwardStack, 0)
  831 +
  832 + // Trim back history stack to max cache length.
  833 + trimCacheStack(cacheBackStack, pjax.defaults.maxCacheLength)
  834 +}
  835 +
  836 +// Shifts cache from directional history cache. Should be
  837 +// called on `popstate` with the previous state id and container
  838 +// contents.
  839 +//
  840 +// direction - "forward" or "back" String
  841 +// id - State ID Number
  842 +// value - DOM Element to cache
  843 +//
  844 +// Returns nothing.
  845 +function cachePop(direction, id, value) {
  846 + var pushStack, popStack
  847 + cacheMapping[id] = value
  848 +
  849 + if (direction === 'forward') {
  850 + pushStack = cacheBackStack
  851 + popStack = cacheForwardStack
  852 + } else {
  853 + pushStack = cacheForwardStack
  854 + popStack = cacheBackStack
  855 + }
  856 +
  857 + pushStack.push(id)
  858 + if (id = popStack.pop())
  859 + delete cacheMapping[id]
  860 +
  861 + // Trim whichever stack we just pushed to to max cache length.
  862 + trimCacheStack(pushStack, pjax.defaults.maxCacheLength)
  863 +}
  864 +
  865 +// Trim a cache stack (either cacheBackStack or cacheForwardStack) to be no
  866 +// longer than the specified length, deleting cached DOM elements as necessary.
  867 +//
  868 +// stack - Array of state IDs
  869 +// length - Maximum length to trim to
  870 +//
  871 +// Returns nothing.
  872 +function trimCacheStack(stack, length) {
  873 + while (stack.length > length)
  874 + delete cacheMapping[stack.shift()]
  875 +}
  876 +
  877 +// Public: Find version identifier for the initial page load.
  878 +//
  879 +// Returns String version or undefined.
  880 +function findVersion() {
  881 + return $('meta').filter(function() {
  882 + var name = $(this).attr('http-equiv')
  883 + return name && name.toUpperCase() === 'X-PJAX-VERSION'
  884 + }).attr('content')
  885 +}
  886 +
  887 +// Install pjax functions on $.pjax to enable pushState behavior.
  888 +//
  889 +// Does nothing if already enabled.
  890 +//
  891 +// Examples
  892 +//
  893 +// $.pjax.enable()
  894 +//
  895 +// Returns nothing.
  896 +function enable() {
  897 + $.fn.pjax = fnPjax
  898 + $.pjax = pjax
  899 + $.pjax.enable = $.noop
  900 + $.pjax.disable = disable
  901 + $.pjax.click = handleClick
  902 + $.pjax.submit = handleSubmit
  903 + $.pjax.reload = pjaxReload
  904 + $.pjax.defaults = {
  905 + timeout: 650,
  906 + push: true,
  907 + replace: false,
  908 + type: 'GET',
  909 + dataType: 'html',
  910 + scrollTo: 0,
  911 + maxCacheLength: 20,
  912 + version: findVersion
  913 + }
  914 + $(window).on('popstate.pjax', onPjaxPopstate)
  915 +}
  916 +
  917 +// Disable pushState behavior.
  918 +//
  919 +// This is the case when a browser doesn't support pushState. It is
  920 +// sometimes useful to disable pushState for debugging on a modern
  921 +// browser.
  922 +//
  923 +// Examples
  924 +//
  925 +// $.pjax.disable()
  926 +//
  927 +// Returns nothing.
  928 +function disable() {
  929 + $.fn.pjax = function() { return this }
  930 + $.pjax = fallbackPjax
  931 + $.pjax.enable = enable
  932 + $.pjax.disable = $.noop
  933 + $.pjax.click = $.noop
  934 + $.pjax.submit = $.noop
  935 + $.pjax.reload = function() { window.location.reload() }
  936 +
  937 + $(window).off('popstate.pjax', onPjaxPopstate)
  938 +}
  939 +
  940 +
  941 +// Add the state property to jQuery's event object so we can use it in
  942 +// $(window).bind('popstate')
  943 +if ( $.inArray('state', $.event.props) < 0 )
  944 + $.event.props.push('state')
  945 +
  946 +// Is pjax supported by this browser?
  947 +$.support.pjax =
  948 + window.history && window.history.pushState && window.history.replaceState &&
  949 + // pushState isn't reliable on iOS until 5.
  950 + !navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]\D|WebApps\/.+CFNetwork)/)
  951 +
  952 +$.support.pjax ? enable() : disable()
  953 +
  954 +})(jQuery);