123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925 |
- (function($){
- function fnPjax(selector, container, options) {
- var context = this
- return this.on('click.pjax', selector, function(event) {
- var opts = $.extend({}, optionsFor(container, options))
- if (!opts.container)
- opts.container = $(this).attr('data-pjax') || context
- handleClick(event, opts)
- })
- }
- function handleClick(event, container, options) {
- options = optionsFor(container, options)
- var link = event.currentTarget
- if (link.tagName.toUpperCase() !== 'A')
- throw "$.fn.pjax or $.pjax.click requires an anchor element"
-
-
- if ( event.which > 1 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey )
- return
-
- if ( location.protocol !== link.protocol || location.hostname !== link.hostname )
- return
-
- if ( link.href.indexOf('#') > -1 && stripHash(link) == stripHash(location) )
- return
-
- if (event.isDefaultPrevented())
- return
- var defaults = {
- url: link.href,
- container: $(link).attr('data-pjax'),
- target: link
- }
- var opts = $.extend({}, defaults, options)
- var clickEvent = $.Event('pjax:click')
- $(link).trigger(clickEvent, [opts])
- if (!clickEvent.isDefaultPrevented()) {
- pjax(opts)
- event.preventDefault()
- $(link).trigger('pjax:clicked', [opts])
- }
- }
- function handleSubmit(event, container, options) {
- options = optionsFor(container, options)
- var form = event.currentTarget
- var $form = $(form)
- if (form.tagName.toUpperCase() !== 'FORM')
- throw "$.pjax.submit requires a form element"
- var defaults = {
- type: ($form.attr('method') || 'GET').toUpperCase(),
- url: $form.attr('action'),
- container: $form.attr('data-pjax'),
- target: form
- }
- if (defaults.type !== 'GET' && window.FormData !== undefined) {
- defaults.data = new FormData(form);
- defaults.processData = false;
- defaults.contentType = false;
- } else {
-
- if ($(form).find(':file').length) {
- return;
- }
-
- defaults.data = $(form).serializeArray();
- }
- pjax($.extend({}, defaults, options))
- event.preventDefault()
- }
- function pjax(options) {
- options = $.extend(true, {}, $.ajaxSettings, pjax.defaults, options)
- if ($.isFunction(options.url)) {
- options.url = options.url()
- }
- var target = options.target
- var hash = parseURL(options.url).hash
- var context = options.context = findContainerFor(options.container)
-
-
-
-
- if (!options.data) options.data = {}
- if ($.isArray(options.data)) {
- options.data.push({name: '_pjax', value: context.selector})
- } else {
- options.data._pjax = context.selector
- }
- function fire(type, args, props) {
- if (!props) props = {}
- props.relatedTarget = target
- var event = $.Event(type, props)
- context.trigger(event, args)
- return !event.isDefaultPrevented()
- }
- var timeoutTimer
- options.beforeSend = function(xhr, settings) {
-
-
- if (settings.type !== 'GET') {
- settings.timeout = 0
- }
- xhr.setRequestHeader('X-PJAX', 'true')
- xhr.setRequestHeader('X-PJAX-Container', context.selector)
- if (!fire('pjax:beforeSend', [xhr, settings]))
- return false
- if (settings.timeout > 0) {
- timeoutTimer = setTimeout(function() {
- if (fire('pjax:timeout', [xhr, options]))
- xhr.abort('timeout')
- }, settings.timeout)
-
- settings.timeout = 0
- }
- var url = parseURL(settings.url)
- if (hash) url.hash = hash
- options.requestUrl = stripInternalParams(url)
- }
- options.complete = function(xhr, textStatus) {
- if (timeoutTimer)
- clearTimeout(timeoutTimer)
- fire('pjax:complete', [xhr, textStatus, options])
- fire('pjax:end', [xhr, options])
- }
- options.error = function(xhr, textStatus, errorThrown) {
- var container = extractContainer("", xhr, options)
- var allowed = fire('pjax:error', [xhr, textStatus, errorThrown, options])
- if (options.type == 'GET' && textStatus !== 'abort' && allowed) {
- locationReplace(container.url)
- }
- }
- options.success = function(data, status, xhr) {
- var previousState = pjax.state;
-
-
- var currentVersion = (typeof $.pjax.defaults.version === 'function') ?
- $.pjax.defaults.version() :
- $.pjax.defaults.version
- var latestVersion = xhr.getResponseHeader('X-PJAX-Version')
- var container = extractContainer(data, xhr, options)
- var url = parseURL(container.url)
- if (hash) {
- url.hash = hash
- container.url = url.href
- }
-
- if (currentVersion && latestVersion && currentVersion !== latestVersion) {
- locationReplace(container.url)
- return
- }
-
- if (!container.contents) {
- locationReplace(container.url)
- return
- }
- pjax.state = {
- id: options.id || uniqueId(),
- url: container.url,
- title: container.title,
- container: context.selector,
- fragment: options.fragment,
- timeout: options.timeout
- }
- if (options.push || options.replace) {
- window.history.replaceState(pjax.state, container.title, container.url)
- }
-
- var blurFocus = $.contains(options.container, document.activeElement)
-
- if (blurFocus) {
- try {
- document.activeElement.blur()
- } catch (e) { }
- }
- if (container.title) document.title = container.title
- fire('pjax:beforeReplace', [container.contents, options], {
- state: pjax.state,
- previousState: previousState
- })
- context.html(container.contents)
-
-
-
-
-
- var autofocusEl = context.find('input[autofocus], textarea[autofocus]').last()[0]
- if (autofocusEl && document.activeElement !== autofocusEl) {
- autofocusEl.focus();
- }
- executeScriptTags(container.scripts)
- var scrollTo = options.scrollTo
-
- if (hash) {
- var name = decodeURIComponent(hash.slice(1))
- var target = document.getElementById(name) || document.getElementsByName(name)[0]
- if (target) scrollTo = $(target).offset().top
- }
- if (typeof scrollTo == 'number') $(window).scrollTop(scrollTo)
- fire('pjax:success', [data, status, xhr, options])
- }
-
-
-
-
- if (!pjax.state) {
- pjax.state = {
- id: uniqueId(),
- url: window.location.href,
- title: document.title,
- container: context.selector,
- fragment: options.fragment,
- timeout: options.timeout
- }
- window.history.replaceState(pjax.state, document.title)
- }
-
- abortXHR(pjax.xhr)
- pjax.options = options
- var xhr = pjax.xhr = $.ajax(options)
- if (xhr.readyState > 0) {
- if (options.push && !options.replace) {
-
- cachePush(pjax.state.id, cloneContents(context))
- window.history.pushState(null, "", options.requestUrl)
- }
- fire('pjax:start', [xhr, options])
- fire('pjax:send', [xhr, options])
- }
- return pjax.xhr
- }
- function pjaxReload(container, options) {
- var defaults = {
- url: window.location.href,
- push: false,
- replace: true,
- scrollTo: false
- }
- return pjax($.extend(defaults, optionsFor(container, options)))
- }
- function locationReplace(url) {
- window.history.replaceState(null, "", pjax.state.url)
- window.location.replace(url)
- }
- var initialPop = true
- var initialURL = window.location.href
- var initialState = window.history.state
- if (initialState && initialState.container) {
- pjax.state = initialState
- }
- if ('state' in window.history) {
- initialPop = false
- }
- function onPjaxPopstate(event) {
-
- if (!initialPop) {
- abortXHR(pjax.xhr)
- }
- var previousState = pjax.state
- var state = event.state
- var direction
- if (state && state.container) {
-
-
-
- if (initialPop && initialURL == state.url) return
- if (previousState) {
-
-
- if (previousState.id === state.id) return
-
- direction = previousState.id < state.id ? 'forward' : 'back'
- }
- var cache = cacheMapping[state.id] || []
- var container = $(cache[0] || state.container), contents = cache[1]
- if (container.length) {
- if (previousState) {
-
-
- cachePop(direction, previousState.id, cloneContents(container))
- }
- var popstateEvent = $.Event('pjax:popstate', {
- state: state,
- direction: direction
- })
- container.trigger(popstateEvent)
- var options = {
- id: state.id,
- url: state.url,
- container: container,
- push: false,
- fragment: state.fragment,
- timeout: state.timeout,
- scrollTo: false
- }
- if (contents) {
- container.trigger('pjax:start', [null, options])
- pjax.state = state
- if (state.title) document.title = state.title
- var beforeReplaceEvent = $.Event('pjax:beforeReplace', {
- state: state,
- previousState: previousState
- })
- container.trigger(beforeReplaceEvent, [contents, options])
- container.html(contents)
- container.trigger('pjax:end', [null, options])
- } else {
- pjax(options)
- }
-
-
- container[0].offsetHeight
- } else {
- locationReplace(location.href)
- }
- }
- initialPop = false
- }
- function fallbackPjax(options) {
- var url = $.isFunction(options.url) ? options.url() : options.url,
- method = options.type ? options.type.toUpperCase() : 'GET'
- var form = $('<form>', {
- method: method === 'GET' ? 'GET' : 'POST',
- action: url,
- style: 'display:none'
- })
- if (method !== 'GET' && method !== 'POST') {
- form.append($('<input>', {
- type: 'hidden',
- name: '_method',
- value: method.toLowerCase()
- }))
- }
- var data = options.data
- if (typeof data === 'string') {
- $.each(data.split('&'), function(index, value) {
- var pair = value.split('=')
- form.append($('<input>', {type: 'hidden', name: pair[0], value: pair[1]}))
- })
- } else if ($.isArray(data)) {
- $.each(data, function(index, value) {
- form.append($('<input>', {type: 'hidden', name: value.name, value: value.value}))
- })
- } else if (typeof data === 'object') {
- var key
- for (key in data)
- form.append($('<input>', {type: 'hidden', name: key, value: data[key]}))
- }
- $(document.body).append(form)
- form.submit()
- }
- function abortXHR(xhr) {
- if ( xhr && xhr.readyState < 4) {
- xhr.onreadystatechange = $.noop
- xhr.abort()
- }
- }
- function uniqueId() {
- return (new Date).getTime()
- }
- function cloneContents(container) {
- var cloned = container.clone()
-
-
- cloned.find('script').each(function(){
- if (!this.src) jQuery._data(this, 'globalEval', false)
- })
- return [container.selector, cloned.contents()]
- }
- function stripInternalParams(url) {
- url.search = url.search.replace(/([?&])(_pjax|_)=[^&]*/g, '')
- return url.href.replace(/\?($|#)/, '$1')
- }
- function parseURL(url) {
- var a = document.createElement('a')
- a.href = url
- return a
- }
- function stripHash(location) {
- return location.href.replace(/#.*/, '')
- }
- function optionsFor(container, options) {
-
- if ( container && options )
- options.container = container
-
- else if ( $.isPlainObject(container) )
- options = container
-
- else
- options = {container: container}
-
- if (options.container)
- options.container = findContainerFor(options.container)
- return options
- }
- function findContainerFor(container) {
- container = $(container)
- if ( !container.length ) {
- throw "no pjax container for " + container.selector
- } else if ( container.selector !== '' && container.context === document ) {
- return container
- } else if ( container.attr('id') ) {
- return $('#' + container.attr('id'))
- } else {
- throw "cant get selector for pjax container!"
- }
- }
- function findAll(elems, selector) {
- return elems.filter(selector).add(elems.find(selector));
- }
- function parseHTML(html) {
- return $.parseHTML(html, document, true)
- }
- function extractContainer(data, xhr, options) {
- var obj = {}, fullDocument = /<html/i.test(data)
-
-
- var serverUrl = xhr.getResponseHeader('X-PJAX-URL')
- obj.url = serverUrl ? stripInternalParams(parseURL(serverUrl)) : options.requestUrl
-
- if (fullDocument) {
- var $head = $(parseHTML(data.match(/<head[^>]*>([\s\S.]*)<\/head>/i)[0]))
- var $body = $(parseHTML(data.match(/<body[^>]*>([\s\S.]*)<\/body>/i)[0]))
- } else {
- var $head = $body = $(parseHTML(data))
- }
-
- if ($body.length === 0)
- return obj
-
-
- obj.title = findAll($head, 'title').last().text()
- if (options.fragment) {
-
-
- if (options.fragment === 'body') {
- var $fragment = $body
- } else {
- var $fragment = findAll($body, options.fragment).first()
- }
- if ($fragment.length) {
- obj.contents = options.fragment === 'body' ? $fragment : $fragment.contents()
-
-
- if (!obj.title)
- obj.title = $fragment.attr('title') || $fragment.data('title')
- }
- } else if (!fullDocument) {
- obj.contents = $body
- }
-
- if (obj.contents) {
-
- obj.contents = obj.contents.not(function() { return $(this).is('title') })
-
- obj.contents.find('title').remove()
-
- obj.scripts = findAll(obj.contents, 'script[src]').remove()
- obj.contents = obj.contents.not(obj.scripts)
- }
-
- if (obj.title) obj.title = $.trim(obj.title)
- return obj
- }
- function executeScriptTags(scripts) {
- if (!scripts) return
- var existingScripts = $('script[src]')
- scripts.each(function() {
- var src = this.src
- var matchedScripts = existingScripts.filter(function() {
- return this.src === src
- })
- if (matchedScripts.length) return
- var script = document.createElement('script')
- var type = $(this).attr('type')
- if (type) script.type = type
- script.src = $(this).attr('src')
- document.head.appendChild(script)
- })
- }
- var cacheMapping = {}
- var cacheForwardStack = []
- var cacheBackStack = []
- function cachePush(id, value) {
- cacheMapping[id] = value
- cacheBackStack.push(id)
-
- trimCacheStack(cacheForwardStack, 0)
-
- trimCacheStack(cacheBackStack, pjax.defaults.maxCacheLength)
- }
- function cachePop(direction, id, value) {
- var pushStack, popStack
- cacheMapping[id] = value
- if (direction === 'forward') {
- pushStack = cacheBackStack
- popStack = cacheForwardStack
- } else {
- pushStack = cacheForwardStack
- popStack = cacheBackStack
- }
- pushStack.push(id)
- if (id = popStack.pop())
- delete cacheMapping[id]
-
- trimCacheStack(pushStack, pjax.defaults.maxCacheLength)
- }
- function trimCacheStack(stack, length) {
- while (stack.length > length)
- delete cacheMapping[stack.shift()]
- }
- function findVersion() {
- return $('meta').filter(function() {
- var name = $(this).attr('http-equiv')
- return name && name.toUpperCase() === 'X-PJAX-VERSION'
- }).attr('content')
- }
- function enable() {
- $.fn.pjax = fnPjax
- $.pjax = pjax
- $.pjax.enable = $.noop
- $.pjax.disable = disable
- $.pjax.click = handleClick
- $.pjax.submit = handleSubmit
- $.pjax.reload = pjaxReload
- $.pjax.defaults = {
- timeout: 650,
- push: true,
- replace: false,
- type: 'GET',
- dataType: 'html',
- scrollTo: 0,
- maxCacheLength: 20,
- version: findVersion
- }
- $(window).on('popstate.pjax', onPjaxPopstate)
- }
- function disable() {
- $.fn.pjax = function() { return this }
- $.pjax = fallbackPjax
- $.pjax.enable = enable
- $.pjax.disable = $.noop
- $.pjax.click = $.noop
- $.pjax.submit = $.noop
- $.pjax.reload = function() { window.location.reload() }
- $(window).off('popstate.pjax', onPjaxPopstate)
- }
- if ( $.inArray('state', $.event.props) < 0 )
- $.event.props.push('state')
- $.support.pjax =
- window.history && window.history.pushState && window.history.replaceState &&
-
- !navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]\D|WebApps\/.+CFNetwork)/)
- $.support.pjax ? enable() : disable()
- })(jQuery);
|