pjax-rails 0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
|