Skip to content

Commit

Permalink
added pjax
Browse files Browse the repository at this point in the history
shortened URLs as much as possible
  • Loading branch information
bbenezech committed Nov 16, 2011
1 parent 321ced3 commit 645d678
Show file tree
Hide file tree
Showing 13 changed files with 330 additions and 45 deletions.
266 changes: 266 additions & 0 deletions app/assets/javascripts/rails_admin/jquery.pjax.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
// jquery.pjax.js
// copyright chris wanstrath
// https://github.com/defunkt/jquery-pjax

(function($){

// 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)
// push - Whether to pushState the URL. Defaults to true (of course).
// replace - Want to use replaceState instead? That's cool.
//
// For convenience the first parameter can be either the container or
// the options object.
//
// Returns the jQuery object
$.fn.pjax = function( container, options ) {
if ( options )
options.container = container
else
options = $.isPlainObject(container) ? container : {container:container}

// We can't persist $objects using the history API so we must use
// a String selector. Bail if we got anything else.
if ( options.container && typeof options.container !== 'string' ) {
throw "pjax container must be a string selector!"
return false
}

return this.live('click', function(event){
// Middle click, cmd click, and ctrl click should open
// links in a new tab as normal.
if ( event.which > 1 || event.metaKey )
return true

var defaults = {
url: (this.href || $(this).attr('data-href')),
container: $(this).attr('data-pjax'),
clickedElement: $(this),
fragment: null
}

$.pjax($.extend({}, defaults, options))

event.preventDefault()
})
}


// Loads a URL with ajax, puts the response body inside a container,
// then pushState()'s the loaded URL.
//
// 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. Must be a String.
// $(container).html(xhr.responseBody)
// push - Whether to pushState the URL. Defaults to true (of course).
// replace - Want to use replaceState instead? That's cool.
//
// Use it just like $.ajax:
//
// var xhr = $.pjax({ url: this.href, container: '#main' })
// console.log( xhr.readyState )
//
// Returns whatever $.ajax returns.
var pjax = $.pjax = function( options ) {
var $container = $(options.container),
success = options.success || $.noop

// We don't want to let anyone override our success handler.
delete options.success

// We can't persist $objects using the history API so we must use
// a String selector. Bail if we got anything else.
if ( typeof options.container !== 'string' )
throw "pjax container must be a string selector!"

options = $.extend(true, {}, pjax.defaults, options)

if ( $.isFunction(options.url) ) {
options.url = options.url()
}

options.context = $container
options.success = function(data){
if ( options.fragment ) {
// If they specified a fragment, look for it in the response
// and pull it out.
var $fragment = $(data).find(options.fragment)
if ( $fragment.length )
data = $fragment.children()
else
return window.location = options.url
} else {
// 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
}

// Make it happen.
this.html(data)

// If there's a <title> tag in the response, use it as
// the page's title.
var oldTitle = document.title,
title = $.trim( this.find('title').remove().text() )
if ( title ) document.title = title

// No <title>? Fragment? Look for data-title and title attributes.
if ( !title && options.fragment ) {
title = $fragment.attr('title') || $fragment.data('title')
}

var state = {
pjax: options.container,
fragment: options.fragment,
timeout: options.timeout
}

// If there are extra params, save the complete URL in the state object
var query = $.param(options.data)
if ( query != "_pjax=true" )
state.url = options.url + (/\?/.test(options.url) ? "&" : "?") + query

if ( options.replace ) {
window.history.replaceState(state, document.title, options.url)
} else if ( options.push ) {
// this extra replaceState before first push ensures good back
// button behavior
if ( !pjax.active ) {
window.history.replaceState($.extend({}, state, {url:null}), oldTitle)
pjax.active = true
}

window.history.pushState(state, document.title, options.url)
}

// Google Analytics support
if ( (options.replace || options.push) && window._gaq )
_gaq.push(['_trackPageview'])

// If the URL has a hash in it, make sure the browser
// knows to navigate to the hash.
var hash = window.location.hash.toString()
if ( hash !== '' ) {
window.location.href = hash
}

// Invoke their success handler if they gave us one.
success.apply(this, arguments)
}

// Cancel the current request if we're already pjaxing
var xhr = pjax.xhr
if ( xhr && xhr.readyState < 4) {
xhr.onreadystatechange = $.noop
xhr.abort()
}

pjax.options = options
pjax.xhr = $.ajax(options)
$(document).trigger('pjax', [pjax.xhr, options])

return pjax.xhr
}


pjax.defaults = {
timeout: 2000,
push: true,
replace: false,
// We want the browser to maintain two separate internal caches: one for
// pjax'd partial page loads and one for normal page loads. Without
// adding this secret parameter, some browsers will often confuse the two.
data: { _pjax: true },
type: 'GET',
dataType: 'html',
beforeSend: function(xhr){
this.trigger('pjax:start', [xhr, pjax.options])
// start.pjax is deprecated
this.trigger('start.pjax', [xhr, pjax.options])
xhr.setRequestHeader('X-PJAX', 'true')
},
error: function(xhr, textStatus, errorThrown){
if ( textStatus !== 'abort' ) {
// console.log(xhr, textStatus, errorThrown)
window.location = pjax.options.url
}

},
complete: function(xhr){
this.trigger('pjax:end', [xhr, pjax.options])
// end.pjax is deprecated
this.trigger('end.pjax', [xhr, pjax.options])
}
}


// Used to detect initial (useless) popstate.
// If history.state exists, assume browser isn't going to fire initial popstate.
var popped = ('state' in window.history), initialURL = location.href


// popstate handler takes care of the back and forward buttons
//
// You probably shouldn't use pjax on pages with other pushState
// stuff yet.
$(window).bind('popstate', function(event){
// Ignore inital popstate that some browsers fire on page load
var initialPop = !popped && location.href == initialURL
popped = true
if ( initialPop ) return

var state = event.state

if ( state && state.pjax ) {
var container = state.pjax
if ( $(container+'').length )
$.pjax({
url: state.url || location.href,
fragment: state.fragment,
container: container,
push: false,
timeout: state.timeout
})
else
window.location = location.href
}
})


// Add the state property to jQuery's event object so we can use it in
// $(window).bind('popstate')
if ( $.inArray('state', $.event.props) < 0 )
$.event.props.push('state')


// Is pjax supported by this browser?
$.support.pjax =
window.history && window.history.pushState && window.history.replaceState
// pushState isn't reliable on iOS yet.
&& !navigator.userAgent.match(/(iPod|iPhone|iPad|WebApps\/.+CFNetwork)/)


// Fall back to normalcy for older browsers.
if ( !$.support.pjax ) {
$.pjax = function( options ) {
window.location = $.isFunction(options.url) ? options.url() : options.url
}
$.fn.pjax = function() { return this }
}

})(jQuery);
8 changes: 4 additions & 4 deletions app/assets/javascripts/rails_admin/ra.filter-box.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

