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.
- data/CHANGELOG.md +16 -0
- data/README.md +52 -20
- data/lib/ajax_pagination.rb +10 -0
- data/lib/ajax_pagination/controller_additions.rb +29 -7
- data/lib/ajax_pagination/helper_additions.rb +109 -7
- data/lib/ajax_pagination/rails.rb +8 -0
- data/lib/ajax_pagination/version.rb +1 -1
- data/lib/assets/javascripts/ajax_pagination.js.erb +132 -41
- data/lib/generators/templates/ajax_pagination.rb +14 -0
- data/spec/ajax_pagination/controller_additions_spec.rb +1 -1
- data/spec/ajax_pagination/integration/ajaxpaginate_spec.rb +99 -14
- data/spec/ajax_pagination/integration/nojavascript_spec.rb +3 -4
- data/spec/rails_app/app/controllers/application_controller.rb +2 -3
- data/spec/rails_app/app/controllers/changelog_controller.rb +0 -1
- data/spec/rails_app/app/controllers/pages_controller.rb +2 -2
- data/spec/rails_app/app/controllers/posts_controller.rb +2 -2
- data/spec/rails_app/app/controllers/sessions_controller.rb +8 -0
- data/spec/rails_app/app/views/layouts/ajax.html.erb +8 -0
- data/spec/rails_app/app/views/layouts/application.html.erb +10 -11
- data/spec/rails_app/app/views/layouts/flash.html.erb +7 -0
- data/spec/rails_app/app/views/posts/_form.html.erb +1 -1
- data/spec/rails_app/app/views/posts/_page.html.erb +3 -3
- data/spec/rails_app/app/views/posts/index.html.erb +2 -2
- data/spec/rails_app/app/views/posts/show.html.erb +3 -2
- data/spec/rails_app/app/views/sessions/count.html.erb +7 -0
- data/spec/rails_app/config/routes.rb +2 -0
- data/spec/rails_app/db/development.sqlite3 +0 -0
- 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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
* [
|
44
|
-
* [
|
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
|
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
|
-
###
|
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
|
-
|
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 :
|
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
|
data/lib/ajax_pagination.rb
CHANGED
@@ -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
|
-
|
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]}/_#{
|
24
|
-
view = { :partial =>
|
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
|
-
|
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] + "/_" +
|
81
|
-
view = {:partial =>
|
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
|
-
|
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
|
-
|
86
|
-
|
87
|
-
|
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
|
-
|
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
|
-
#
|
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
|
@@ -8,8 +8,31 @@
|
|
8
8
|
* Copyright (c) 2012 Ronald Ping Man Chan
|
9
9
|
* Distributed under the LGPL license
|
10
10
|
*/
|
11
|
-
|
12
|
-
|
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
|
-
|
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 ($(
|
37
|
-
$(
|
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
|
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
|
-
|
43
|
-
|
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
|
-
|
47
|
-
var
|
48
|
-
|
49
|
-
|
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
|
-
|
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
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
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
|
-
//
|
84
|
-
|
85
|
-
|
86
|
-
|
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
|
-
|
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(
|
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(
|
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('#
|
24
|
+
find('#_paginated_section').find('.next_page').click
|
19
25
|
page.should have_selector('.ajaxloader')
|
20
|
-
sleep(
|
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(
|
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(
|
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('#
|
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('#
|
42
|
-
sleep(
|
51
|
+
find('#_paginated_section').find('.next_page').click
|
52
|
+
sleep(1)
|
43
53
|
click_link 'About'
|
44
|
-
sleep(
|
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(
|
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(
|
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(
|
68
|
+
sleep(1)
|
59
69
|
page.should have_no_selector('#aboutpagetitle')
|
60
70
|
page.evaluate_script('window.history.forward();') # forward to about
|
61
|
-
sleep(
|
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('#
|
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 =>
|
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(
|
7
|
+
sleep(1)
|
9
8
|
end
|
10
9
|
end
|
11
10
|
|
@@ -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 =>
|
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 => :
|
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 => :
|
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
|
@@ -1,7 +1,7 @@
|
|
1
1
|
<!DOCTYPE html>
|
2
2
|
<html>
|
3
3
|
<head>
|
4
|
-
<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
|
-
<
|
18
|
-
|
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 => "
|
28
|
-
|
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>
|
@@ -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
|
-
<%=
|
7
|
+
<%= ajax_link_to 'Show', post, :pagination => "" %>
|
8
8
|
<% if session[:admin] %>
|
9
|
-
| <%=
|
10
|
-
<%=
|
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
|
-
<%=
|
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
|
-
<%=
|
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
|
-
<%=
|
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 %>
|
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.
|
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-
|
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: &
|
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: *
|
24
|
+
version_requirements: *5860940
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rspec-rails
|
27
|
-
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: *
|
35
|
+
version_requirements: *5860300
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: sqlite3
|
38
|
-
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: *
|
46
|
+
version_requirements: *5859780
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: will_paginate
|
49
|
-
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: *
|
57
|
+
version_requirements: *5859220
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: capybara
|
60
|
-
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: *
|
68
|
+
version_requirements: *5858640
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: rails
|
71
|
-
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: *
|
79
|
+
version_requirements: *5857940
|
80
80
|
- !ruby/object:Gem::Dependency
|
81
81
|
name: jquery-rails
|
82
|
-
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: *
|
90
|
+
version_requirements: *5857180
|
91
91
|
- !ruby/object:Gem::Dependency
|
92
92
|
name: jquery-historyjs
|
93
|
-
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: *
|
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
|