ajax_pagination 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (28) hide show
  1. data/CHANGELOG.md +16 -0
  2. data/README.md +52 -20
  3. data/lib/ajax_pagination.rb +10 -0
  4. data/lib/ajax_pagination/controller_additions.rb +29 -7
  5. data/lib/ajax_pagination/helper_additions.rb +109 -7
  6. data/lib/ajax_pagination/rails.rb +8 -0
  7. data/lib/ajax_pagination/version.rb +1 -1
  8. data/lib/assets/javascripts/ajax_pagination.js.erb +132 -41
  9. data/lib/generators/templates/ajax_pagination.rb +14 -0
  10. data/spec/ajax_pagination/controller_additions_spec.rb +1 -1
  11. data/spec/ajax_pagination/integration/ajaxpaginate_spec.rb +99 -14
  12. data/spec/ajax_pagination/integration/nojavascript_spec.rb +3 -4
  13. data/spec/rails_app/app/controllers/application_controller.rb +2 -3
  14. data/spec/rails_app/app/controllers/changelog_controller.rb +0 -1
  15. data/spec/rails_app/app/controllers/pages_controller.rb +2 -2
  16. data/spec/rails_app/app/controllers/posts_controller.rb +2 -2
  17. data/spec/rails_app/app/controllers/sessions_controller.rb +8 -0
  18. data/spec/rails_app/app/views/layouts/ajax.html.erb +8 -0
  19. data/spec/rails_app/app/views/layouts/application.html.erb +10 -11
  20. data/spec/rails_app/app/views/layouts/flash.html.erb +7 -0
  21. data/spec/rails_app/app/views/posts/_form.html.erb +1 -1
  22. data/spec/rails_app/app/views/posts/_page.html.erb +3 -3
  23. data/spec/rails_app/app/views/posts/index.html.erb +2 -2
  24. data/spec/rails_app/app/views/posts/show.html.erb +3 -2
  25. data/spec/rails_app/app/views/sessions/count.html.erb +7 -0
  26. data/spec/rails_app/config/routes.rb +2 -0
  27. data/spec/rails_app/db/development.sqlite3 +0 -0
  28. metadata +27 -18
data/CHANGELOG.md CHANGED
@@ -1,3 +1,19 @@
1
+ ## v0.3.0
2
+ * Class method ajax_pagination :reload option - a string in json form no longer accepted, pass it in as a hash or array of hashes instead
3
+ * image option added to ajax_pagination view helper, to specify a loading image other than the default
4
+ * For reloading of a page section on browser back/forward, urlregex urlparts option changed to parse the url in strict mode
5
+ * Fixed bug: The loading never stopped if page was invalid, it will now display the error page properly
6
+ * Fixed bug: back/forward not loading properly in certain cases
7
+ * AJAX Pagination now uses jquery-ujs! The internals have changed, but the released public API has not otherwise changed. However, this important change allows AJAX Pagination to handle links and forms requesting via POST, PUT and DELETE methods.
8
+ * Added link helper ajax_link_to, which wraps link_to. This helper is useful for one-off links to perform an AJAX call. In fact, the output link format that required to get AJAX Pagination to work.
9
+ * The original way to specify that links use AJAX Pagination - wrapping links within a container of ajaxpagination class, with data-pagination attribute is now a convenience feature provided, which actually uses javascript to set the same link attributes as the link helper.
10
+ * Added support for redirection - redirects are intercepted, and if they are AJAX calls with a pagination url parameter, will be modified into a Status 200 OK response with Location header. The AJAX call be manually follow the redirect, and update the url in the address bar. the pagination url parameter context can also be kept.
11
+ * Add support for changing page title in the returned AJAX partial which is better if using for site-wide navigation (before it simply preserved the previous title)
12
+ * Scrolling feature fixed, and a new configuration - scroll_margin provided
13
+ * Added form helpers which trigger ajax pagination
14
+ * Added ajax_options method, which can be used especially with form helpers from gems such as Formtastic or Simple Form.
15
+ * Added specs for new features (including displaying error pages, redirecting within div, submitting forms with PUT POST DELETE, testing ajax_link_to, ajax_form_tag, ajax_form_for (and with all these, indirectly testing ajax_options), title changes)
16
+
1
17
  ## v0.2.0
2
18
  **Note: The API has changed slightly from previous versions. If you have used an advanced feature - the :partial option, please use :render now, and if used in a controller, a non-partial template will be used. To use a partial, specify :render => { :partial => "" }.**
3
19
 
data/README.md CHANGED
@@ -1,21 +1,11 @@
1
1
  # AJAX Pagination
