rails-pjax 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -29,7 +29,7 @@ The PJAX container has to be marked with data-pjax-container attribute, so for e
29
29
 
30
30
  FIXME: Currently the layout is hardcoded to "application". Need to delegate that to the specific layout of the controller.
31
31
 
32
- Examples for redirect_to
32
+ Examples for redirect_pjax_to
33
33
  -----------------------------
34
34
 
35
35
  class ProjectsController < ApplicationController
@@ -44,19 +44,19 @@ Examples for redirect_to
44
44
 
45
45
  def create
46
46
  @project = Project.create params[:project]
47
- redirect_to :show, @project
47
+ redirect_pjax_to :show, @project
48
48
  end
49
49
 
50
50
  def update
51
51
  @project.update_attributes params[:project]
52
- redirect_to :show, @project
52
+ redirect_pjax_to :show, @project
53
53
  end
54
54
 
55
55
  def destroy
56
56
  @project.destroy
57
57
 
58
58
  index # set the objects needed for rendering index
59
- redirect_to :index
59
+ redirect_pjax_to :index
60
60
  end
61
61
 
62
62
  private
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'rails-pjax'
3
- s.version = '0.0.1'
3
+ s.version = '0.0.2'
4
4
  s.author = 'Helioid (original David Heinemeier Hansson and PJAX by Chris Wanstrath)'
5
5
  s.email = 'info@helioid.com'
6
6
  s.summary = 'PJAX integration for Rails 3.1+'
Binary file
@@ -24,8 +24,8 @@
24
24
  //
25
25
  // Returns the jQuery object