$.filters = filters = {
append: function(field_label, field_name, field_type, field_value, field_operator, field_options, multiple_values, index) {
var value_name = 'filters[' + field_name + '][' + index + '][value]';
var operator_name = 'filters[' + field_name + '][' + index + '][operator]';
var value_name = 'f[' + field_name + '][' + index + '][v]';
var operator_name = 'f[' + field_name + '][' + index + '][o]';
switch(field_type) {
case 'boolean':
var control = '<select class="span3 " name="' + value_name + '">' +
Expand Down Expand Up @@ -71,7 +71,7 @@

var content = '<div class="row filter clearfix">' +
'<span class="span3">' +
'<span data-original-title="Click to remove this filter" rel="twipsy" class="btn info delete" data-disabler-name="filters[' + field_name + '][' + index + '][disabled]">' + field_label + '</span>' +
'<span data-original-title="Click to remove this filter" rel="twipsy" class="btn info delete" data-disabler-name="f[' + field_name + '][' + index + '][disabled]">' + field_label + '</span>' +
'</span>' +
'<span class="span3">'+
control +
Expand All @@ -91,7 +91,7 @@
$(this).data('field-operator'),
$(this).data('field-options'),
$(this).data('field-multiple_values'),
$.now()
$.now().toString().slice(7,11)
);
$("[rel=twipsy]").twipsy();
});
Expand Down
1 change: 1 addition & 0 deletions app/assets/javascripts/rails_admin/rails_admin.js.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
theme = :default
require_asset 'rails_admin/jquery-1.6.4.min'
require_asset 'jquery_ujs'
require_asset 'rails_admin/jquery.pjax'
require_asset 'jquery.remotipart'
require_asset 'rails_admin/jquery-ui-1.8.16.custom.js'
require_asset 'bootstrap'
Expand Down
14 changes: 8 additions & 6 deletions app/assets/javascripts/rails_admin/ui.js.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,21 @@ $("#list input.checkbox.toggle").live "click", ->
$("#list a, #list form").live "ajax:complete", (xhr, data, status) ->
$("#list").replaceWith data.responseText

