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 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. 171function 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 options.requestUrl = parseURL(settings.url).href 229 } 230 231 options.complete = function(xhr, textStatus) { 232 if (timeoutTimer) 233 clearTimeout(timeoutTimer) 234 235 fire('pjax:complete', [xhr, textStatus, options]) 236 237 fire('pjax:end', [xhr, options]) 238 } 239 240 options.error = function(xhr, textStatus, errorThrown) { 241 var container = extractContainer("", xhr, options) 242 243 var allowed = fire('pjax:error', [xhr, textStatus, errorThrown, options]) 244 if (options.type == 'GET' && textStatus !== 'abort' && allowed) { 245 locationReplace(container.url) 246 } 247 } 248 249 options.success = function(data, status, xhr) { 250 var previousState = pjax.state; 251 252 // If $.pjax.defaults.version is a function, invoke it first. 253 // Otherwise it can be a static string. 254 var currentVersion = (typeof $.pjax.defaults.version === 'function') ? 255 $.pjax.defaults.version() : 256 $.pjax.defaults.version 257 258 var latestVersion = xhr.getResponseHeader('X-PJAX-Version') 259 260 var container = extractContainer(data, xhr, options) 261 262 // If there is a layout version mismatch, hard load the new url 263 if (currentVersion && latestVersion && currentVersion !== latestVersion) { 264 locationReplace(container.url) 265 return 266 } 267 268 // If the new response is missing a body, hard load the page 269 if (!container.contents) { 270 locationReplace(container.url) 271 return 272 } 273 274 pjax.state = { 275 id: options.id || uniqueId(), 276 url: container.url, 277 title: container.title, 278 container: context.selector, 279 fragment: options.fragment, 280 timeout: options.timeout 281 } 282 283 if (options.push || options.replace) { 284 window.history.replaceState(pjax.state, container.title, container.url) 285 } 286 287 // Clear out any focused controls before inserting new page contents. 288 try { 289 document.activeElement.blur() 290 } catch (e) { } 291 292 if (container.title) document.title = container.title 293 294 fire('pjax:beforeReplace', [container.contents, options], { 295 state: pjax.state, 296 previousState: previousState 297 }) 298 context.html(container.contents) 299 300 // FF bug: Won't autofocus fields that are inserted via JS. 301 // This behavior is incorrect. So if theres no current focus, autofocus 302 // the last field. 303 // 304 // http://www.w3.org/html/wg/drafts/html/master/forms.html 305 var autofocusEl = context.find('input[autofocus], textarea[autofocus]').last()[0] 306 if (autofocusEl && document.activeElement !== autofocusEl) { 307 autofocusEl.focus(); 308 } 309 310 executeScriptTags(container.scripts) 311 312 // Scroll to top by default 313 if (typeof options.scrollTo === 'number') 314 $(window).scrollTop(options.scrollTo) 315 316 // If the URL has a hash in it, make sure the browser 317 // knows to navigate to the hash. 318 if ( hash !== '' ) { 319 // Avoid using simple hash set here. Will add another history 320 // entry. Replace the url with replaceState and scroll to target 321 // by hand. 322 // 323 // window.location.hash = hash 324 var url = parseURL(container.url) 325 url.hash = hash 326 327 pjax.state.url = url.href 328 window.history.replaceState(pjax.state, container.title, url.href) 329 330 var target = document.getElementById(url.hash.slice(1)) 331 if (target) $(window).scrollTop($(target).offset().top) 332 } 333 334 fire('pjax:success', [data, status, xhr, options]) 335 } 336 337 338 // Initialize pjax.state for the initial page load. Assume we're 339 // using the container and options of the link we're loading for the 340 // back button to the initial page. This ensures good back button 341 // behavior. 342 if (!pjax.state) { 343 pjax.state = { 344 id: uniqueId(), 345 url: window.location.href, 346 title: document.title, 347 container: context.selector, 348 fragment: options.fragment, 349 timeout: options.timeout 350 } 351 window.history.replaceState(pjax.state, document.title) 352 } 353
vendor: 14,519 bytes, lines 354-889
354 // Cancel the current request if we're already pjaxing 355 var xhr = pjax.xhr 356 if ( xhr && xhr.readyState < 4) { 357 xhr.onreadystatechange = $.noop 358 xhr.abort() 359 } 360 361 pjax.options = options 362 var xhr = pjax.xhr = $.ajax(options) 363 364 if (xhr.readyState > 0) { 365 if (options.push && !options.replace) { 366 // Cache current container element before replacing it 367 cachePush(pjax.state.id, context.clone().contents()) 368 369 window.history.pushState(null, "", stripPjaxParam(options.requestUrl)) 370 } 371 372 fire('pjax:start', [xhr, options]) 373 fire('pjax:send', [xhr, options]) 374 } 375 376 return pjax.xhr 377} 378 379// Public: Reload current page with pjax. 380// 381// Returns whatever $.pjax returns. 382function pjaxReload(container, options) { 383 var defaults = { 384 url: window.location.href, 385 push: false, 386 replace: true, 387 scrollTo: false 388 } 389 390 return pjax($.extend(defaults, optionsFor(container, options))) 391} 392 393// Internal: Hard replace current state with url. 394// 395// Work for around WebKit 396// https://bugs.webkit.org/show_bug.cgi?id=93506 397// 398// Returns nothing. 399function locationReplace(url) { 400 window.history.replaceState(null, "", pjax.state.url) 401 window.location.replace(url) 402} 403 404 405var initialPop = true 406var initialURL = window.location.href 407var initialState = window.history.state 408 409// Initialize $.pjax.state if possible 410// Happens when reloading a page and coming forward from a different 411// session history. 412if (initialState && initialState.container) { 413 pjax.state = initialState 414} 415 416// Non-webkit browsers don't fire an initial popstate event 417if ('state' in window.history) { 418 initialPop = false 419} 420 421// popstate handler takes care of the back and forward buttons 422// 423// You probably shouldn't use pjax on pages with other pushState 424// stuff yet. 425function onPjaxPopstate(event) { 426 var previousState = pjax.state; 427 var state = event.state 428 429 if (state && state.container) { 430 // When coming forward from a separate history session, will get an 431 // initial pop with a state we are already at. Skip reloading the current 432 // page. 433 if (initialPop && initialURL == state.url) return 434 435 // If popping back to the same state, just skip. 436 // Could be clicking back from hashchange rather than a pushState. 437 if (pjax.state && pjax.state.id === state.id) return 438 439 var container = $(state.container) 440 if (container.length) { 441 var direction, contents = cacheMapping[state.id] 442 443 if (pjax.state) { 444 // Since state ids always increase, we can deduce the history 445 // direction from the previous state. 446 direction = pjax.state.id < state.id ? 'forward' : 'back' 447 448 // Cache current container before replacement and inform the 449 // cache which direction the history shifted. 450 cachePop(direction, pjax.state.id, container.clone().contents()) 451 } 452 453 var popstateEvent = $.Event('pjax:popstate', { 454 state: state, 455 direction: direction 456 }) 457 container.trigger(popstateEvent) 458 459 var options = { 460 id: state.id, 461 url: state.url, 462 container: container, 463 push: false, 464 fragment: state.fragment, 465 timeout: state.timeout, 466 scrollTo: false 467 } 468 469 if (contents) { 470 container.trigger('pjax:start', [null, options]) 471 472 pjax.state = state 473 if (state.title) document.title = state.title 474 var beforeReplaceEvent = $.Event('pjax:beforeReplace', { 475 state: state, 476 previousState: previousState 477 }) 478 container.trigger(beforeReplaceEvent, [contents, options]) 479 container.html(contents) 480 481 container.trigger('pjax:end', [null, options]) 482 } else { 483 pjax(options) 484 } 485 486 // Force reflow/relayout before the browser tries to restore the 487 // scroll position. 488 container[0].offsetHeight 489 } else { 490 locationReplace(location.href) 491 } 492 } 493 initialPop = false 494} 495 496// Fallback version of main pjax function for browsers that don't 497// support pushState. 498// 499// Returns nothing since it retriggers a hard form submission. 500function fallbackPjax(options) { 501 var url = $.isFunction(options.url) ? options.url() : options.url, 502 method = options.type ? options.type.toUpperCase() : 'GET' 503 504 var form = $('<form>', { 505 method: method === 'GET' ? 'GET' : 'POST', 506 action: url, 507 style: 'display:none' 508 }) 509 510 if (method !== 'GET' && method !== 'POST') { 511 form.append($('<input>', { 512 type: 'hidden', 513 name: '_method', 514 value: method.toLowerCase() 515 })) 516 } 517 518 var data = options.data 519 if (typeof data === 'string') { 520 $.each(data.split('&'), function(index, value) { 521 var pair = value.split('=') 522 form.append($('<input>', {type: 'hidden', name: pair[0], value: pair[1]})) 523 }) 524 } else if (typeof data === 'object') { 525 for (key in data) 526 form.append($('<input>', {type: 'hidden', name: key, value: data[key]})) 527 } 528 529 $(document.body).append(form) 530 form.submit() 531} 532 533// Internal: Generate unique id for state object. 534// 535// Use a timestamp instead of a counter since ids should still be 536// unique across page loads. 537// 538// Returns Number. 539function uniqueId() { 540 return (new Date).getTime() 541} 542 543// Internal: Strips _pjax param from url 544// 545// url - String 546// 547// Returns String. 548function stripPjaxParam(url) { 549 return url 550 .replace(/\?_pjax=[^&]+&?/, '?') 551 .replace(/_pjax=[^&]+&?/, '') 552 .replace(/[\?&]$/, '') 553} 554 555// Internal: Parse URL components and returns a Locationish object. 556// 557// url - String URL 558// 559// Returns HTMLAnchorElement that acts like Location. 560function parseURL(url) { 561 var a = document.createElement('a') 562 a.href = url 563 return a 564} 565 566// Internal: Return the `href` component of given URL object with the hash 567// portion removed. 568// 569// location - Location or HTMLAnchorElement 570// 571// Returns String 572function stripHash(location) { 573 return location.href.replace(/#.*/, '') 574} 575 576// Internal: Build options Object for arguments. 577// 578// For convenience the first parameter can be either the container or 579// the options object. 580// 581// Examples 582// 583// optionsFor('#container') 584// // => {container: '#container'} 585// 586// optionsFor('#container', {push: true}) 587// // => {container: '#container', push: true} 588// 589// optionsFor({container: '#container', push: true}) 590// // => {container: '#container', push: true} 591// 592// Returns options Object. 593function optionsFor(container, options) { 594 // Both container and options 595 if ( container && options ) 596 options.container = container 597 598 // First argument is options Object 599 else if ( $.isPlainObject(container) ) 600 options = container 601 602 // Only container 603 else 604 options = {container: container} 605 606 // Find and validate container 607 if (options.container) 608 options.container = findContainerFor(options.container) 609 610 return options 611} 612 613// Internal: Find container element for a variety of inputs. 614// 615// Because we can't persist elements using the history API, we must be 616// able to find a String selector that will consistently find the Element. 617// 618// container - A selector String, jQuery object, or DOM Element. 619// 620// Returns a jQuery object whose context is `document` and has a selector. 621function findContainerFor(container) { 622 container = $(container) 623 624 if ( !container.length ) { 625 throw "no pjax container for " + container.selector 626 } else if ( container.selector !== '' && container.context === document ) { 627 return container 628 } else if ( container.attr('id') ) { 629 return $('#' + container.attr('id')) 630 } else { 631 throw "cant get selector for pjax container!" 632 } 633} 634 635// Internal: Filter and find all elements matching the selector. 636// 637// Where $.fn.find only matches descendants, findAll will test all the 638// top level elements in the jQuery object as well. 639// 640// elems - jQuery object of Elements 641// selector - String selector to match 642// 643// Returns a jQuery object. 644function findAll(elems, selector) { 645 return elems.filter(selector).add(elems.find(selector)); 646} 647 648function parseHTML(html) { 649 return $.parseHTML(html, document, true) 650} 651 652// Internal: Extracts container and metadata from response. 653// 654// 1. Extracts X-PJAX-URL header if set 655// 2. Extracts inline <title> tags 656// 3. Builds response Element and extracts fragment if set 657// 658// data - String response data 659// xhr - XHR response 660// options - pjax options Object 661// 662// Returns an Object with url, title, and contents keys. 663function extractContainer(data, xhr, options) { 664 var obj = {}, fullDocument = /<html/i.test(data) 665 666 // Prefer X-PJAX-URL header if it was set, otherwise fallback to 667 // using the original requested url. 668 obj.url = stripPjaxParam(xhr.getResponseHeader('X-PJAX-URL') || options.requestUrl) 669 670 // Attempt to parse response html into elements 671 if (fullDocument) { 672 var $head = $(parseHTML(data.match(/<head[^>]*>([\s\S.]*)<\/head>/i)[0])) 673 var $body = $(parseHTML(data.match(/<body[^>]*>([\s\S.]*)<\/body>/i)[0])) 674 } else { 675 var $head = $body = $(parseHTML(data)) 676 } 677 678 // If response data is empty, return fast 679 if ($body.length === 0) 680 return obj 681 682 // If there's a <title> tag in the header, use it as 683 // the page's title. 684 obj.title = findAll($head, 'title').last().text() 685 686 if (options.fragment) { 687 // If they specified a fragment, look for it in the response 688 // and pull it out. 689 if (options.fragment === 'body') { 690 var $fragment = $body 691 } else { 692 var $fragment = findAll($body, options.fragment).first() 693 } 694 695 if ($fragment.length) { 696 obj.contents = options.fragment === 'body' ? $fragment : $fragment.contents() 697 698 // If there's no title, look for data-title and title attributes 699 // on the fragment 700 if (!obj.title) 701 obj.title = $fragment.attr('title') || $fragment.data('title') 702 } 703 704 } else if (!fullDocument) { 705 obj.contents = $body 706 } 707 708 // Clean up any <title> tags 709 if (obj.contents) { 710 // Remove any parent title elements 711 obj.contents = obj.contents.not(function() { return $(this).is('title') }) 712 713 // Then scrub any titles from their descendants 714 obj.contents.find('title').remove() 715 716 // Gather all script[src] elements 717 obj.scripts = findAll(obj.contents, 'script[src]').remove() 718 obj.contents = obj.contents.not(obj.scripts) 719 } 720 721 // Trim any whitespace off the title 722 if (obj.title) obj.title = $.trim(obj.title) 723 724 return obj 725} 726 727// Load an execute scripts using standard script request. 728// 729// Avoids jQuery's traditional $.getScript which does a XHR request and 730// globalEval. 731// 732// scripts - jQuery object of script Elements 733// 734// Returns nothing. 735function executeScriptTags(scripts) { 736 if (!scripts) return 737 738 var existingScripts = $('script[src]') 739 740 scripts.each(function() { 741 var src = this.src 742 var matchedScripts = existingScripts.filter(function() { 743 return this.src === src 744 }) 745 if (matchedScripts.length) return 746 747 var script = document.createElement('script') 748 var type = $(this).attr('type') 749 if (type) script.type = type 750 script.src = $(this).attr('src') 751 document.head.appendChild(script) 752 }) 753} 754 755// Internal: History DOM caching class. 756var cacheMapping = {} 757var cacheForwardStack = [] 758var cacheBackStack = [] 759 760// Push previous state id and container contents into the history 761// cache. Should be called in conjunction with `pushState` to save the 762// previous container contents. 763// 764// id - State ID Number 765// value - DOM Element to cache 766// 767// Returns nothing. 768function cachePush(id, value) { 769 cacheMapping[id] = value 770 cacheBackStack.push(id) 771 772 // Remove all entries in forward history stack after pushing a new page. 773 trimCacheStack(cacheForwardStack, 0) 774 775 // Trim back history stack to max cache length. 776 trimCacheStack(cacheBackStack, pjax.defaults.maxCacheLength) 777} 778 779// Shifts cache from directional history cache. Should be 780// called on `popstate` with the previous state id and container 781// contents. 782// 783// direction - "forward" or "back" String 784// id - State ID Number 785// value - DOM Element to cache 786// 787// Returns nothing. 788function cachePop(direction, id, value) { 789 var pushStack, popStack 790 cacheMapping[id] = value 791 792 if (direction === 'forward') { 793 pushStack = cacheBackStack 794 popStack = cacheForwardStack 795 } else { 796 pushStack = cacheForwardStack 797 popStack = cacheBackStack 798 } 799 800 pushStack.push(id) 801 if (id = popStack.pop()) 802 delete cacheMapping[id] 803 804 // Trim whichever stack we just pushed to to max cache length. 805 trimCacheStack(pushStack, pjax.defaults.maxCacheLength) 806} 807 808// Trim a cache stack (either cacheBackStack or cacheForwardStack) to be no 809// longer than the specified length, deleting cached DOM elements as necessary. 810// 811// stack - Array of state IDs 812// length - Maximum length to trim to 813// 814// Returns nothing. 815function trimCacheStack(stack, length) { 816 while (stack.length > length) 817 delete cacheMapping[stack.shift()] 818} 819 820// Public: Find version identifier for the initial page load. 821// 822// Returns String version or undefined. 823function findVersion() { 824 return $('meta').filter(function() { 825 var name = $(this).attr('http-equiv') 826 return name && name.toUpperCase() === 'X-PJAX-VERSION' 827 }).attr('content') 828} 829 830// Install pjax functions on $.pjax to enable pushState behavior. 831// 832// Does nothing if already enabled. 833// 834// Examples 835// 836// $.pjax.enable() 837// 838// Returns nothing. 839function enable() { 840 $.fn.pjax = fnPjax 841 $.pjax = pjax 842 $.pjax.enable = $.noop 843 $.pjax.disable = disable 844 $.pjax.click = handleClick 845 $.pjax.submit = handleSubmit 846 $.pjax.reload = pjaxReload 847 $.pjax.defaults = { 848 timeout: 650, 849 push: true, 850 replace: false, 851 type: 'GET', 852 dataType: 'html', 853 scrollTo: 0, 854 maxCacheLength: 20, 855 version: findVersion 856 } 857 $(window).on('popstate.pjax', onPjaxPopstate) 858} 859 860// Disable pushState behavior. 861// 862// This is the case when a browser doesn't support pushState. It is 863// sometimes useful to disable pushState for debugging on a modern 864// browser. 865// 866// Examples 867// 868// $.pjax.disable() 869// 870// Returns nothing. 871function disable() { 872 $.fn.pjax = function() { return this } 873 $.pjax = fallbackPjax 874 $.pjax.enable = enable 875 $.pjax.disable = $.noop 876 $.pjax.click = $.noop 877 $.pjax.submit = $.noop 878 $.pjax.reload = function() { window.location.reload() } 879 880 $(window).off('popstate.pjax', onPjaxPopstate) 881} 882 883 884// Add the state property to jQuery's event object so we can use it in 885// $(window).bind('popstate') 886if ( $.inArray('state', $.event.props) < 0 ) 887 $.event.props.push('state') 888 889// Is pjax supported by this browser?
890$.support.pjax = 891 window.history && window.history.pushState && window.history.replaceState && 892 // pushState isn't reliable on iOS until 5. 893 !navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]\D|WebApps\/.+CFNetwork)/) 894 895$.support.pjax ? enable() : disable() 896 897})(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.