26
26
  $.fn.pjax = function( container, options ) {
27
- return this.live('click.pjax', function(event){
28
- handleClick(event, container, options)
27
+ return this.live('click', function(event){
28
+ return handleClick(event, container, options)
29
29
  })
30
30
  }
31
31
 
@@ -53,11 +53,7 @@ function handleClick(event, container, options) {
53
53
 
54
54
  var link = event.currentTarget
55
55
 
56
- // If current target isnt a link, try to find the first A descendant
57
56
  if (link.tagName.toUpperCase() !== 'A')
58
- link = $(link).find('a')[0]
59
-
60
- if (!link)
61
57
  throw "$.fn.pjax or $.pjax.click requires an anchor element"
62
58
 
63
59
  // Middle click, cmd click, and ctrl click should open
@@ -85,6 +81,30 @@ function handleClick(event, container, options) {
85
81
  $.pjax($.extend({}, defaults, options))
86
82
 
87
83
  event.preventDefault()
84
+ return false
85
+ }
86
+
87
+ // Internal: Strips _pjax param from url
88
+ //
89
+ // url - String
90
+ //
91
+ // Returns String.
92
+ function stripPjaxParam(url) {
93
+ return url
94
+ .replace(/\?_pjax=[^&]+&?/, '?')
95
+ .replace(/_pjax=[^&]+&?/, '')
96
+ .replace(/[\?&]$/, '')
97
+ }
98
+
99
+ // Internal: Parse URL components and returns a Locationish object.
100
+ //
101
+ // url - String URL
102
+ //
103
+ // Returns HTMLAnchorElement that acts like Location.
104
+ function parseURL(url) {
105
+ var a = document.createElement('a')
106
+ a.href = url
107
+ return a
88
108
  }
89
109
 
90
110
 
@@ -119,7 +139,8 @@ var pjax = $.pjax = function( options ) {
119
139
  // DEPRECATED: use options.target
120
140
  if (!target && options.clickedElement) target = options.clickedElement[0]
121
141
 
122
- var hash = parseURL(options.url).hash
142
+ var url = options.url
143
+ var hash = parseURL(url).hash
123
144
 
124
145
  // DEPRECATED: Save references to original event callbacks. However,
125
146
  // listening for custom pjax:* events is prefered.
@@ -146,6 +167,8 @@ var pjax = $.pjax = function( options ) {
146
167
  var timeoutTimer
147
168
 
148
169
  options.beforeSend = function(xhr, settings) {
170
+ url = stripPjaxParam(settings.url)
171
+
149
172
  if (settings.timeout > 0) {
150
173
  timeoutTimer = setTimeout(function() {
151
174
  if (fire('pjax:timeout', [xhr, options]))
@@ -169,18 +192,9 @@ var pjax = $.pjax = function( options ) {
169
192
 
170
193
  if (!fire('pjax:beforeSend', [xhr, settings])) return false
171
194
 
172
- if (options.push && !options.replace) {
173
- // Cache current container element before replacing it
174
- containerCache.push(pjax.state.id, context.clone(true, true).contents())
175
-
176
- window.history.pushState(null, "", options.url)
177
- }
178
-
179
195
  fire('pjax:start', [xhr, options])
180
196
  // start.pjax is deprecated
181
197
  fire('start.pjax', [xhr, options])
182
-
183
- fire('pjax:send', [xhr, settings])
184
198
  }
185
199
 
186
200
  options.complete = function(xhr, textStatus) {
@@ -198,42 +212,72 @@ var pjax = $.pjax = function( options ) {
198
212
  }
199
213
 
200
214
  options.error = function(xhr, textStatus, errorThrown) {
201
- var container = extractContainer("", xhr, options)
215
+ var respUrl = xhr.getResponseHeader('X-PJAX-URL')
216
+ if (respUrl) url = stripPjaxParam(respUrl)
202
217
 
203
218
  // DEPRECATED: Invoke original `error` handler
204
219
  if (oldError) oldError.apply(this, arguments)
205
220
 
206
221
  var allowed = fire('pjax:error', [xhr, textStatus, errorThrown, options])
207
222
  if (textStatus !== 'abort' && allowed)
208
- window.location = container.url
223
+ window.location = url
209
224
  }
210
225
 
211
226
  options.success = function(data, status, xhr) {
212
- var container = extractContainer(data, xhr, options)
227
+ var respUrl = xhr.getResponseHeader('X-PJAX-URL')
228
+ if (respUrl) url = stripPjaxParam(respUrl)
229
+
230
+ var title, oldTitle = document.title
231
+
232
+ if ( options.fragment ) {
233
+ // If they specified a fragment, look for it in the response
234
+ // and pull it out.
235
+ var html = $('<html>').html(data)
236
+ var $fragment = html.find(options.fragment)
237
+ if ( $fragment.length ) {
238
+ this.html($fragment.contents())
239
+
240
+ // If there's a <title> tag in the response, use it as
241
+ // the page's title. Otherwise, look for data-title and title attributes.
242
+ title = html.find('title').text() || $fragment.attr('title') || $fragment.data('title')
243
+ } else {
244
+ return window.location = url
245
+ }
246
+ } else {
247
+ // If we got no data or an entire web page, go directly
248
+ // to the page and let normal error handling happen.
249
+ if ( !$.trim(data) || /<html/i.test(data) )
250
+ return window.location = url
251
+
252
+ this.html(data)
213
253
 
214
- if (!container.contents) {
215
- window.location = container.url
216
- return
254
+ // If there's a <title> tag in the response, use it as
255
+ // the page's title.
256
+ title = this.find('title').remove().text()
217
257
  }
218
258
 
219
- pjax.state = {
220
- id: options.id || uniqueId(),
221
- url: container.url,
222
- container: context.selector,
259
+ if ( title ) document.title = $.trim(title)
260
+
261
+ var state = {
262
+ url: url,
263
+ pjax: this.selector,
223
264
  fragment: options.fragment,
224
265
  timeout: options.timeout
225
266
  }
226
267
 
227
- if (options.push || options.replace) {
228
- window.history.replaceState(pjax.state, container.title, container.url)
229
- }
230
-
231
- if (container.title) document.title = container.title
232
- context.html(container.contents)
268
+ if ( options.replace ) {
269
+ pjax.active = true
270
+ window.history.replaceState(state, document.title, url)
271
+ } else if ( options.push ) {
272
+ // this extra replaceState before first push ensures good back
273
+ // button behavior
274
+ if ( !pjax.active ) {
275
+ window.history.replaceState($.extend({}, state, {url:null}), oldTitle)
276
+ pjax.active = true
277
+ }
233
278
 
234
- // Scroll to top by default
235
- if (typeof options.scrollTo === 'number')
236
- $(window).scrollTop(options.scrollTo)
279
+ window.history.pushState(state, document.title, url)
280
+ }
237
281
 
238
282
  // Google Analytics support
239
283
  if ( (options.replace || options.push) && window._gaq )
@@ -252,21 +296,6 @@ var pjax = $.pjax = function( options ) {
252
296
  }
253
297
 
254
298
 
255
- // Initialize pjax.state for the initial page load. Assume we're
256
- // using the container and options of the link we're loading for the
257
- // back button to the initial page. This ensures good back button
258
- // behavior.
259
- if (!pjax.state) {
260
- pjax.state = {
261
- id: uniqueId(),
262
- url: window.location.href,
263
- container: context.selector,
264
- fragment: options.fragment,
265
- timeout: options.timeout
266
- }
267
- window.history.replaceState(pjax.state, document.title)
268
- }
269
-
270
299
  // Cancel the current request if we're already pjaxing
271
300
  var xhr = pjax.xhr
272
301
  if ( xhr && xhr.readyState < 4) {
@@ -276,47 +305,12 @@ var pjax = $.pjax = function( options ) {
276
305
 
277
306
  pjax.options = options
278
307
  pjax.xhr = $.ajax(options)
279
-
280
- // pjax event is deprecated
281
308
  $(document).trigger('pjax', [pjax.xhr, options])
282
309
 
283
310
  return pjax.xhr
284
311
  }
285
312
 
286
313
 
287
- // Internal: Generate unique id for state object.
288
- //
289
- // Use a timestamp instead of a counter since ids should still be
290
- // unique across page loads.
291
- //
292
- // Returns Number.
293
- function uniqueId() {
294
- return (new Date).getTime()
295
- }
296
-
297
- // Internal: Strips _pjax param from url
298
- //
299
- // url - String
300
- //
301
- // Returns String.
302
- function stripPjaxParam(url) {
303
- return url
304
- .replace(/\?_pjax=[^&]+&?/, '?')
305
- .replace(/_pjax=[^&]+&?/, '')
306
- .replace(/[\?&]$/, '')
307
- }
308
-
309
- // Internal: Parse URL components and returns a Locationish object.
310
- //
311
- // url - String URL
312
- //
313
- // Returns HTMLAnchorElement that acts like Location.
314
- function parseURL(url) {
315
- var a = document.createElement('a')
316
- a.href = url
317
- return a
318
- }
319
-
320
314
  // Internal: Build options Object for arguments.
321
315
  //
322
316
  // For convenience the first parameter can be either the container or
@@ -376,181 +370,15 @@ function findContainerFor(container) {
376
370
  }
377
371
  }
378
372
 
379
- // Internal: Filter and find all elements matching the selector.
380
- //
381
- // Where $.fn.find only matches descendants, findAll will test all the
382
- // top level elements in the jQuery object as well.
383
- //
384
- // elems - jQuery object of Elements
385
- // selector - String selector to match
386
- //
387
- // Returns a jQuery object.
388
- function findAll(elems, selector) {
389
- var results = $()
390
- elems.each(function() {
391
- if ($(this).is(selector))
392
- results = results.add(this)
393
- results = results.add(selector, this)
394
- })
395
- return results
396
- }
397
-
398
- // Internal: Extracts container and metadata from response.
399
- //
400
- // 1. Extracts X-PJAX-URL header if set
401
- // 2. Extracts inline <title> tags
402
- // 3. Builds response Element and extracts fragment if set
403
- //
404
- // data - String response data
405
- // xhr - XHR response
406
- // options - pjax options Object
407
- //
408
- // Returns an Object with url, title, and contents keys.
409
- function extractContainer(data, xhr, options) {
410
- var obj = {}
411
-
412
- // Prefer X-PJAX-URL header if it was set, otherwise fallback to
413
- // using the original requested url.
414
- obj.url = stripPjaxParam(xhr.getResponseHeader('X-PJAX-URL') || options.url)
415
-
416
- // Attempt to parse response html into elements
417
- var $data = $(data)
418
-
419
- // If response data is empty, return fast
420
- if ($data.length === 0)
421
- return obj
422
-
423
- // If there's a <title> tag in the response, use it as
424
- // the page's title.
425
- obj.title = findAll($data, 'title').last().text()
426
-
427
- if (options.fragment) {
428
- // If they specified a fragment, look for it in the response
429
- // and pull it out.
430
- var $fragment = findAll($data, options.fragment).first()
431
-
432
- if ($fragment.length) {
433
- obj.contents = $fragment.contents()
434
-
435
- // If there's no title, look for data-title and title attributes
436
- // on the fragment
437
- if (!obj.title)
438
- obj.title = $fragment.attr('title') || $fragment.data('title')
439
- }
440
-
441
- } else if (!/<html/i.test(data)) {
442
- obj.contents = $data
443
- }
444
-
445
- // Clean up any <title> tags
446
- if (obj.contents) {
447
- // Remove any parent title elements
448
- obj.contents = obj.contents.not('title')
449
-
450
- // Then scrub any titles from their descendents
451
- obj.contents.find('title').remove()
452
- }
453
-
454
- // Trim any whitespace off the title
455
- if (obj.title) obj.title = $.trim(obj.title)
456
-
457
- return obj
458
- }
459
-
460
- // Public: Reload current page with pjax.
461
- //
462
- // Returns whatever $.pjax returns.
463
- pjax.reload = function(container, options) {
464
- var defaults = {
465
- url: window.location.href,
466
- push: false,
467
- replace: true,
468
- scrollTo: false
469
- }
470
-
471
- return $.pjax($.extend(defaults, optionsFor(container, options)))
472
- }
473
-
474
373
 
475
374
  pjax.defaults = {
476
375
  timeout: 650,
477
376
  push: true,
478
377
  replace: false,
479
378
  type: 'GET',
480
- dataType: 'html',
481
- scrollTo: 0,
482
- maxCacheLength: 20
379
+ dataType: 'html'
483
380
  }
484
381
 
485
- // Internal: History DOM caching class.
486
- function Cache() {
487
- this.mapping = {}
488
- this.forwardStack = []
489
- this.backStack = []
490
- }
491
- // Push previous state id and container contents into the history
492
- // cache. Should be called in conjunction with `pushState` to save the
493
- // previous container contents.
494
- //
495
- // id - State ID Number
496
- // value - DOM Element to cache
497
- //
498
- // Returns nothing.
499
- Cache.prototype.push = function(id, value) {
500
- this.mapping[id] = value
501
- this.backStack.push(id)
502
-
503
- // Remove all entires in forward history stack after pushing
504
- // a new page.
505
- while (this.forwardStack.length)
506
- delete this.mapping[this.forwardStack.shift()]
507
-
508
- // Trim back history stack to max cache length.
509
- while (this.backStack.length > pjax.defaults.maxCacheLength)
510
- delete this.mapping[this.backStack.shift()]
511
- }
512
- // Retrieve cached DOM Element for state id.
513
- //
514
- // id - State ID Number
515
- //
516
- // Returns DOM Element(s) or undefined if cache miss.
517
- Cache.prototype.get = function(id) {
518
- return this.mapping[id]
519
- }
520
- // Shifts cache from forward history cache to back stack. Should be
521
- // called on `popstate` with the previous state id and container
522
- // contents.
523
- //
524
- // id - State ID Number
525
- // value - DOM Element to cache
526
- //
527
- // Returns nothing.
528
- Cache.prototype.forward = function(id, value) {
529
- this.mapping[id] = value
530
- this.backStack.push(id)
531
-
532
- if (id = this.forwardStack.pop())
533
- delete this.mapping[id]
534
- }
535
- // Shifts cache from back history cache to forward stack. Should be
536
- // called on `popstate` with the previous state id and container
537
- // contents.
538
- //
539
- // id - State ID Number
540
- // value - DOM Element to cache
541
- //
542
- // Returns nothing.
543
- Cache.prototype.back = function(id, value) {
544
- this.mapping[id] = value
545
- this.forwardStack.push(id)
546
-
547
- if (id = this.backStack.pop())
548
- delete this.mapping[id]
549
- }
550
-
551
- var containerCache = new Cache
552
-
553
-
554
382
  // Export $.pjax.click
555
383
  pjax.click = handleClick
556
384
 
@@ -572,54 +400,18 @@ $(window).bind('popstate', function(event){
572
400
 
573
401
  var state = event.state
574
402
 
575
- if (state && state.container) {
576
- var container = $(state.container)
577
- if (container.length) {
578
- var contents = containerCache.get(state.id)
579
-
580
- if (pjax.state) {
581
- // Since state ids always increase, we can deduce the history
582
- // direction from the previous state.
583
- var direction = pjax.state.id < state.id ? 'forward' : 'back'
584
-
585
- // Cache current container before replacement and inform the
586
- // cache which direction the history shifted.
587
- containerCache[direction](pjax.state.id, container.clone(true, true).contents())
588
- }
589
-
590
- var options = {
591
- id: state.id,
592
- url: state.url,
403
+ if ( state && state.pjax ) {
404
+ var container = state.pjax
405
+ if ( $(container+'').length )
406
+ $.pjax({
407
+ url: state.url || location.href,
408
+ fragment: state.fragment,
593
409
  container: container,
594
410
  push: false,
595
- fragment: state.fragment,
596
- timeout: state.timeout,
597
- scrollTo: false
598
- }
599
-
600
- if (contents) {
601
- // pjax event is deprecated
602
- $(document).trigger('pjax', [null, options])
603
- container.trigger('pjax:start', [null, options])
604
- // end.pjax event is deprecated
605
- container.trigger('start.pjax', [null, options])
606
-
607
- container.html(contents)
608
- pjax.state = state
609
-
610
- container.trigger('pjax:end', [null, options])
611
- // end.pjax event is deprecated
612
- container.trigger('end.pjax', [null, options])
613
- } else {
614
- $.pjax(options)
615
- }
616
-
617
- // Force reflow/relayout before the browser tries to restore the
618
- // scroll position.
619
- container[0].offsetHeight
620
- } else {
411
+ timeout: state.timeout
412
+ })
413
+ else
621
414
  window.location = location.href
622
- }
623
415
  }
624
416
  })
625
417
 
@@ -672,7 +464,6 @@ if ( !$.support.pjax ) {
672
464
  form.submit()
673
465
  }
674
466
  $.pjax.click = $.noop
675
- $.pjax.reload = window.location.reload
676
467
  $.fn.pjax = function() { return this }
677
468
  }
678
469
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails-pjax
3
3
  version: !ruby/object:Gem::Version
4
- hash: 29
4
+ hash: 27
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 1
10
- version: 0.0.1
9
+ - 2
10
+ version: 0.0.2
11
11
  platform: ruby
12
12
  authors:
13
13
  - Helioid (original David Heinemeier Hansson and PJAX by Chris Wanstrath)
@@ -42,6 +42,7 @@ extra_rdoc_files: []
42
42
  files:
43
43
  - ./pjax_rails.gemspec
44
44
  - ./README.md
45
+ - ./rails-pjax-0.0.1.gem
45
46
  - ./vendor/assets/javascripts/jquery.pjax.js
46
47
  - ./lib/pjax_rails.rb
47
48
  - ./lib/pjax.rb