$("#list table th.header").live "click", ->
$.ajax
url: $(this).data("link")
success: (data) ->
$("#list").replaceWith data

$("table#history th.header").live "click", ->
window.location = $(this).data("link")

$(document).ready ->
$('.pjax').pjax('[data-pjax-container]')
$('.pjax-form').live 'submit', (event) ->
event.preventDefault()
$.pjax
container: '[data-pjax-container]'
url: this.action + (if (this.action.indexOf('?') != -1) then '&' else '?') + $(this).serialize()
$(".alert-message").alert()
$("[rel=twipsy]").twipsy()
$('.animate-width-to').each ->
length = $(this).data("animate-length")
width = $(this).data("animate-width-to")
$(this).animate(width: width, length, 'easeOutQuad')


2 changes: 1 addition & 1 deletion app/controllers/rails_admin/main_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ def get_collection(model_config, scope)
options = options.merge(:page => (params[:page] || 1).to_i, :per => (params[:per] || model_config.list.items_per_page)) unless params[:associated_collection] || params[:all]
options = options.merge(:include => associations) unless associations.blank?
options = options.merge(get_sort_hash(model_config)) unless params[:associated_collection]
options = options.merge(model_config.abstract_model.get_conditions_hash(model_config, params[:query], params[:filters])) if params[:query] || params[:filters]
options = options.merge(model_config.abstract_model.get_conditions_hash(model_config, params[:query], params[:f])) if params[:query] || params[:f]
options = options.merge(:bulk_ids => params[:bulk_ids]) if params[:bulk_ids]

objects = model_config.abstract_model.all(options, scope)
Expand Down
2 changes: 1 addition & 1 deletion app/views/kaminari/twitter-bootstrap/_next_page.html.haml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
- if current_page.last?
%li.next.disabled= link_to raw(t 'views.pagination.next'), '#'
- else
%li.next= link_to raw(t 'views.pagination.next'), url, :remote => remote
%li.next= link_to raw(t 'views.pagination.next'), url, :class => (remote ? 'pjax' : '')
4 changes: 2 additions & 2 deletions app/views/kaminari/twitter-bootstrap/_page.html.haml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
- if page.current?
%li.active= link_to page, url, :remote => remote
%li.active= link_to page, url, :class => (remote ? 'pjax' : '')
- else
%li= link_to page, url, :remote => remote
%li= link_to page, url, :class => (remote ? 'pjax' : '')
2 changes: 1 addition & 1 deletion app/views/kaminari/twitter-bootstrap/_prev_page.html.haml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
- if current_page.first?
%li.prev.disabled= link_to raw(t 'views.pagination.previous'), '#'
- else
%li.prev= link_to raw(t 'views.pagination.previous'), url, :remote => remote
%li.prev= link_to raw(t 'views.pagination.previous'), url, :class => (remote ? 'pjax' : '')
Loading

0 comments on commit 645d678

Please sign in to comment.