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