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