Commit b264ce02d1936312a503f13eedd6eb7ea1a524ac
Exists in
master
and in
1 other branch
Merge pull request #107 from martinciu/notices-paging
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); | ... | ... |