2
2
  [![Build Status](https://secure.travis-ci.org/ronalchn/ajax_pagination.png?branch=master)](http://travis-ci.org/ronalchn/ajax_pagination)
3
3
 
4
- Handles AJAX pagination for you, by hooking up the links you want to load content with javascript in designated page containers. Each webpage can have multiple page containers, each with a different set of pagination links. The page containers can be nested. Degrades gracefully when javascript is disabled.
5
-
6
- ## Introduction
7
- This gem can ajaxify any pagination solution. Links wrapped in containers with specific classes will be ajaxified. This means that clicking it will instead send an AJAX request for the page in javascript format. The result will replace the content in a container for the content. this gem is tested to work with will_paginate, but should work for other pagination solutions, as well as navigation level links or tabbed interfaces. The ajax call will load new content into the designated content container.
8
-
9
- Please note, this is not a pagination solution by itself. You should use a pagination solution such as will_paginate and Kaminari, or a menu builder such as Simple-navigation or Semantic-menu, or you can roll your own. After that is implemented, you can use AJAX Pagination to ajaxify it, so that when users change pages, they do not have the reload the whole page.
10
-
11
- ## Background
12
- This gem depends on Rails 3.1+, jQuery and jquery-historyjs. The gem was extracted from http://github.com/xrymbos/nztrain-v2/, and further development will be tied to the needs of the application. Therefore, some dependencies are because the application uses a particular version of these other gems. If you need to use this in other versions/javascript frameworks, I would welcome any pull requests. They are not currently supported because I do not need to use this gem in those other frameworks.
4
+ [Wiki](https://github.com/ronalchn/ajax_pagination/wiki) | [RDoc](http://rdoc.info/gems/ajax_pagination/frames) | [Changelog](https://github.com/ronalchn/ajax_pagination/blob/master/CHANGELOG.md)
13
5
 
14
- The original AJAX pagination functionality was inspired by the RailsCasts on pagination with AJAX. However, other functionality was added to the pagination, and more modular code was desired, especially when many different controllers need pagination. Added functionality includes loading cues and support of multiple pagination areas on the same webpage.
15
-
16
- Because the code became more modular, it also made it suitable to turn into a Ruby Gem, so that others can create AJAX pagination without fiddling with the details.
6
+ Handles AJAX pagination for you, by hooking up the links you want to load content with javascript in designated page containers. Each webpage can have multiple page containers, each with a different set of pagination links. The page containers can be nested. Degrades gracefully when javascript is disabled.
17
7
 
18
- Rails 3.1+ is required only because the javascript is placed into the assets pipeline.
8
+ For more, see [Introduction and Background](https://github.com/ronalchn/ajax_pagination/wiki/Introduction-and-Background).
19
9
 
20
10
  ## Installation
21
11
  Add to your Gemfile:
@@ -34,14 +24,15 @@ AJAX Pagination depends on jquery-rails and jquery-historyjs, so if their javasc
34
24
 
35
25
  ```javascript
36
26
  //= require jquery
27
+ //= require jquery_ujs
37
28
  //= require history
38
29
  ```
39
30
 
40
31
  ## Getting Started
41
32
  The next section presents the usage of the functions in detail. However, it also presents the different options that can be chosen. For a simpler overview of how you can easily use this gem, please read one of the more specific guides below (found in the wiki):
42
33
 
43
- * [Add AJAX to will_paginate](https://github.com/ronalchn/ajax_pagination/wiki/Adding-AJAX-to-will_paginate)
44
- * [Add AJAX to site navigation](https://github.com/ronalchn/ajax_pagination/wiki/Adding-AJAX-to-site-navigation)
34
+ * [Adding AJAX to will_paginate](https://github.com/ronalchn/ajax_pagination/wiki/Adding-AJAX-to-will_paginate)
35
+ * [Adding AJAX to site navigation](https://github.com/ronalchn/ajax_pagination/wiki/Adding-AJAX-to-site-navigation)
45
36
 
46
37
  and much [more](https://github.com/ronalchn/ajax_pagination/wiki/Home).
47
38
 
@@ -77,12 +68,14 @@ This will cause it to display content in the _mypartial.* view.
77
68
 
78
69
  If you are using will_paginate, and the links are wrapped in a div with class="pagination", the links will be ajaxified automatically.
79
70
 
80
- Otherwise, you should wrap the links with a container. We recommend that the class given is "ajaxpagination". You can put the links inside the partial, for example:
71
+ Otherwise, you can wrap a set of links with a container. We recommend that the class given is "ajaxpagination". You can put the links inside the partial, for example:
81
72
 
82
73
  ```html
83
74
  <div class="ajaxpagination"><a href="#">My ajaxified link</a></div>
84
75
  ```
85
76
 
77
+ If you want to ajaxify individual links, check out the ajax_link_to helper (shown later), which is more flexible (can handle non-GET requests). Wrapping links in a container is actually just a convenience feature, which internally does the same things ajax_link_to does.
78
+
86
79
  If you are using will_paginate, you can simply put the links inside the partial (so that the new links get reloaded when the page changes):
87
80
 
88
81
  ```erb
@@ -196,7 +189,7 @@ AJAX Pagination can also add a loading image and partially blanking out of the p
196
189
 
197
190
  Links outside are still clickable (such as the will_paginate links).
198
191
 
199
- The loading image is currently an image asset at "ajax-loader.gif", so put your loading image there. You can specify a new default filename in your initializer.
192
+ The loading image is currently an image asset at "ajax-loader.gif", so put your loading image there. You can specify a new default filename in your initializer. If you want a different loading image (other than configuring a site-wide default), you can pass an option :image => "newimageinassetpipeline.gif" to the ajax_pagination view helper method.
200
193
 
201
194
  If you want all the content in the partial (or otherwise wrapped by the ajax_pagination helper method) to be included as a loading zone (with the visual loading cues), you can instead, set the :loadzone option to true, eg:
202
195
 
@@ -208,7 +201,7 @@ If you want all the content in the partial (or otherwise wrapped by the ajax_pag
208
201
 
209
202
  In this case, whatever is inside the yield will not need to call ajax_pagination_loadzone.
210
203
 
211
- ### Content reloading
204
+ ### Browser History
212
205
 
213
206
  The back and forward buttons on your browser may not work properly yet. It will work as long as the link includes distinct query parameter with the same name as the pagination name for the set. For example, if the name of the pagination set is "page" (the default), when the browser url changes, AJAX Pagination looks for a change in the links query parameter with the same name, such as if the url changes from /path/to/controller?page=4 to /path/to/controller?page=9, then AJAX Pagination knows that the content corresponding to the pagination set needs reloading. The absence of the parameter is a distinct state, so changes such as /path/to/controller to /path/to/controller?page=0 are detected.
214
207
 
@@ -234,12 +227,51 @@ For more flexibility, a number of conditions can be passed in an array. If any o
234
227
  <%= ajax_pagination :reload => [{:urlregex => "page=([0-9]+)", :regexindex => 1},{:query => "watching"}] %>
235
228
  ```
236
229
 
237
- Instead of passing in the Array/Hash Ruby object, a string in json form is accepted:
230
+ Sometimes, you may have a small section of the page which is paginated, but for which you do not want to change the url. In effect, the page you are on should remain the same, simply with a cool effect, which changes the content displayed. This would mean that the new content loaded does not add to browser history.
231
+
232
+ By default, the url does get changed, and browser history is added to. To turn this off, you can set :history => false, eg:
238
233
 
239
234
  ```erb
240
- <%= ajax_pagination :reload => '[{"urlregex":"page=([0-9]+)","regexindex":1},{"query":"page"}]' %>
235
+ <%= ajax_pagination :history => false %>
241
236
  ```
242
237
 
238
+ When history is turned off, by default, reload is also set to never reload the content.
239
+
240
+ ### Link and Form helpers
241
+ For individual links, you can ajaxify them using ajax_link_to with the same format as the link_to helper, but making sure to pass a pagination option:
242
+
243
+ ```erb
244
+ <%= ajax_link_to "Name", posts_url, :pagination => "page" %>
245
+ ```
246
+
247
+ Similarly, there exist AJAX form helpers, again with the same format as the original form helpers:
248
+
249
+ ```erb
250
+ <%= ajax_form_tag posts_url, :method => "post", :class => "myclass", :pagination => "page" do %>
251
+ ...
252
+ <% end %>
253
+
254
+ <%= ajax_form_for @post, :method => "post", :html => {:class => "myclass", :pagination => "menu"} do %>
255
+ ...
256
+ <% end %>
257
+ ```
258
+
259
+ Note that these form helpers actually use another helper method ajax_options, with the original non-ajax helpers. This means that you can also use:
260
+
261
+ ```erb
262
+ <%= link_to "Name", posts_url, ajax_options :pagination => "page" %>
263
+
264
+ <%= form_tag posts_url, ajax_options :method => "post", :class => "myclass", :pagination => "page" do %>
265
+ ...
266
+ <% end %>
267
+
268
+ <%= form_for @post, :method => "post", :html => ajax_options({:class => "myclass", :pagination => "menu"}) do %>
269
+ ...
270
+ <% end %>
271
+ ```
272
+
273
+ This is actually a powerful feature, because though you would simply use the ajax_link_to, ajax_form_tag, ajax_form_for tags as shorthand, if you are using another helper method (eg. one from Formtastic or Simple Form gems), you can use ajax_options with that helper method.
274
+
243
275
  ### Initializer
244
276
  You can configure AJAX Pagination in the initializer. Run ```
245
277
  rails generate ajax_pagination:install
@@ -12,6 +12,16 @@ module AjaxPagination
12
12
  mattr_accessor :warnings
13
13
  @@warnings = nil # if nil, uses default, which is true only in development mode
14
14
 
15
+ # intercepts any AJAX Pagination 302 redirects, and turns them into Status 200 OK, with a Location: header. AJAX code can manually perform the redirection.
16
+ # can be disabled in the initializer
17
+ mattr_accessor :redirect_after_filter
18
+ @@redirect_after_filter = true
19
+
20
+ # when changing pages, AJAX Pagination scrolls to ensure the page sees the top of the changing section, plus an additional margin in pixels
21
+ # to turn this feature off, set scroll_margin to '-Infinity'
22
+ mattr_accessor :scroll_margin
23
+ @@scroll_margin = 20
24
+
15
25
  # run rails generate ajax_pagination:install to create a default initializer with configuration values
16
26
  def self.config
17
27
  yield self
@@ -18,10 +18,11 @@ module AjaxPagination
18
18
  pagination = options[:pagination] || ""
19
19
  view = options[:render] || nil
20
20
  define_method(:default_render) do |*args|
21
- if params[:pagination] && params[:pagination] == pagination && request.format == "html" # override if calling AJAX Pagination
21
+ paramspagination = request.GET[:pagination] || params[:pagination]
22
+ if paramspagination && paramspagination == pagination && request.format == "html" # override if calling AJAX Pagination
22
23
  unless view
23
- if lookup_context.find_all("#{params[:controller]}/_#{params[:pagination]}").any?
24
- view = { :partial => params[:pagination] } # render partial, layout is off
24
+ if lookup_context.find_all("#{params[:controller]}/_#{paramspagination}").any?
25
+ view = { :partial => paramspagination } # render partial, layout is off
25
26
  else
26
27
  view = { :layout => false } # render default view, but turn off layout
27
28
  end
@@ -74,11 +75,12 @@ module AjaxPagination
74
75
  # end
75
76
  #
76
77
  def ajax_pagination(format,options = {})
77
- if params[:pagination] == (options[:pagination] || 'page').to_s
78
+ paramspagination = request.GET[:pagination] || params[:pagination]
79
+ if paramspagination == (options[:pagination] || 'page').to_s
78
80
  if options[:render]
79
81
  view = options[:render] # render non partial
80
- elsif lookup_context.find_all(params[:controller] + "/_" + params[:pagination]).any?
81
- view = {:partial => params[:pagination]} # render partial of the same name as pagination
82
+ elsif lookup_context.find_all(params[:controller] + "/_" + paramspagination).any?
83
+ view = {:partial => paramspagination} # render partial of the same name as pagination
82
84
  else # render usual view
83
85
  view = {}
84
86
  end
@@ -125,7 +127,27 @@ module AjaxPagination
125
127
  #
126
128
  # The heavy computation will only be performed on posts which will be displayed when AJAX Pagination only wants a partial.
127
129
  def ajax_pagination_displayed?(pagination = :page)
128
- (!request.format.html?) || (params[:pagination].nil?) || (params[:pagination] == pagination.to_s)
130
+ paramspagination = request.GET[:pagination] || params[:pagination]
131
+ (!request.format.html?) || (paramspagination.nil?) || (paramspagination == pagination.to_s)
132
+ end
133
+
134
+ # This after_filter method is automatically included by AJAX Paginate, and does not need to be included manually. However,
135
+ # it can be disabled in the initializer, and if desired, enabled in specific controllers by adding this method as an after_filter.
136
+ #
137
+ # This after_filter method will intercept any redirects for AJAX calls by
138
+ # AJAX Paginate, and turn it into a Status 200 OK response, with an extra Location: header.
139
+ #
140
+ # This is used because of the transparent redirection otherwise done by browsers on receiving a 30x status code. The AJAX
141
+ # code cannot then detect that the request was redirected, and is therefore unable to change the url in the History object.
142
+ # AJAX Pagination javascript code will detect any 200 OK responses with a Location header, and treat this as a redirection.
143
+ #
144
+ # This filter should not affect other uses, because only AJAX calls trigger this. In addition, a ?pagination= parameter is required.
145
+ # Therefore other AJAX libraries or usage otherwise should not be affected.
146
+ def ajax_pagination_redirect
147
+ paramspagination = request.GET[:pagination] || params[:pagination]
148
+ if request.xhr? && paramspagination && response.status==302 # alter redirect response so that it can be detected by the client javascript
149
+ response.status = 200 # change response to OK, location header is preserved, so AJAX can get the new page manually
150
+ end
129
151
  end
130
152
  end
131
153
  end
@@ -73,21 +73,37 @@ module AjaxPagination
73
73
  #
74
74
  # If nil, AJAX Pagination acts as if it was passed {:query => options [:pagination]}.
75
75
  #
76
+ # [:+image+]
77
+ # Specify another image to be used as the loading image. The string passed is an image in the assets pipeline.
78
+ # If not specified, the default loading image is used.
79
+ #
76
80
  # [:+loadzone+]
77
81
  # Instead of using the ajax_pagination_loadzone tag, this option can be set to true. Everything inside this tag
78
82
  # will then be regarded as a loading zone, and the visual loading cues will apply to all the content here.
79
83
  #
84
+ # [:+history+]
85
+ # Whether the url changes, as if an new page was accessed, including adding page to the history. This defaults to
86
+ # true. Therefore, (sub)pages accessed through AJAX Pagination act as if a whole new page was accessed.
87
+ #
88
+ # If false then it is as if no new page is accessed, and the history is not changed. It therefore appears as if following
89
+ # the link simply creates a cool AJAX effect on the current page. If false, then :reload defaults to {:urlregex => ""},
90
+ # meaning that it will never reload when browser back/forward buttons are used, whether the url changes or not.
91
+ #
80
92
  def ajax_pagination(options = {})
81
93
  pagination = options[:pagination] || 'page' # by default the name of the pagination is 'page'
82
94
  partial = options[:render] || pagination # default partial rendered is the name of the pagination
83
- reload = options[:reload]
84
95
  divoptions = { :id => "#{pagination}_paginated_section", :class => "paginated_section" }
85
- case reload.class.to_s
86
- when "String"
87
- divoptions["data-pagination"] = reload
96
+ data = {};
97
+ if options.has_key? :history
98
+ data[:history] = (options[:history] != false)
99
+ data[:reload] = {:urlregex => ""} unless data[:history] # by default never reloads a history-less section
100
+ end
101
+ case options[:reload].class.to_s
88
102
  when "Hash", "Array"
89
- divoptions["data-pagination"] = reload.to_json
103
+ data[:reload] = options[:reload]
90
104
  end
105
+ data[:image] = asset_path options[:image] if options[:image].class.to_s == "String"
106
+ divoptions["data-pagination"] = data.to_json if !data.empty?
91
107
  if options[:loadzone]
92
108
  divoptions[:class] = "paginated_section paginated_content"
93
109
  divoptions[:style] = "position: relative;"
@@ -105,11 +121,11 @@ module AjaxPagination
105
121
  # is reloaded, it may take some time. In the meanwhile, AJAX Pagination will look in the partial amoung its
106
122
  # immediate child for this tag. If this tag exists, it will cover this tag with a semi-transparent
107
123
  # rectangle, and make the old partial unclickable (links and other elements are therefore disabled).
108
- # A loading image is also displayed above the content.
124
+ # A loading image is also displayed above the content. Only one loading zone is allowed. The rest are ignored.
109
125
  #
110
126
  # Use this tag in your partial, wrapped around all the content you want to disable. For example, if you are
111
127
  # displaying pagination links which you do not want to disable, as well as content you wish to disable,
112
- # you partial might contain:
128
+ # your partial might contain:
113
129
  #
114
130
  # <%= will_paginate @objects, :params => { :pagination => nil } %>
115
131
  # <%= ajax_pagination_loadzone do %>
@@ -122,6 +138,92 @@ module AjaxPagination
122
138
  yield
123
139
  end
124
140
  end
141
+
142
+ # modifies the html options, so that it calls AJAX Pagination. This method adds the appropriate parameters to make an AJAX call via
143
+ # AJAX Pagination, using jquery-ujs.
144
+ #
145
+ # More specifically, it ensures the following attributes are defined :remote => true, "data-type" => 'html', :pagination => ?.
146
+ # The pagination attribute must be defined, or else it defaults to the empty string "".
147
+ # This link always sets data-remote to true - setting to false is not allowed, since AJAX Pagination would not be triggered.
148
+ #
149
+ # Below is an alternative way to create an ajax link instead of ajax_link_to
150
+ #
151
+ # <%= link_to "Name", posts_url, ajax_options :pagination => "page" %>
152
+ #
153
+ def ajax_options(html_options = {})
154
+ html_options["data-pagination".to_sym] = html_options.delete(:pagination) || html_options.delete("data-pagination") || "" # renames the option pagination to data-pagination
155
+ html_options[:remote] = true
156
+ html_options["data-type".to_sym] ||= html_options.delete("data-type") || 'html'
157
+ html_options
158
+ end
159
+
160
+ # Wrapper for link_to, but makes the link trigger an AJAX call through AJAX Pagination.
161
+ # The arguments passed are in the same order as link_to the advantage of using this helper
162
+ # is that if the implementation is changed, links using this helper will still work. Internally uses ajax_options
163
+ #
164
+ # The example below creates a link "Name", which when clicked, will load posts_url into the section of the page named
165
+ # "page" using AJAX.
166
+ #
167
+ # <%= ajax_link_to "Name", posts_url, :pagination => "page" %>
168
+ #
169
+ def ajax_link_to(*args, &block)
170
+ if block_given? # inject new html_options argument and call link_to
171
+ html_options = args[1] || {}
172
+ args[1] = ajax_options html_options
173
+ link_to(*args,&block)
174
+ else
175
+ html_options = args[2] || {}
176
+ args[2] = ajax_options html_options
177
+ link_to(*args)
178
+ end
179
+ end
180
+
181
+ # Wrapper for form_tag, following are equivalent:
182
+ #
183
+ # <%= ajax_form_tag posts_url, :method => "post", :class => "myclass", :pagination => "page" do %>
184
+ # ...
185
+ # <% end %>
186
+ #
187
+ # <%= form_tag posts_url, ajax_options :method => "post", :class => "myclass", :pagination => "page" do %>
188
+ # ...
189
+ # <% end %>
190
+ #
191
+ def ajax_form_tag(url_for_options = {}, options = {}, &block)
192
+ options = ajax_options(options)
193
+ if block_given?
194
+ form_tag(url_for_options,options, &block)
195
+ else
196
+ form_tag(url_for_options,options)
197
+ end
198
+ end
199
+
200
+ # Wrapper for form_for. The following are equivalent
201
+ #
202
+ # <%= ajax_form_for @post, :method => "post", :html => {:class => "myclass", :pagination => "menu"} do %>
203
+ # ...
204
+ # <% end %>
205
+ #
206
+ # <%= form_for @post, :method => "post", :html => ajax_options({:class => "myclass", :pagination => "menu"}) do %>
207
+ # ...
208
+ # <% end %>
209
+ #
210
+ # Please be aware that in the second alternative, you should never set :remote => false manually, eg:
211
+ #
212
+ # <%= form_tag @post, :remote => false, :html => ajax_options(:pagination => "menu") do %><!-- Never Do This!!! -->
213
+ #
214
+ # This will prevent AJAX Pagination from being called.
215
+ #
216
+ # If you are using Formtastic or Simple Form gems, you can use the second method to add AJAX Pagination functionality
217
+ # at the same time. This form_for helper is only a convenience method (shorthand)
218
+ #
219
+ def ajax_form_for(record, options = {}, &block)
220
+ raise ArgumentError, "Missing block" unless block_given?
221
+ options[:html] ||= {}
222
+ options[:html] = ajax_options options[:html]
223
+ options.delete(:remote) # html sub-hash already has remote set true
224
+ form_for(record,options,&block)
225
+ end
226
+
125
227
  end
126
228
  end
127
229
 
@@ -14,5 +14,13 @@ module AjaxPagination
14
14
  AjaxPagination.warnings = Rails.env == 'development' # default setting
15
15
  end
16
16
  end
17
+
18
+ initializer 'ajax_pagination.redirect_filter' do
19
+ if AjaxPagination.redirect_after_filter == true
20
+ ActiveSupport.on_load(:action_controller) do
21
+ after_filter :ajax_pagination_redirect
22
+ end
23
+ end
24
+ end
17
25
  end
18
26
  end
@@ -1,3 +1,3 @@
1
1
  module AjaxPagination
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -8,8 +8,31 @@
8
8
  * Copyright (c) 2012 Ronald Ping Man Chan
9
9
  * Distributed under the LGPL license
10
10
  */
11
- if (History && History.enabled) {
12
- jQuery(document).ready(function () {
11
+ jQuery(document).ready(function () {
12
+ function minVersion(version) {
13
+ var $vrs = window.jQuery.fn.jquery.split('.'),
14
+ min = version.split('.');
15
+ for (var i=0, len=min.length; i<len; i++) {
16
+ if ($vrs[i]) {
17
+ min[i] = parseInt(min[i],10);
18
+ $vrs[i] = parseInt($vrs[i],10);
19
+ if ($vrs[i] < min[i]) return false;
20
+ else if ($vrs[i] > min[i]) return true;
21
+ }
22
+ else return false;
23
+ }
24
+ return true;
25
+ }
26
+ <% minjQuery = '1.7' %>
27
+ <% if AjaxPagination.warnings %>
28
+ if (!(History && minVersion('<%= minjQuery %>'))) { // dependencies missing
29
+ var missing = "";
30
+ if (!History) missing += "\nHistory.js not installed";
31
+ if (!minVersion('<%= minjQuery %>')) missing += "\njQuery version <%= minjQuery %>+ not installed. Currently installed: jQuery " + window.jQuery.fn.jquery;
32
+ alert("AJAX Pagination warning:" + missing);
33
+ }
34
+ <% end %>
35
+ if (History && History.enabled && minVersion('<%= minjQuery %>')) {
13
36
  (function( $ ) {
14
37
  var pagination_loader_state = new Array(); // the page we are waiting for
15
38
  var pagination_url = location.href; // url we came from, so we can see transitions of the url
@@ -23,7 +46,8 @@ if (History && History.enabled) {
23
46
  var height = paginated_content.height();
24
47
  // setup loading look
25
48
  var img = document.createElement("IMG");
26
- img.src = "<%= asset_path AjaxPagination.loading_image %>";
49
+ if (paginated_section.data("pagination") !== undefined && paginated_section.data("pagination").image !== undefined) img.src = paginated_section.data("pagination").image;
50
+ else img.src = "<%= asset_path AjaxPagination.loading_image %>";
27
51
  var margin = Math.round(height>400?100:(height/4));
28
52
  $(img).css({'margin': margin + 'px', 'max-height': (3*height/4) + 'px'}).addClass('ajaxloader');
29
53
  var div = document.createElement("DIV");
@@ -33,35 +57,76 @@ if (History && History.enabled) {
33
57
  paginated_content.append(div);
34
58
 
35
59
  // scroll to top of paginated_section if it is not visible
36
- if ($('body').scrollTop() > paginated_section.offset().top) {
37
- $('body').scrollTop(paginated_section.offset().top);
60
+ if ($(document).scrollTop() > paginated_section.offset().top - <%= AjaxPagination.scroll_margin %>) {
61
+ $(document).scrollTop(paginated_section.offset().top - <%= AjaxPagination.scroll_margin %>);
38
62
  }
39
63
  }
40
- function swapPage(pagination_name,requesturl) { // swaps the page at pagination_name to that from requesturl
64
+ // when this function is used beforeSend of an AJAX request, will use the resulting content in a section of the page
65
+ // this event handler has the same arguments as for jquery and jquery-ujs, except it also takes the name of the section to put the content into as first argument
66
+ // adapter functions will be used to reconcile the differences in arguments, this is required because jquery and jquery-ujs has different ways to get the pagination_name argument
67
+ function beforeSendHandler(pagination_name,jqXHR,settings) {
68
+ var id = "#" + pagination_name + "_paginated_section"; // element id we are looking for
69
+ var requesturl = settings.url;
70
+ if ($(id).length != 1) { // something wrong, cannot find unique section to load page into
71
+ <% if AjaxPagination.warnings %>
72
+ alert("AJAX Pagination warning:\nExpected one pagination section called " + pagination_name + ", found " + $("#" + pagination_name + "_paginated_section").length);
73
+ <% end %>
74
+ return false; // continue AJAX normally
75
+ }
41
76
  display_pagination_loader(pagination_name);
42
- pagination_loader_state[pagination_name] = requesturl; // remember which page number we are waiting for
43
- //$.ajax(requesturl,{dataType:'script',cache:true});
77
+ // register callbacks for other events
78
+ jqXHR.done(function(data, textStatus, jqXHR) {
79
+ if (requesturl != pagination_loader_state[pagination_name]) return; // ignore stale content
80
+ if (jqXHR.status == 200 && jqXHR.getResponseHeader('Location') !== null) { // special AJAX redirect
81
+ var redirecturl = jqXHR.getResponseHeader('Location');
82
+ swapPage(pagination_name,redirecturl);
83
+ pagination_url = redirecturl;
84
+ History.replaceState(null,document.title,redirecturl);
85
+ return;
86
+ }
87
+ // find matching element id in data, after removing script tags
88
+ var page = $("<div>").append(data.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,""));
89
+ var content = page.find(id);
90
+ if (content.length>0) {
91
+ $(id).html(content.html());
92
+ <% if AjaxPagination.warnings %>
93
+ alert("AJAX Pagination warning:\nExtra content returned by AJAX request ignored. Only a portion of the page content returned by the server was required. To fix this, explicitly call ajax_pagination for :pagination => \"" + pagination_name + "\" to render only the partial view required. This warning can be turned off in the ajax_pagination initializer file.");
94
+ <% end %>
95
+ }
96
+ else { // otherwise use all the content, including any scripts - we consider scripts specifically returned in the partial probably should be re-run
97
+ page = $("<div>").append(data);
98
+ content = page.find("body");
99
+ if (content.length>0) $(id).html(content.html()); // if it has a body tag, only include its contents (for full html structure), leaving out <head> etc sections.
100
+ else $(id).html(page.html()); // otherwise include the whole html snippet
101
+ }
102
+ // if page contains a title, use it
103
+ content = page.find("title");
104
+ if (content.length>0) History.replaceState(null,content.html(),location.href);
105
+ delete pagination_loader_state[pagination_name]; // not waiting for page anymore
106
+ });
107
+ jqXHR.fail(function(jqXHR, textStatus, errorThrown) {
108
+ if (requesturl != pagination_loader_state[pagination_name]) return; // ignore stale content
109
+ $(id).html(jqXHR.responseText);
110
+ delete pagination_loader_state[pagination_name]; // not waiting for page anymore
111
+ });
112
+ return true;
113
+ }
114
+ function swapPage(pagination_name,requesturl) { // swaps the page at pagination_name to that from requesturl (used by History.popState, therefore no remote link has been clicked)
115
+ // send our own ajax request, and tie it into the beforeSendHandler used for jquery-ujs as well
44
116
  $.ajax({url: requesturl, data: {pagination:pagination_name},
45
117
  dataType: 'html',
46
- success: function(data) {
47
- var id = "#" + pagination_name + "_paginated_section"; // element id we are looking for
48
- // find matching element id in data, after removing script tags
49
- var content = $("<div>").append(data.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,"")).find(id);
50
- if (content.length>0) {
51
- $(id).html(content.html());
52
- <% if AjaxPagination.warnings %>
53
- alert("AJAX Pagination warning:\nExtra content returned by AJAX request ignored. Only a portion of the page content returned by the server was required. To fix this, explicitly call ajax_pagination for :pagination => \"" + pagination_name + "\" to render only the partial view required. This warning can be turned off in the ajax_pagination initializer file.");
54
- <% end %>
55
- }
56
- else $(id).html(data); // otherwise use all the content
57
- delete pagination_loader_state[pagination_name]; // not waiting for page anymore
58
- },
59
- error: function(jqXHR, textStatus, errorThrown) {
60
- $(id).html(jqXHR.responseText);
118
+ beforeSend: function (jqXHR,settings) {
119
+ var result = beforeSendHandler(pagination_name,jqXHR,settings);
120
+ pagination_loader_state[pagination_name] = settings.url; // remember which page number we are waiting for
121
+ return result;
61
122
  }
62
123
  });
63
124
  }
64
- $('body').on("click", ".pagination a, .ajaxpagination a", function() {
125
+ // these special containers are for convenience only, to apply the required data-remote, data-pagination attributes to all links inside
126
+ $(document).on("click", ".pagination a, .ajaxpagination a, a.ajaxpagination", function(e) {
127
+ // ignore if already selected by jquery-ujs
128
+ if ($(this).filter($.rails.linkClickSelector).length>0) return true; // continue with jquery-ujs - this behaviour is necessary because we do not know if the jquery-ujs handler executes before or after this handler
129
+ // find out what data-pagination should be set to
65
130
  var pagination_container = $(this).closest(".pagination, .ajaxpagination"); // container of links (use to check for data-pagination first)
66
131
  var pagination_name = pagination_container.data('pagination');
67
132
  if (pagination_name === undefined) {
@@ -74,18 +139,45 @@ if (History && History.enabled) {
74
139
  }
75
140
  pagination_name = pagination_name[1];
76
141
  }
77
- if ($("#" + pagination_name + "_paginated_section").length != 1) { // something wrong, cannot find unique section to load page into
78
- <% if AjaxPagination.warnings %>
79
- alert("AJAX Pagination warning:\nExpected one pagination section called " + pagination_name + ", found " + $("#" + pagination_name + "_paginated_section").length);
80
- <% end %>
81
- return true; // follow link normally
142
+
143
+ // set data-remote, data-pagination
144
+ $(this).attr({'data-remote':'true'}); // needs to be set so that the jquery-ujs selectors work
145
+ $(this).data({'remote':'true','pagination':pagination_name}); // needs to be set because attributes only read into jquery's data memory once
146
+ if ($(this).data('type') === undefined) { // to be moved to ajax:before filter when https://github.com/rails/jquery-ujs/pull/241 is successful, and jquery-rails minimum version updated
147
+ $(this).data('type','html'); // AJAX Pagination requests return html be default
82
148
  }
83
- //var requesturl = $.param.querystring(this.href,{pagination:pagination_name}); // by default, adds ?pagination=page
84
- swapPage(pagination_name,this.href);
85
- pagination_url = this.href;
86
- History.pushState(null,document.title,this.href);
149
+ // stop this event from having further effect (because we do not know if jquery-ujs's event handler executed before or after us)
150
+ e.preventDefault();
151
+ e.stopImmediatePropagation();
152
+ // click element again to dispatch the event all over again - thus ensuring it is handled by jquery-ujs
153
+ $(this).click();
87
154
  return false;
88
155
  });
156
+ $(document).on("ajax:before","a, " + $.rails.inputChangeSelector, function() {
157
+ var pagination_name = $(this).data('pagination');
158
+ if (pagination_name === undefined) return true; // this is not an AJAX Pagination AJAX request
159
+ $(this).data('params',$.extend($(this).data('params'),{'pagination':pagination_name})); // add data-pagination to the params data
160
+ return true;
161
+ });
162
+ $(document).on("ajax:before","form", function() {
163
+ // alter action to include pagination parameter in the GET part of the action url
164
+ $(this).attr('action',$.param.querystring($(this).attr('action'),{pagination:$(this).data('pagination')}));
165
+ });
166
+ $(document).on("ajax:beforeSend","a, form, " + $.rails.inputChangeSelector, function (e,jqXHR,settings) {
167
+ var pagination_name = $(this).data('pagination');
168
+ if (pagination_name === undefined) return true; // this is not an AJAX Pagination AJAX request
169
+ if (beforeSendHandler(pagination_name,jqXHR,settings)) {
170
+ var data = $("#" + pagination_name + "_paginated_section").data("pagination");
171
+ if (data === undefined || data.history === undefined || data.history) {
172
+ var data = $.deparam.querystring($.url(settings.url).attr('query'));
173
+ delete data['pagination'];
174
+ pagination_url = $.param.querystring(settings.url,data,2);
175
+ History.pushState(null,document.title,pagination_url);
176
+ }
177
+ pagination_loader_state[pagination_name] = settings.url;
178
+ }
179
+ return true;
180
+ });
89
181
 
90
182
  History.Adapter.bind(window,'popstate',function(){ // popstate, but can work with hash changes as well
91
183
  var from = pagination_url, to = location.href; // from what state to what other state
@@ -95,12 +187,12 @@ if (History && History.enabled) {
95
187
  if (pagination_name == null) return; // pagination not set up properly
96
188
 
97
189
  // if data-pagination is not defined, the use default reload test
98
- if ($(this).data('pagination') === undefined) {
190
+ if ($(this).data('pagination') === undefined || $(this).data('pagination').reload === undefined) {
99
191
  // if ?pagination_name=ABC, where ABC is the same for both urls, then don't need to reload
100
192
  if ($.deparam.querystring(from)[pagination_name] === $.deparam.querystring(to)[pagination_name]) return;
101
193
  }
102
194
  else { // otherwise parse json and perform tests
103
- var reload = $(this).data('pagination');
195
+ var reload = $(this).data('pagination').reload;
104
196
  if (!(reload instanceof Array)) reload = new Array(reload);
105
197
  var changed = false;
106
198
  for (i=0;i<reload.length;i++) {
@@ -110,8 +202,8 @@ if (History && History.enabled) {
110
202
  if (reload[i].urlregex !== undefined) {
111
203
  var fstr = from, tstr = to;
112
204
  if (reload[i].urlpart !== undefined) {
113
- fstr = $.url(from).attr(reload[i].urlpart);
114
- tstr = $.url(to).attr(reload[i].urlpart);
205
+ fstr = $.url(from,true).attr(reload[i].urlpart);
206
+ tstr = $.url(to,true).attr(reload[i].urlpart);
115
207
  if (typeof(fstr)!="string" || typeof(tstr)!="string") continue; // skip
116
208
  }
117
209
  var index = 0;
@@ -126,8 +218,7 @@ if (History && History.enabled) {
126
218
  }
127
219
  if (!changed) return; // otherwise it has changed, and we must reload
128
220
  }
129
- var requesturl = $.param.querystring(location.href,{pagination:pagination_name}); // by default, adds ?pagination=page
130
- swapPage(pagination_name,requesturl);
221
+ swapPage(pagination_name,location.href);
131
222
  });
132
223
 
133
224
  pagination_url = location.href; // update url (new url recognised)
@@ -135,6 +226,6 @@ if (History && History.enabled) {
135
226
 
136
227
  History.Adapter.trigger(window,"popstate"); // update stuff on page load
137
228
  })( jQuery );
138
- });
139
- }
229
+ }
230
+ });
140
231
 
@@ -9,4 +9,18 @@ AjaxPagination.config do |config|
9
9
  # config.warnings = true
10
10
  # or to turn off in all environments
11
11
  # config.warnings = false
12
+
13
+ # Intercepts 302 redirects, if the request is an AJAX call with a ?pagination= parameter in the GET url
14
+ # This is used because browsers transparently follow 302 redirects, without the AJAX javascript code being aware
15
+ # that a redirection has taken place. The response is changed into a Status 200 OK, with an extra Location: header
16
+ # The javascript code can then manually follow the redirect, and perform the necessary cleanup tasks (change the url
17
+ # in the address bar). The default is on (true), use the following line to disable it if desired. If disabled, it can
18
+ # be re-enabled in specific controllers by adding after_filter :ajax_pagination_redirect to the class.
19
+ # config.redirect_after_filter = true
20
+
21
+ # when changing pages, AJAX Pagination scrolls to ensure the page sees the top of the changing section, plus an additional margin in pixels
22
+ # The default margin is 20 pixels, as set by the line below
23
+ # config.scroll_margin = 20
24
+ # To disable auto-scrolling, uncomment the line below
25
+ # config.scroll_margin = '-Infinity'
12
26
  end
@@ -5,7 +5,7 @@ describe AjaxPagination::ControllerAdditions do
5
5
  @controller.stub!(:params).and_return({:pagination => name, :controller => "dummycontroller"})
6
6
  end
7
7
  def stub_request_format_html(bool)
8
- @controller.stub!(:request).and_return(stub(:format => stub(:html? => bool)))
8
+ @controller.stub!(:request).and_return(stub({:format => stub(:html? => bool), :GET => {} }))
9
9
  end
10
10
  def stub_lookup_context(result)
11
11
  @controller.stub!(:lookup_context).and_return(stub(:find_all => result))
@@ -2,63 +2,148 @@ require 'spec_helper'
2
2
 
3
3
  describe 'paginating with javascript on', :js => true do
4
4
  it 'displays a loading image' do
5
+ # following 3 lines to warm up loading image
6
+ visit("http://localhost:#{SERVERPORT}") # goes to welcome page
7
+ click_link 'About'
8
+ sleep(2)
9
+
5
10
  visit("http://localhost:#{SERVERPORT}") # goes to welcome page
6
11
  page.should have_no_selector('.ajaxloader')
12
+ sleep(1)
7
13
  click_link 'About'
8
14
  page.should have_selector('.ajaxloader')
9
- sleep(0.5)
15
+ sleep(1)
10
16
  page.should have_no_selector('.ajaxloader')
11
17
  click_link 'Readme'
12
18
  page.should have_selector('.ajaxloader')
13
- sleep(0.5)
19
+ sleep(1)
14
20
  page.should have_no_selector('.ajaxloader')
15
21
  end
16
22
  it 'displays a loading image with nested and multiple paginated sections' do
17
23
  visit("http://localhost:#{SERVERPORT}/changelog")
18
- find('#page_paginated_section').find('.next_page').click
24
+ find('#_paginated_section').find('.next_page').click
19
25
  page.should have_selector('.ajaxloader')
20
- sleep(0.5)
26
+ sleep(1)
21
27
  page.should have_no_selector('.ajaxloader')
22
28
  find('#signin').click
23
29
  visit("http://localhost:#{SERVERPORT}/posts")
24
30
  sleep(2)
25
31
  find('#page_paginated_section').find('.next_page').click
26
32
  page.should have_selector('.ajaxloader')
27
- sleep(0.5)
33
+ sleep(1)
28
34
  page.should have_no_selector('.ajaxloader')
29
35
  find('#upcomingpage_paginated_section').find('.next_page').click
30
36
  page.should have_selector('.ajaxloader')
31
- sleep(0.5)
37
+ sleep(1)
32
38
  page.should have_no_selector('.ajaxloader')
33
39
  end
34
40
  it 'shows the configured loading image' do
35
41
  visit("http://localhost:#{SERVERPORT}/changelog")
36
- find('#page_paginated_section').find('.next_page').click
42
+ find('#_paginated_section').find('.next_page').click
37
43
  page.should have_xpath("//img[@class='ajaxloader' and @src = '/assets/myajax-loader.gif']")
44
+ sleep(1)
45
+ visit("http://localhost:#{SERVERPORT}/posts")
46
+ find('#_paginated_section').find('.next_page').click
47
+ page.should have_xpath("//img[@class='ajaxloader' and @src = '/assets/ajax-loader.gif']")
38
48
  end
39
49
  it 'works with browser back and forward buttons' do
40
50
  visit("http://localhost:#{SERVERPORT}/changelog")
41
- find('#page_paginated_section').find('.next_page').click
42
- sleep(0.5)
51
+ find('#_paginated_section').find('.next_page').click
52
+ sleep(1)
43
53
  click_link 'About'
44
- sleep(0.5)
54
+ sleep(1)
45
55
  click_link 'Readme'
46
56
  sleep(2)
47
57
  page.should have_no_selector('.ajaxloader')
48
58
  page.should have_selector('#readmepagetitle')
49
59
  page.evaluate_script('window.history.back();') # back to About
50
60
  page.should have_selector('.ajaxloader')
51
- sleep(0.5)
61
+ sleep(1)
52
62
  page.should have_no_selector('.ajaxloader')
53
63
  page.should have_selector('#aboutpagetitle')
54
64
  page.evaluate_script('window.history.forward();') # forward to readme
55
- sleep(0.5)
65
+ sleep(1)
56
66
  page.should have_selector('#readmepagetitle')
57
67
  page.evaluate_script('window.history.go(-2);') # back to changelog page 2
58
- sleep(0.5)
68
+ sleep(1)
59
69
  page.should have_no_selector('#aboutpagetitle')
60
70
  page.evaluate_script('window.history.forward();') # forward to about
61
- sleep(0.5)
71
+ sleep(1)
62
72
  page.should have_selector('#aboutpagetitle')
63
73
  end
74
+ it 'displays error pages within div' do
75
+ visit("http://localhost:#{SERVERPORT}") # goes to welcome page
76
+ click_link("AJAX Pagination Example Application")
77
+ sleep(2)
78
+ page.current_url.should == "http://localhost:#{SERVERPORT}/broken%20link"
79
+ page.should have_content("AJAX Pagination Example Application")
80
+ page.should have_content("No route matches")
81
+ end
82
+ it 'changes url to match redirection' do
83
+ visit("http://localhost:#{SERVERPORT}")
84
+ click_link("Posts")
85
+ sleep(1)
86
+ page.should have_content("New Post")
87
+ myurl = page.current_url # to get the canonical url
88
+ click_link("New Post")
89
+ sleep(2)
90
+ page.should have_content("Access Denied")
91
+ page.current_url.should == myurl
92
+ end
93
+ it 'submits ajax_form_tag form via post' do
94
+ visit("http://localhost:#{SERVERPORT}/pages/about")
95
+ count = page.find("#submits").html.to_i
96
+ click_button("Submit")
97
+ sleep(1)
98
+ page.should have_content("#{count+1} submit")
99
+ page.should have_selector('#aboutpagetitle') # ensures loading was via AJAX Pagination
100
+ end
101
+ it 'history does not change if :history => false' do
102
+ visit("http://localhost:#{SERVERPORT}/pages/about")
103
+ myurl = page.current_url # to get the canonical url
104
+ count = page.find("#submits").html.to_i
105
+ click_button("Submit")
106
+ sleep(1)
107
+ page.should have_content("#{count+1} submit")
108
+ page.current_url.should == myurl # url remains the same (so history has not changed)
109
+ end
110
+ it 'submits ajax_form_for form via POST and PUT and DELETE link' do
111
+ visit("http://localhost:#{SERVERPORT}")
112
+ find('#signin').click
113
+ click_link("Posts")
114
+ sleep(1)
115
+ page.should have_content("New Post")
116
+ myurl = page.current_url # to get the canonical url
117
+ visit("http://localhost:#{SERVERPORT}/posts/new")
118
+ within("#new_post") do
119
+ fill_in 'Title', :with => 'very unique title for test'
120
+ fill_in 'Content', :with => 'my supercontent'
121
+ end
122
+ click_button("Create Post");
123
+ page.should have_selector('.ajaxloader')
124
+ sleep(2)
125
+ page.should have_content("Post was successfully created.")
126
+ page.current_url.should_not == myurl # means we have gotten redirected
127
+ click_link("Edit");
128
+ within(".edit_post") do
129
+ fill_in 'Content', :with => 'my supercontent again'
130
+ end
131
+ click_button("Update Post");
132
+ page.should have_selector('.ajaxloader')
133
+ sleep(2)
134
+ page.should have_content("my supercontent again")
135
+ click_link("Destroy");
136
+ page.driver.browser.switch_to.alert.accept
137
+ page.should have_selector('.ajaxloader')
138
+ sleep(2)
139
+ page.should have_content("Post destroyed.")
140
+ end
141
+ it 'changes title' do
142
+ visit("http://localhost:#{SERVERPORT}")
143
+ title = page.evaluate_script("document.title") # because what is between the <title> tags and what is shown in the window title can differ (document.title gets set by javascript)
144
+ click_link("About");
145
+ sleep(1)
146
+ page.should have_selector('#aboutpagetitle') # ensures loading was via AJAX Pagination
147
+ page.evaluate_script("document.title").should_not == title
148
+ end
64
149
  end
@@ -15,16 +15,15 @@ describe 'paginating without javascript', :type => :request, :driver => :rack_te
15
15
  it 'still paginates nested and multiple paginated sections' do
16
16
  visit(changelog_url)
17
17
  page.should have_selector('.previous_page.disabled')
18
- find('#page_paginated_section').find('.next_page').click
18
+ find('#_paginated_section').find('.next_page').click
19
19
  page.should have_no_selector('.previous_page.disabled')
20
20
  find('#signin').click
21
21
  visit(posts_url)
22
- page.should have_selector('#page_paginated_section .previous_page.disabled')
23
- find('#page_paginated_section').find('.next_page').click
24
- page.should have_no_selector('#page_paginated_section .previous_page.disabled')
25
22
  page.should have_selector('#upcomingpage_paginated_section .previous_page.disabled')
26
23
  find('#upcomingpage_paginated_section').find('.next_page').click
27
24
  page.should have_no_selector('#upcomingpage_paginated_section .previous_page.disabled')
25
+ page.should have_selector('#page_paginated_section .previous_page.disabled')
26
+ find('#page_paginated_section').find('.next_page').click
28
27
  page.should have_no_selector('#page_paginated_section .previous_page.disabled')
29
28
  end
30
29
  end
@@ -1,11 +1,10 @@
1
1
  class ApplicationController < ActionController::Base
2
2
  protect_from_forgery
3
3
  before_filter :slowajaxload
4
- ajax_pagination :pagination => 'menu'
5
- #ajax_pagination :pagination => 'sdf', :partial => {:template => "pages/readme"}
4
+ ajax_pagination :pagination => "", :render => { :layout => "ajax" }
6
5
  def slowajaxload
7
6
  if params[:pagination] && Rails.env == "test"
8
- sleep(0.5)
7
+ sleep(1)
9
8
  end
10
9
  end
11
10
 
@@ -7,7 +7,6 @@ class ChangelogController < ApplicationController
7
7
  respond_to do |format|
8
8
  format.html # index.html.erb
9
9
  ajax_pagination(format)
10
- ajax_pagination format, :pagination => :menu
11
10
  end
12
11
  end
13
12
  end
@@ -5,7 +5,7 @@ class PagesController < ApplicationController
5
5
  @readme = IO.read(File.expand_path("../../../../../README.md",__FILE__))
6
6
  respond_to do |format|
7
7
  format.html
8
- ajax_pagination format, :pagination => :menu, :render => {:file => "pages/readme"}
8
+ ajax_pagination format, :pagination => "", :render => {:file => "pages/readme", :layout => "ajax"}
9
9
  end
10
10
  end
11
11
 
@@ -15,7 +15,7 @@ class PagesController < ApplicationController
15
15
  def welcome
16
16
  respond_to do |format|
17
17
  format.html
18
- ajax_pagination format, :pagination => :menu
18
+ ajax_pagination format, :pagination => "", :render => { :layout => "ajax" }
19
19
  end
20
20
  end
21
21
  end
@@ -9,7 +9,7 @@ class PostsController < ApplicationController
9
9
  format.html # index.html.erb
10
10
  format.json { render :json => @posts }
11
11
  ajax_pagination format, :pagination => :upcomingpage
12
- ajax_pagination format, :pagination => :menu
12
+ ajax_pagination format, :pagination => "", :render => { :layout => "ajax" }
13
13
  end
14
14
  end
15
15
 
@@ -87,7 +87,7 @@ class PostsController < ApplicationController
87
87
  @post.destroy
88
88
 
89
89
  respond_to do |format|
90
- format.html { redirect_to posts_url }
90
+ format.html { redirect_to posts_url, :notice => "Post destroyed." }
91
91
  format.json { head :no_content }
92
92
  end
93
93
  end
@@ -8,4 +8,12 @@ class SessionsController < ApplicationController
8
8
  session[:admin] = false
9
9
  redirect_to root_url, :notice => "Successfully signed out"
10
10
  end
11
+
12
+ def count
13
+ session[:count] ||= 0
14
+ if request.post?
15
+ session[:count] += 1
16
+ end
17
+ render :layout => false
18
+ end
11
19
  end
@@ -0,0 +1,8 @@
1
+ <html>
2
+ <head>
3
+ <title><%= (params[:controller] + " " + params[:action]).titleize %> - RailsApp</title>
4
+ </head>
5
+ <body>
6
+ <%= render :template => "layouts/flash" %>
7
+ </body>
8
+ </html>
@@ -1,7 +1,7 @@
1
1
  <!DOCTYPE html>
2
2
  <html>
3
3
  <head>
4
- <title>RailsApp</title>
4
+ <title><%= (params[:controller] + " " + params[:action]).titleize %> - RailsApp</title>
5
5
  <%= stylesheet_link_tag "application", :media => "all" %>
6
6
  <%= javascript_include_tag "application" %>
7
7
  <%= csrf_meta_tags %>
@@ -14,8 +14,13 @@
14
14
  <%= link_to "Sign in as admin", sessions_signin_url, :id => "signin" %>
15
15
  <% end %>
16
16
  </div>
17
- <h1>AJAX Pagination Example Application</h1>
18
- <div class="ajaxpagination menu" data-pagination="menu">
17
+ <div style="float: right; min-width: 150px;">
18
+ <%= ajax_pagination :pagination => "count", :history => false do %>
19
+ <%= render :template => "sessions/count" %>
20
+ <% end %>
21
+ </div>
22
+ <h1><%= ajax_link_to "AJAX Pagination Example Application", "broken link", :pagination => "" %><!-- note the broken link is intentional, for testing purposes --></h1>
23
+ <div class="ajaxpagination menu" data-pagination="">
19
24
  <ul>
20
25
  <li><%= link_to "Home", root_url %></li>
21
26
  <li><%= link_to "Posts", posts_url %></li>
@@ -24,14 +29,8 @@
24
29
  <li><%= link_to "About", pages_about_url %></li>
25
30
  </ul>
26
31
  </div>
27
- <%= ajax_pagination :pagination => "menu", :reload => {:urlpart => "path", :urlregex => "^.*$"}, :loadzone => true do %>
28
- <% if flash[:notice] %>
29
- <p id="notice"><%= flash[:notice] %></p>
30
- <% end %>
31
- <% if flash[:alert] %>
32
- <p id="alert"><%= flash[:alert] %></p>
33
- <% end %>
34
- <%= yield %>
32
+ <%= ajax_pagination :pagination => "", :reload => {:urlpart => "path", :urlregex => "^.*$"}, :loadzone => true do %>
33
+ <%= render :template => "layouts/flash" %>
35
34
  <% end %>
36
35
  </body>
37
36
  </html>
@@ -0,0 +1,7 @@
1
+ <% if flash[:notice] %>
2
+ <p id="notice"><%= flash[:notice] %></p>
3
+ <% end %>
4
+ <% if flash[:alert] %>
5
+ <p id="alert"><%= flash[:alert] %></p>
6
+ <% end %>
7
+ <%= yield %>
@@ -1,4 +1,4 @@
1
- <%= form_for(@post) do |f| %>
1
+ <%= ajax_form_for(@post) do |f| %>
2
2
  <% if @post.errors.any? %>
3
3
  <div id="error_explanation">
4
4
  <h2><%= pluralize(@post.errors.count, "error") %> prohibited this post from being saved:</h2>
@@ -4,10 +4,10 @@
4
4
  <div style="border-bottom: 1px solid black; padding-bottom: 10px;">
5
5
  <p><h3><%= post.title %></h3><i>Published on <%= post.published_at.strftime('%d %B %Y') if post.published_at? %></i></p>
6
6
  <%= simple_format(post.content) %>
7
- <%= link_to 'Show', post %>
7
+ <%= ajax_link_to 'Show', post, :pagination => "" %>
8
8
  <% if session[:admin] %>
9
- | <%= link_to 'Edit', edit_post_path(post) %> |
10
- <%= link_to 'Destroy', post, :confirm => 'Are you sure?', :method => :delete %>
9
+ | <%= ajax_link_to 'Edit', edit_post_path(post), :pagination => "" %> |
10
+ <%= ajax_link_to 'Destroy', post, :confirm => 'Are you sure?', :method => :delete, :pagination => "" %>
11
11
  <% end %>
12
12
  </div>
13
13
  <% end %>
@@ -8,8 +8,8 @@
8
8
  <h3>Published</h3>
9
9
  <% end %>
10
10
 
11
- <%= ajax_pagination %>
11
+ <%= ajax_pagination :image => "ajax-loader.gif" %>
12
12
 
13
13
  <br />
14
14
 
15
- <%= link_to 'New Post', new_post_path %>
15
+ <%= ajax_link_to 'New Post', new_post_path %><!-- This link is intentionally unsecured, so non-admins will get a redirection to an "access denied" page -->
@@ -8,6 +8,7 @@
8
8
 
9
9
 
10
10
  <% if session[:admin] %>
11
- <%= link_to 'Edit', edit_post_path(@post) %> |
11
+ <%= ajax_link_to 'Edit', edit_post_path(@post) %> |
12
+ <%= ajax_link_to 'Destroy', @post, :confirm => 'Are you sure?', :method => :delete, :pagination => "" %> |
12
13
  <% end %>
13
- <%= link_to 'Back', posts_path %>
14
+ <%= ajax_link_to 'Back', posts_path %>
@@ -0,0 +1,7 @@
1
+ <span id="submits"><%= session[:count].to_i %></span>
2
+ submit<%= 's' unless session[:count]==1 %> so far.
3
+ <%= ajax_form_tag sessions_count_url, :method => "post", :pagination => "count" do %>
4
+ <span class="actions">
5
+ <%= submit_tag "Submit" %>
6
+ </span>
7
+ <% end %>
@@ -12,6 +12,8 @@ RailsApp::Application.routes.draw do
12
12
  namespace :sessions do
13
13
  get 'signin'
14
14
  get 'signout'
15
+ get 'count'
16
+ post 'count'
15
17
  end
16
18
  # The priority is based upon order of creation:
17
19
  # first created -> highest priority.
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ajax_pagination
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-02-23 00:00:00.000000000 Z
12
+ date: 2012-02-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
16
- requirement: &17693300 !ruby/object:Gem::Requirement
16
+ requirement: &5860940 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *17693300
24
+ version_requirements: *5860940
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rspec-rails
27
- requirement: &17692880 !ruby/object:Gem::Requirement
27
+ requirement: &5860300 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *17692880
35
+ version_requirements: *5860300
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: sqlite3
38
- requirement: &17692440 !ruby/object:Gem::Requirement
38
+ requirement: &5859780 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *17692440
46
+ version_requirements: *5859780
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: will_paginate
49
- requirement: &17691880 !ruby/object:Gem::Requirement
49
+ requirement: &5859220 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: '0'
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *17691880
57
+ version_requirements: *5859220
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: capybara
60
- requirement: &17691360 !ruby/object:Gem::Requirement
60
+ requirement: &5858640 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: '0'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *17691360
68
+ version_requirements: *5858640
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rails
71
- requirement: &17690540 !ruby/object:Gem::Requirement
71
+ requirement: &5857940 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ! '>='
@@ -76,10 +76,10 @@ dependencies:
76
76
  version: '3.1'
77
77
  type: :runtime
78
78
  prerelease: false
79
- version_requirements: *17690540
79
+ version_requirements: *5857940
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: jquery-rails
82
- requirement: &17720620 !ruby/object:Gem::Requirement
82
+ requirement: &5857180 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - ! '>='
@@ -87,10 +87,10 @@ dependencies:
87
87
  version: 1.0.17
88
88
  type: :runtime
89
89
  prerelease: false
90
- version_requirements: *17720620
90
+ version_requirements: *5857180
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: jquery-historyjs
93
- requirement: &17720080 !ruby/object:Gem::Requirement
93
+ requirement: &5856700 !ruby/object:Gem::Requirement
94
94
  none: false
95
95
  requirements:
96
96
  - - ! '>='
@@ -98,7 +98,7 @@ dependencies:
98
98
  version: '0'
99
99
  type: :runtime
100
100
  prerelease: false
101
- version_requirements: *17720080
101
+ version_requirements: *5856700
102
102
  description: Handles AJAX pagination for you, by hooking up the links you want to
103
103
  load content with javascript in designated page containers. Each webpage can have
104
104
  multiple page containers, each with a different set of pagination links. The page
@@ -158,7 +158,9 @@ files:
158
158
  - spec/rails_app/app/models/post.rb
159
159
  - spec/rails_app/app/views/changelog/_page.html.erb
160
160
  - spec/rails_app/app/views/changelog/index.html.erb
161
+ - spec/rails_app/app/views/layouts/ajax.html.erb
161
162
  - spec/rails_app/app/views/layouts/application.html.erb
163
+ - spec/rails_app/app/views/layouts/flash.html.erb
162
164
  - spec/rails_app/app/views/pages/about.html.erb
163
165
  - spec/rails_app/app/views/pages/readme.html.erb
164
166
  - spec/rails_app/app/views/pages/welcome.html.erb
@@ -169,6 +171,7 @@ files:
169
171
  - spec/rails_app/app/views/posts/index.html.erb
170
172
  - spec/rails_app/app/views/posts/new.html.erb
171
173
  - spec/rails_app/app/views/posts/show.html.erb
174
+ - spec/rails_app/app/views/sessions/count.html.erb
172
175
  - spec/rails_app/config.ru
173
176
  - spec/rails_app/config/application.rb
174
177
  - spec/rails_app/config/boot.rb
@@ -215,12 +218,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
215
218
  - - ! '>='
216
219
  - !ruby/object:Gem::Version
217
220
  version: '0'
221
+ segments:
222
+ - 0
223
+ hash: -3757162407362940278
218
224
  required_rubygems_version: !ruby/object:Gem::Requirement
219
225
  none: false
220
226
  requirements:
221
227
  - - ! '>='
222
228
  - !ruby/object:Gem::Version
223
229
  version: '0'
230
+ segments:
231
+ - 0
232
+ hash: -3757162407362940278
224
233
  requirements: []
225
234
  rubyforge_project: ajax_pagination
226
235
  rubygems_version: 1.8.15