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);
... ...