Permalink
Browse files

pjax

  • Loading branch information...
0 parents commit 3efcc3c968c18c4deeb27a1bab1b7306ca4b6e99 @defunkt defunkt committed Feb 26, 2011
Showing with 279 additions and 0 deletions.
  1. +20 −0 LICENSE
  2. +117 −0 README
  3. +142 −0 jquery.pjax.js
20 LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) Chris Wanstrath
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+Software), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
117 README
@@ -0,0 +1,117 @@
+## pushState + ajax = pjax
+
+ .--.
+ / \
+ ## a a
+ ( '._)
+ |'-- |
+ _.\___/_ ___pjax___
+ ."\> \Y/|<'. '._.-'
+ / \ \_\/ / '-' /
+ | --'\_/|/ | _/
+ |___.-' | |`'`
+ | | |
+ | / './
+ /__./` | |
+ \ | |
+ \ | |
+ ; | |
+ / | |
+ jgs |___\_.\_
+ `-"--'---'
+
+
+## what is it?
+
+pjax loads content from your server into an existing page
+without reloading that entire page while ensuring permalinks,
+titles, and the back button work as expected. It enhances the
+browsing experience of your users - nothing more.
+
+
+## three ways to pjax on the client side:
+
+1. Functionally obtrusive, loading the href with ajax into data-pjax:
+
+ <a href='/explore' data-pjax='#main'>Explore</a>
+
+ $('a[data-pjax]').pjax()
+
+
+2. Slightly obtrusive, passing a container and jQuery ajax options:
+
+ <a href='/explore' class='js-pjax'>Explore</a>
+
+ $('a.js-pjax').pjax('#main', { error: function(){
+ $('.error').text('Something went wrong!')
+ })
+
+
+3. Unobtrusive, using the pjax 'loading' callback:
+
+ <div id='main'>
+ <div class='loader' style='display:none'><img src='spin.gif'></div>
+ <div class='tabs'>
+ <a href='/explore'>Explore</a>
+ <a href='/help'>Help</a>
+ </div>
+ </div>
+
+ $('a').pjax('#main', { loading: function(){
+ this == the target container == #main
+ $(this).find('.loader').show()
+ })
+
+
+## $(link).pjax( container, options )
+
+The $(link).pjax() function accepts a container, an options object,
+or both. The options are the same as jQuery's ajax options with the
+following additions:
+
+container - The selector of the container to load the reponse body into, or
+ the container itself.
+ push - Whether to pushState the URL. Defaults to true (of course).
+ loading - A callback to fire after it's been too many ms and
+ you want to ease the user's pain with a loading indicator.
+ You can also bind to the 'loading.pjax' event on container.
+
+
+## $.pjax( options )
+
+You can also just call $.pjax directly. It acts much like $.ajax, even
+returning the same thing and accepting the same options.
+
+The pjax-specific keys listed in the $(link).pjax() section work here
+as well.
+
+This pjax call:
+
+ $.pjax({
+ url: '/authors',
+ container: '#main'
+ })
+
+Roughly translates into this ajax call:
+
+ $.ajax({
+ url: '/authors?pjax=true',
+ dataType: 'html',
+ success: function(data){
+ $('#main').html( data )
+ history.pushState( null, $(data).filter('title').text(), '/authors' )
+ })
+ })
+
+
+## install it
+
+ $ cd path/to/js
+ $ curl -O https://github.com/defunkt/jquery-pjax/raw/master/jquery.pjax.js
+
+Then, in your HTML:
+
+ <script src="path/to/js/jquery.pjax.js"></script>
+
+Replace 'path/to/js' with the path to your JavaScript directory,
+e.g. 'public/javascripts'.
142 jquery.pjax.js
@@ -0,0 +1,142 @@
+// When called on a link, fetches the href with ajax into the
+// container specified as the first parameter or with the data-pjax
+// attribute on the link itself.
+//
+// Tries to make sure the back button and ctrl+click work the way
+// you'd expect.
+//
+// Accepts a jQuery ajax options object that may include these
+// pjax specific options:
+//
+// container - Where to stick the response body. Usually a String selector.
+// $(container).html(xhr.responseBody)
+// loading - A callback to fire after it's been too many ms and
+// you want to ease the user's pain with a loading indicator.
+// You can also bind to the 'loading.pjax' event on container.
+// push - Whether to pushState the URL. Defaults to true (of course).
+//
+// For convenience the first parameter can be either the container or
+// the options object.
+//
+// Returns the jQuery object
+jQuery.fn.pjax = function( container, options ) {
+ var $ = jQuery
+
+ if ( options )
+ options.container = container
+ else
+ options = container
+
+ $(this).live('click', function(){
+ // Middle click, cmd click, and ctrl click should open
+ // links in a new tab as normal.
+ if ( event.which == 2 || event.metaKey )
+ return true
+
+ var defaults = {
+ url: this.href,
+ container: $(this).attr('data-pjax')
+ }
+
+ $.pjax( $.extend({}, defaults, options) )
+
+ return false
+ })
+}
+
+
+// Loads a URL with ajax, puts the response body inside a container,
+// then pushState()'s the loaded URL. Also ensures the back button
+// works the way you'd expect.
+//
+// Works just like $.ajax in that it accepts a jQuery ajax
+// settings object (with keys like url, type, data, etc).
+//
+// Accepts these extra keys:
+//
+// container - Where to stick the response body.
+// $(container).html(xhr.responseBody)
+// loading - A callback to fire after it's been too many ms and
+// you want to ease the user's pain with a loading indicator.
+// You can also bind to the 'loading.pjax' event on container.
+// push - Whether to pushState the URL. Defaults to true (of course).
+//
+// Use it just like $.ajax:
+//
+// var xhr = $.pjax({ url: this.href, container: '#main' })
+// console.log( xhr.readyState )
+//
+// Returns whatever $.ajax returns.
+jQuery.pjax = function( options ) {
+ // Helper
+ var $ = jQuery
+
+ var defaults = {
+ data: { pjax: true },
+ type: 'GET',
+ dataType: 'html',
+ error: function(){ window.location = options.url },
+ success: function(data){
+ // If we got no data or an entire web page, go directly
+ // to the page and let normal error handling happen.
+ if ( !$.trim(data) || /<html/i.test(data) )
+ return window.location = options.url
+
+ // If there's a <title> tag in the response, use it as
+ // the page's title.
+ var title = $.trim( $(data).filter('title').remove().text() )
+ if ( title ) document.title = title
+
+ // Make it happen.
+ $(options.container).html( data )
+
+ // If they didn't explicitly disable `push`, call pushState()
+ if ( options.push !== false ) {
+ window.history.pushState( { pjax: options.container },
+ document.title, options.url )
+ }
+
+ // Invoke their success handler if they gave us one.
+ success.apply( this, arguments )
+ }
+ }
+
+ // We don't want to let anyone override our success handler.
+ var success = options.success || $.noop
+ delete options.success
+
+ options = $.extend( true, {}, defaults, options )
+ var xhr = $.ajax( options )
+
+ // If we haven't found what we're looking for after a buncha ms
+ // you might want to show a 'Loading...' indicator.
+ setTimeout(function(){
+ if ( xhr.readyState == 4 ) return
+ $(options.container).trigger('loading.pjax')
+ if ( options.loading ) options.loading.call( options.container )
+ }, 350)
+
+ $(document).trigger('pjax', xhr, options)
+ return xhr
+}
+
+// Bind our popstate handler which takes care of the back and
+// forward buttons, but only once we've called pjax()
+jQuery(document).one('pjax', function(){
+ jQuery(window).bind('popstate', function(event){
+ var state = event.state
+ if ( state && state.pjax )
+ jQuery.pjax({ url: location.href, container: state.pjax, push: false })
+ else
+ window.location = location.href
+ })
+})
+
+// Add the state property to jQuery's event object so we can use it in
+// $(window).bind('popstate')
+jQuery.event.props.push('state')
+
+// Fall back to normalcy for older browsers.
+if ( !window.history || !window.history.pushState ) {
+ jQuery.pjax = jQuery.fn.pjax = $.noop
+}

0 comments on commit 3efcc3c

Please sign in to comment.