pjax-rails 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,3 @@
1
+ //= require ./jquery_pjax
2
+ //= require ./page_triggers
3
+ //= require ./enable_pjax
@@ -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'
@@ -0,0 +1,11 @@
1
+ require 'pjax'
2
+
3
+ module PjaxRails
4
+ class Engine < ::Rails::Engine
5
+ initializer "pjax_rails.add_controller" do
6
+ ActiveSupport.on_load(:action_controller) do
7
+ ::ApplicationController.send :include, Pjax
8
+ end
9
+ end
10
+ end
11
+ end
@@ -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
@@ -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
+