pjax-rails 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +48 -0
- data/lib/assets/javascripts/pjax/enable_pjax.js.coffee +1 -0
- data/lib/assets/javascripts/pjax/index.js +3 -0
- data/lib/assets/javascripts/pjax/jquery_pjax.js +208 -0
- data/lib/assets/javascripts/pjax/page_triggers.js.coffee +10 -0
- data/lib/pjax-rails.rb +11 -0
- data/lib/pjax.rb +29 -0
- data/pjax-rails.gemspec +11 -0
- metadata +73 -0
data/README.md
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
PJAX for Rails 3.1+
|
2
|
+
===================
|
3
|
+
|
4
|
+
Integrate Chris Wanstrath's PJAX into Rails 3.1+ via the asset pipeline.
|
5
|
+
|
6
|
+
To activate, add this to your app/assets/javascripts/application.js (or whatever bundle you use):
|
7
|
+
|
8
|
+
//=require pjax
|
9
|
+
|
10
|
+
All links that matches $('a:not([data-remote]):not([data-behavior])') will then use PJAX.
|
11
|
+
|
12
|
+
FIXME: Currently the layout is hardcoded to "application". Need to delegate that to the specific layout of the controller.
|
13
|
+
|
14
|
+
Examples for redirect_pjax_to
|
15
|
+
-----------------------------
|
16
|
+
|
17
|
+
class ProjectsController < ApplicationController
|
18
|
+
before_filter :set_project, except: [ :index, :create ]
|
19
|
+
|
20
|
+
def index
|
21
|
+
@projects = current_user.projects
|
22
|
+
end
|
23
|
+
|
24
|
+
def show
|
25
|
+
end
|
26
|
+
|
27
|
+
def create
|
28
|
+
@project = Project.create params[:project]
|
29
|
+
redirect_pjax_to :show, @project
|
30
|
+
end
|
31
|
+
|
32
|
+
def update
|
33
|
+
@project.update_attributes params[:project]
|
34
|
+
redirect_pjax_to :show, @project
|
35
|
+
end
|
36
|
+
|
37
|
+
def destroy
|
38
|
+
@project.destroy
|
39
|
+
|
40
|
+
index # set the objects needed for rendering index
|
41
|
+
redirect_pjax_to :index
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
def set_project
|
46
|
+
@project = current_user.projects.find params[:id].to_i
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
$ -> $('a:not([data-remote]):not([data-behavior])').pjax('div#main')
|
@@ -0,0 +1,208 @@
|
|
1
|
+
// jquery_pjax.js
|
2
|
+
// copyright chris wanstrath
|
3
|
+
// https://github.com/defunkt/pjax
|
4
|
+
|
5
|
+
// When called on a link, fetches the href with ajax into the
|
6
|
+
// container specified as the first parameter or with the data-pjax
|
7
|
+
// attribute on the link itself.
|
8
|
+
//
|
9
|
+
// Tries to make sure the back button and ctrl+click work the way
|
10
|
+
// you'd expect.
|
11
|
+
//
|
12
|
+
// Accepts a jQuery ajax options object that may include these
|
13
|
+
// pjax specific options:
|
14
|
+
//
|
15
|
+
// container - Where to stick the response body. Usually a String selector.
|
16
|
+
// $(container).html(xhr.responseBody)
|
17
|
+
// push - Whether to pushState the URL. Defaults to true (of course).
|
18
|
+
// replace - Want to use replaceState instead? That's cool.
|
19
|
+
//
|
20
|
+
// For convenience the first parameter can be either the container or
|
21
|
+
// the options object.
|
22
|
+
//
|
23
|
+
// Returns the jQuery object
|
24
|
+
$.fn.pjax = function( container, options ) {
|
25
|
+
if ( options )
|
26
|
+
options.container = container
|
27
|
+
else
|
28
|
+
options = $.isPlainObject(container) ? container : {container:container}
|
29
|
+
|
30
|
+
return this.live('click', function(event){
|
31
|
+
// Middle click, cmd click, and ctrl click should open
|
32
|
+
// links in a new tab as normal.
|
33
|
+
if ( event.which > 1 || event.metaKey )
|
34
|
+
return true
|
35
|
+
|
36
|
+
var defaults = {
|
37
|
+
url: this.href,
|
38
|
+
container: $(this).attr('data-pjax')
|
39
|
+
}
|
40
|
+
|
41
|
+
$.pjax($.extend({}, defaults, options))
|
42
|
+
|
43
|
+
event.preventDefault()
|
44
|
+
})
|
45
|
+
}
|
46
|
+
|
47
|
+
|
48
|
+
// Loads a URL with ajax, puts the response body inside a container,
|
49
|
+
// then pushState()'s the loaded URL.
|
50
|
+
//
|
51
|
+
// Works just like $.ajax in that it accepts a jQuery ajax
|
52
|
+
// settings object (with keys like url, type, data, etc).
|
53
|
+
//
|
54
|
+
// Accepts these extra keys:
|
55
|
+
//
|
56
|
+
// container - Where to stick the response body.
|
57
|
+
// $(container).html(xhr.responseBody)
|
58
|
+
// push - Whether to pushState the URL. Defaults to true (of course).
|
59
|
+
// replace - Want to use replaceState instead? That's cool.
|
60
|
+
//
|
61
|
+
// Use it just like $.ajax:
|
62
|
+
//
|
63
|
+
// var xhr = $.pjax({ url: this.href, container: '#main' })
|
64
|
+
// console.log( xhr.readyState )
|
65
|
+
//
|
66
|
+
// Returns whatever $.ajax returns.
|
67
|
+
$.pjax = function( options ) {
|
68
|
+
var $container = $(options.container),
|
69
|
+
success = options.success || $.noop
|
70
|
+
|
71
|
+
// We don't want to let anyone override our success handler.
|
72
|
+
delete options.success
|
73
|
+
|
74
|
+
var defaults = {
|
75
|
+
timeout: 650,
|
76
|
+
push: true,
|
77
|
+
replace: false,
|
78
|
+
// We want the browser to maintain two separate internal caches: one for
|
79
|
+
// pjax'd partial page loads and one for normal page loads. Without
|
80
|
+
// adding this secret parameter, some browsers will often confuse the two.
|
81
|
+
data: { _pjax: true },
|
82
|
+
type: 'GET',
|
83
|
+
dataType: 'html',
|
84
|
+
beforeSend: function(xhr){
|
85
|
+
$(document).trigger('start.pjax')
|
86
|
+
xhr.setRequestHeader('X-PJAX', 'true')
|
87
|
+
},
|
88
|
+
error: function(){
|
89
|
+
window.location = options.url
|
90
|
+
},
|
91
|
+
complete: function(){
|
92
|
+
$(document).trigger('end.pjax')
|
93
|
+
},
|
94
|
+
success: function(data){
|
95
|
+
// If we got no data or an entire web page, go directly
|
96
|
+
// to the page and let normal error handling happen.
|
97
|
+
if ( !$.trim(data) || /<html/i.test(data) )
|
98
|
+
return window.location = options.url
|
99
|
+
|
100
|
+
// Make it happen.
|
101
|
+
$container.html(data)
|
102
|
+
|
103
|
+
// If there's a <title> tag in the response, use it as
|
104
|
+
// the page's title.
|
105
|
+
var oldTitle = document.title,
|
106
|
+
title = $.trim( $container.find('title').remove().text() )
|
107
|
+
if ( title ) document.title = title
|
108
|
+
|
109
|
+
var state = {
|
110
|
+
pjax: options.container,
|
111
|
+
timeout: options.timeout
|
112
|
+
}
|
113
|
+
|
114
|
+
// We can't persist $objects using the history API so we need to store
|
115
|
+
// the string selector.
|
116
|
+
if ( $.isPlainObject(state.pjax) )
|
117
|
+
state.pjax = state.pjax.selector
|
118
|
+
|
119
|
+
// If there are extra params, save the complete URL in the state object
|
120
|
+
var query = $.param(options.data)
|
121
|
+
if ( query != "_pjax=true" )
|
122
|
+
state.url = options.url + (/\?/.test(options.url) ? "&" : "?") + query
|
123
|
+
|
124
|
+
if ( options.replace ) {
|
125
|
+
window.history.replaceState(state, document.title, options.url)
|
126
|
+
} else if ( options.push ) {
|
127
|
+
// this extra replaceState before first push ensures good back
|
128
|
+
// button behavior
|
129
|
+
if ( !$.pjax.active ) {
|
130
|
+
window.history.replaceState($.extend({}, state, {url:null}), oldTitle)
|
131
|
+
$.pjax.active = true
|
132
|
+
}
|
133
|
+
|
134
|
+
window.history.pushState(state, document.title, options.url)
|
135
|
+
}
|
136
|
+
|
137
|
+
// Google Analytics support
|
138
|
+
if ( (options.replace || options.push) && window._gaq )
|
139
|
+
_gaq.push(['_trackPageview'])
|
140
|
+
|
141
|
+
// Invoke their success handler if they gave us one.
|
142
|
+
success.apply(this, arguments)
|
143
|
+
}
|
144
|
+
}
|
145
|
+
|
146
|
+
options = $.extend(true, {}, defaults, options)
|
147
|
+
|
148
|
+
if ( $.isFunction(options.url) ) {
|
149
|
+
options.url = options.url()
|
150
|
+
}
|
151
|
+
|
152
|
+
// Cancel the current request if we're already pjaxing
|
153
|
+
var xhr = $.pjax.xhr
|
154
|
+
if ( xhr && xhr.readyState < 4) {
|
155
|
+
xhr.onreadystatechange = $.noop
|
156
|
+
xhr.abort()
|
157
|
+
}
|
158
|
+
|
159
|
+
$.pjax.xhr = $.ajax(options)
|
160
|
+
$(document).trigger('pjax', $.pjax.xhr, options)
|
161
|
+
|
162
|
+
return $.pjax.xhr
|
163
|
+
}
|
164
|
+
|
165
|
+
|
166
|
+
// Used to detect initial (useless) popstate.
|
167
|
+
// If history.state exists, assume browser isn't going to fire initial popstate.
|
168
|
+
var popped = ('state' in window.history), initialURL = location.href
|
169
|
+
|
170
|
+
|
171
|
+
// popstate handler takes care of the back and forward buttons
|
172
|
+
//
|
173
|
+
// You probably shouldn't use pjax on pages with other pushState
|
174
|
+
// stuff yet.
|
175
|
+
$(window).bind('popstate', function(event) {
|
176
|
+
// Ignore inital popstate that some browsers fire on page load
|
177
|
+
var initialPop = !popped && location.href == initialURL
|
178
|
+
popped = true
|
179
|
+
if ( initialPop ) return
|
180
|
+
|
181
|
+
var state = event.state
|
182
|
+
|
183
|
+
if ( state && state.pjax ) {
|
184
|
+
var $container = $(state.pjax+'')
|
185
|
+
if ( $container.length )
|
186
|
+
$.pjax({
|
187
|
+
url: state.url || location.href,
|
188
|
+
container: $container,
|
189
|
+
push: false,
|
190
|
+
timeout: state.timeout
|
191
|
+
})
|
192
|
+
else
|
193
|
+
window.location = location.href
|
194
|
+
}
|
195
|
+
})
|
196
|
+
|
197
|
+
|
198
|
+
// Add the state property to jQuery's event object so we can use it in
|
199
|
+
// $(window).bind('popstate')
|
200
|
+
if ( $.event.props.indexOf('state') < 0 )
|
201
|
+
$.event.props.push('state')
|
202
|
+
|
203
|
+
|
204
|
+
// Fall back to normalcy for older browsers.
|
205
|
+
if ( !window.history || !window.history.pushState ) {
|
206
|
+
$.pjax = $.noop
|
207
|
+
$.fn.pjax = function() { return this }
|
208
|
+
}
|
@@ -0,0 +1,10 @@
|
|
1
|
+
$(document).ready ->
|
2
|
+
$(document).trigger 'pageChanged'
|
3
|
+
$(document).trigger 'pageUpdated'
|
4
|
+
|
5
|
+
$(document).bind 'end.pjax', ->
|
6
|
+
$(document).trigger 'pageChanged'
|
7
|
+
$(document).trigger 'pageUpdated'
|
8
|
+
|
9
|
+
$(document).bind 'ajaxComplete', ->
|
10
|
+
$(document).trigger 'pageUpdated'
|
data/lib/pjax-rails.rb
ADDED
data/lib/pjax.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
module Pjax
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
included do
|
5
|
+
layout ->(c) { pjax_request? ? false : 'application' }
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
def redirect_pjax_to(action, url = nil)
|
10
|
+
new_url = url_for(url ? url : { action: action })
|
11
|
+
|
12
|
+
render js: <<-EJS
|
13
|
+
if (!window.history || !window.history.pushState) {
|
14
|
+
window.location.href = '#{new_url}';
|
15
|
+
} else {
|
16
|
+
$('div.pages').html(#{render_to_string("#{action}.html.erb").to_json});
|
17
|
+
$(document).trigger('end.pjax');
|
18
|
+
|
19
|
+
var title = $.trim($('div.pages').find('title').remove().text());
|
20
|
+
if (title) document.title = title;
|
21
|
+
window.history.pushState({}, document.title, '#{new_url}');
|
22
|
+
}
|
23
|
+
EJS
|
24
|
+
end
|
25
|
+
|
26
|
+
def pjax_request?
|
27
|
+
env['HTTP_X_PJAX'].present?
|
28
|
+
end
|
29
|
+
end
|
data/pjax-rails.gemspec
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'pjax-rails'
|
3
|
+
s.version = '0.1'
|
4
|
+
s.author = 'David Heinemeier Hansson (PJAX by Chris Wanstrath)'
|
5
|
+
s.email = 'david@loudthinking.com'
|
6
|
+
s.summary = 'PJAX integration for Rails 3.1+'
|
7
|
+
|
8
|
+
s.add_dependency 'jquery-rails'
|
9
|
+
|
10
|
+
s.files = Dir["#{File.dirname(__FILE__)}/**/*"]
|
11
|
+
end
|
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pjax-rails
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: "0.1"
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- David Heinemeier Hansson (PJAX by Chris Wanstrath)
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-05-17 00:00:00 -04:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: jquery-rails
|
18
|
+
prerelease: false
|
19
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
20
|
+
none: false
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: "0"
|
25
|
+
type: :runtime
|
26
|
+
version_requirements: *id001
|
27
|
+
description:
|
28
|
+
email: david@loudthinking.com
|
29
|
+
executables: []
|
30
|
+
|
31
|
+
extensions: []
|
32
|
+
|
33
|
+
extra_rdoc_files: []
|
34
|
+
|
35
|
+
files:
|
36
|
+
- ./lib/assets/javascripts/pjax/enable_pjax.js.coffee
|
37
|
+
- ./lib/assets/javascripts/pjax/index.js
|
38
|
+
- ./lib/assets/javascripts/pjax/jquery_pjax.js
|
39
|
+
- ./lib/assets/javascripts/pjax/page_triggers.js.coffee
|
40
|
+
- ./lib/pjax-rails.rb
|
41
|
+
- ./lib/pjax.rb
|
42
|
+
- ./pjax-rails.gemspec
|
43
|
+
- ./README.md
|
44
|
+
has_rdoc: true
|
45
|
+
homepage:
|
46
|
+
licenses: []
|
47
|
+
|
48
|
+
post_install_message:
|
49
|
+
rdoc_options: []
|
50
|
+
|
51
|
+
require_paths:
|
52
|
+
- lib
|
53
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: "0"
|
59
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
|
+
none: false
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: "0"
|
65
|
+
requirements: []
|
66
|
+
|
67
|
+
rubyforge_project:
|
68
|
+
rubygems_version: 1.6.2
|
69
|
+
signing_key:
|
70
|
+
specification_version: 3
|
71
|
+
summary: PJAX integration for Rails 3.1+
|
72
|
+
test_files: []
|
73
|
+
|