Commit e17caf089de82c5ffadb0e15b7ed67ea3a36896f
1 parent
b9c9ce3b
Exists in
master
and in
1 other branch
notices paging using pjax
Showing
4 changed files
with
285 additions
and
4 deletions
Show diff stats
app/controllers/errs_controller.rb
... | ... | @@ -34,6 +34,10 @@ class ErrsController < ApplicationController |
34 | 34 | @notices = @problem.notices.paginate(:page => page, :per_page => 1) |
35 | 35 | @notice = @notices.first |
36 | 36 | @comment = Comment.new |
37 | + if request.headers['X-PJAX'] | |
38 | + params["_pjax"] = nil | |
39 | + render :layout => false | |
40 | + end | |
37 | 41 | end |
38 | 42 | |
39 | 43 | def create_issue | ... | ... |
config/application.rb
... | ... | @@ -39,7 +39,7 @@ module Errbit |
39 | 39 | # config.i18n.default_locale = :de |
40 | 40 | |
41 | 41 | # JavaScript files you want as :defaults (application.js is always included). |
42 | - config.action_view.javascript_expansions[:defaults] = %w(jquery underscore-1.1.6 rails form) | |
42 | + config.action_view.javascript_expansions[:defaults] = %w(jquery underscore-1.1.6 rails form jquery.pjax) | |
43 | 43 | |
44 | 44 | # > rails generate - config |
45 | 45 | config.generators do |g| | ... | ... |
public/javascripts/application.js
... | ... | @@ -2,6 +2,8 @@ |
2 | 2 | |
3 | 3 | $(function() { |
4 | 4 | |
5 | + var currentTab = "summary"; | |
6 | + | |
5 | 7 | function init() { |
6 | 8 | |
7 | 9 | activateTabbedPanels(); |
... | ... | @@ -31,6 +33,17 @@ $(function() { |
31 | 33 | $('input[type=submit][data-action]').click(function() { |
32 | 34 | $(this).closest('form').attr('action', $(this).attr('data-action')); |
33 | 35 | }); |
36 | + | |
37 | + $('.notice-pagination').each(function() { | |
38 | + $('.notice-pagination a').pjax('#content', { timeout: 2000}); | |
39 | + $('#content').bind('pjax:start', function() { | |
40 | + currentTab = $('.tab-bar ul li a.button.active').attr('rel'); | |
41 | + }); | |
42 | + | |
43 | + $('#content').bind('pjax:end', function() { | |
44 | + activateTabbedPanels(); | |
45 | + }); | |
46 | + }); | |
34 | 47 | } |
35 | 48 | |
36 | 49 | function activateTabbedPanels() { |
... | ... | @@ -39,13 +52,13 @@ $(function() { |
39 | 52 | var panel = $('#'+tab.attr('rel')); |
40 | 53 | panel.addClass('panel'); |
41 | 54 | panel.find('h3').hide(); |
42 | - }) | |
55 | + }); | |
43 | 56 | |
44 | 57 | $('.tab-bar a').click(function(){ |
45 | 58 | activateTab($(this)); |
46 | 59 | return(false); |
47 | 60 | }); |
48 | - activateTab($('.tab-bar a').first()); | |
61 | + activateTab($('.tab-bar ul li a.button[rel=' + currentTab + ']')); | |
49 | 62 | } |
50 | 63 | |
51 | 64 | function activateTab(tab) { |
... | ... | @@ -65,7 +78,7 @@ $(function() { |
65 | 78 | var checkbox = $(this).find('input[name="problems[]"]'); |
66 | 79 | checkbox.attr('checked', !checkbox.is(':checked')); |
67 | 80 | } |
68 | - }) | |
81 | + }); | |
69 | 82 | } |
70 | 83 | |
71 | 84 | init(); | ... | ... |
... | ... | @@ -0,0 +1,264 @@ |
1 | +// jquery.pjax.js | |
2 | +// copyright chris wanstrath | |
3 | +// https://github.com/defunkt/jquery-pjax | |
4 | + | |
5 | +(function($){ | |
6 | + | |
7 | +// When called on a link, fetches the href with ajax into the | |
8 | +// container specified as the first parameter or with the data-pjax | |
9 | +// attribute on the link itself. | |
10 | +// | |
11 | +// Tries to make sure the back button and ctrl+click work the way | |
12 | +// you'd expect. | |
13 | +// | |
14 | +// Accepts a jQuery ajax options object that may include these | |
15 | +// pjax specific options: | |
16 | +// | |
17 | +// container - Where to stick the response body. Usually a String selector. | |
18 | +// $(container).html(xhr.responseBody) | |
19 | +// push - Whether to pushState the URL. Defaults to true (of course). | |
20 | +// replace - Want to use replaceState instead? That's cool. | |
21 | +// | |
22 | +// For convenience the first parameter can be either the container or | |
23 | +// the options object. | |
24 | +// | |
25 | +// Returns the jQuery object | |
26 | +$.fn.pjax = function( container, options ) { | |
27 | + if ( options ) | |
28 | + options.container = container | |
29 | + else | |
30 | + options = $.isPlainObject(container) ? container : {container:container} | |
31 | + | |
32 | + // We can't persist $objects using the history API so we must use | |
33 | + // a String selector. Bail if we got anything else. | |
34 | + if ( options.container && typeof options.container !== 'string' ) { | |
35 | + throw "pjax container must be a string selector!" | |
36 | + return false | |
37 | + } | |
38 | + | |
39 | + return this.live('click', function(event){ | |
40 | + // Middle click, cmd click, and ctrl click should open | |
41 | + // links in a new tab as normal. | |
42 | + if ( event.which > 1 || event.metaKey ) | |
43 | + return true | |
44 | + | |
45 | + var defaults = { | |
46 | + url: this.href, | |
47 | + container: $(this).attr('data-pjax'), | |
48 | + clickedElement: $(this), | |
49 | + fragment: null | |
50 | + } | |
51 | + | |
52 | + $.pjax($.extend({}, defaults, options)) | |
53 | + | |
54 | + event.preventDefault() | |
55 | + }) | |
56 | +} | |
57 | + | |
58 | + | |
59 | +// Loads a URL with ajax, puts the response body inside a container, | |
60 | +// then pushState()'s the loaded URL. | |
61 | +// | |
62 | +// Works just like $.ajax in that it accepts a jQuery ajax | |
63 | +// settings object (with keys like url, type, data, etc). | |
64 | +// | |
65 | +// Accepts these extra keys: | |
66 | +// | |
67 | +// container - Where to stick the response body. Must be a String. | |
68 | +// $(container).html(xhr.responseBody) | |
69 | +// push - Whether to pushState the URL. Defaults to true (of course). | |
70 | +// replace - Want to use replaceState instead? That's cool. | |
71 | +// | |
72 | +// Use it just like $.ajax: | |
73 | +// | |
74 | +// var xhr = $.pjax({ url: this.href, container: '#main' }) | |
75 | +// console.log( xhr.readyState ) | |
76 | +// | |
77 | +// Returns whatever $.ajax returns. | |
78 | +var pjax = $.pjax = function( options ) { | |
79 | + var $container = $(options.container), | |
80 | + success = options.success || $.noop | |
81 | + | |
82 | + // We don't want to let anyone override our success handler. | |
83 | + delete options.success | |
84 | + | |
85 | + // We can't persist $objects using the history API so we must use | |
86 | + // a String selector. Bail if we got anything else. | |
87 | + if ( typeof options.container !== 'string' ) | |
88 | + throw "pjax container must be a string selector!" | |
89 | + | |
90 | + options = $.extend(true, {}, pjax.defaults, options) | |
91 | + | |
92 | + if ( $.isFunction(options.url) ) { | |
93 | + options.url = options.url() | |
94 | + } | |
95 | + | |
96 | + options.context = $container | |
97 | + | |
98 | + options.success = function(data){ | |
99 | + if ( options.fragment ) { | |
100 | + // If they specified a fragment, look for it in the response | |
101 | + // and pull it out. | |
102 | + var $fragment = $(data).find(options.fragment) | |
103 | + if ( $fragment.length ) | |
104 | + data = $fragment.children() | |
105 | + else | |
106 | + return window.location = options.url | |
107 | + } else { | |
108 | + // If we got no data or an entire web page, go directly | |
109 | + // to the page and let normal error handling happen. | |
110 | + if ( !$.trim(data) || /<html/i.test(data) ) | |
111 | + return window.location = options.url | |
112 | + } | |
113 | + | |
114 | + // Make it happen. | |
115 | + this.html(data) | |
116 | + | |
117 | + // If there's a <title> tag in the response, use it as | |
118 | + // the page's title. | |
119 | + var oldTitle = document.title, | |
120 | + title = $.trim( this.find('title').remove().text() ) | |
121 | + if ( title ) document.title = title | |
122 | + | |
123 | + // No <title>? Fragment? Look for data-title and title attributes. | |
124 | + if ( !title && options.fragment ) { | |
125 | + title = $fragment.attr('title') || $fragment.data('title') | |
126 | + } | |
127 | + | |
128 | + var state = { | |
129 | + pjax: options.container, | |
130 | + fragment: options.fragment, | |
131 | + timeout: options.timeout | |
132 | + } | |
133 | + | |
134 | + // If there are extra params, save the complete URL in the state object | |
135 | + var query = $.param(options.data) | |
136 | + if ( query != "_pjax=true" ) | |
137 | + state.url = options.url + (/\?/.test(options.url) ? "&" : "?") + query | |
138 | + | |
139 | + if ( options.replace ) { | |
140 | + window.history.replaceState(state, document.title, options.url) | |
141 | + } else if ( options.push ) { | |
142 | + // this extra replaceState before first push ensures good back | |
143 | + // button behavior | |
144 | + if ( !pjax.active ) { | |
145 | + window.history.replaceState($.extend({}, state, {url:null}), oldTitle) | |
146 | + pjax.active = true | |
147 | + } | |
148 | + | |
149 | + window.history.pushState(state, document.title, options.url) | |
150 | + } | |
151 | + | |
152 | + // Google Analytics support | |
153 | + if ( (options.replace || options.push) && window._gaq ) | |
154 | + _gaq.push(['_trackPageview']) | |
155 | + | |
156 | + // If the URL has a hash in it, make sure the browser | |
157 | + // knows to navigate to the hash. | |
158 | + var hash = window.location.hash.toString() | |
159 | + if ( hash !== '' ) { | |
160 | + window.location.href = hash | |
161 | + } | |
162 | + | |
163 | + // Invoke their success handler if they gave us one. | |
164 | + success.apply(this, arguments) | |
165 | + } | |
166 | + | |
167 | + // Cancel the current request if we're already pjaxing | |
168 | + var xhr = pjax.xhr | |
169 | + if ( xhr && xhr.readyState < 4) { | |
170 | + xhr.onreadystatechange = $.noop | |
171 | + xhr.abort() | |
172 | + } | |
173 | + | |
174 | + pjax.options = options | |
175 | + pjax.xhr = $.ajax(options) | |
176 | + $(document).trigger('pjax', [pjax.xhr, options]) | |
177 | + | |
178 | + return pjax.xhr | |
179 | +} | |
180 | + | |
181 | + | |
182 | +pjax.defaults = { | |
183 | + timeout: 650, | |
184 | + push: true, | |
185 | + replace: false, | |
186 | + // We want the browser to maintain two separate internal caches: one for | |
187 | + // pjax'd partial page loads and one for normal page loads. Without | |
188 | + // adding this secret parameter, some browsers will often confuse the two. | |
189 | + data: { _pjax: true }, | |
190 | + type: 'GET', | |
191 | + dataType: 'html', | |
192 | + beforeSend: function(xhr){ | |
193 | + this.trigger('pjax:start', [xhr, pjax.options]) | |
194 | + // start.pjax is deprecated | |
195 | + this.trigger('start.pjax', [xhr, pjax.options]) | |
196 | + xhr.setRequestHeader('X-PJAX', 'true') | |
197 | + }, | |
198 | + error: function(xhr, textStatus, errorThrown){ | |
199 | + if ( textStatus !== 'abort' ) | |
200 | + window.location = pjax.options.url | |
201 | + }, | |
202 | + complete: function(xhr){ | |
203 | + this.trigger('pjax:end', [xhr, pjax.options]) | |
204 | + // end.pjax is deprecated | |
205 | + this.trigger('end.pjax', [xhr, pjax.options]) | |
206 | + } | |
207 | +} | |
208 | + | |
209 | + | |
210 | +// Used to detect initial (useless) popstate. | |
211 | +// If history.state exists, assume browser isn't going to fire initial popstate. | |
212 | +var popped = ('state' in window.history), initialURL = location.href | |
213 | + | |
214 | + | |
215 | +// popstate handler takes care of the back and forward buttons | |
216 | +// | |
217 | +// You probably shouldn't use pjax on pages with other pushState | |
218 | +// stuff yet. | |
219 | +$(window).bind('popstate', function(event){ | |
220 | + // Ignore inital popstate that some browsers fire on page load | |
221 | + var initialPop = !popped && location.href == initialURL | |
222 | + popped = true | |
223 | + if ( initialPop ) return | |
224 | + | |
225 | + var state = event.state | |
226 | + | |
227 | + if ( state && state.pjax ) { | |
228 | + var container = state.pjax | |
229 | + if ( $(container+'').length ) | |
230 | + $.pjax({ | |
231 | + url: state.url || location.href, | |
232 | + fragment: state.fragment, | |
233 | + container: container, | |
234 | + push: false, | |
235 | + timeout: state.timeout | |
236 | + }) | |
237 | + else | |
238 | + window.location = location.href | |
239 | + } | |
240 | +}) | |
241 | + | |
242 | + | |
243 | +// Add the state property to jQuery's event object so we can use it in | |
244 | +// $(window).bind('popstate') | |
245 | +if ( $.inArray('state', $.event.props) < 0 ) | |
246 | + $.event.props.push('state') | |
247 | + | |
248 | + | |
249 | +// Is pjax supported by this browser? | |
250 | +$.support.pjax = | |
251 | + window.history && window.history.pushState && window.history.replaceState | |
252 | + // pushState isn't reliable on iOS yet. | |
253 | + && !navigator.userAgent.match(/(iPod|iPhone|iPad|WebApps\/.+CFNetwork)/) | |
254 | + | |
255 | + | |
256 | +// Fall back to normalcy for older browsers. | |
257 | +if ( !$.support.pjax ) { | |
258 | + $.pjax = function( options ) { | |
259 | + window.location = $.isFunction(options.url) ? options.url() : options.url | |
260 | + } | |
261 | + $.fn.pjax = function() { return this } | |
262 | +} | |
263 | + | |
264 | +})(jQuery); | ... | ... |