pjax_rails 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,6 +9,11 @@ module Pjax
9
9
  before_filter :set_pjax_url, :if => :pjax_request?
10
10
  end
11
11
 
12
+ class Error < StandardError; end
13
+ class Unsupported < Error; end
14
+
15
+ rescue_from Pjax::Unsupported, :with => :pjax_unsupported
16
+
12
17
  protected
13
18
  def pjax_request?
14
19
  env['HTTP_X_PJAX'].present?
@@ -23,9 +28,28 @@ module Pjax
23
28
  request.headers['X-PJAX-Container']
24
29
  end
25
30
 
31
+ def pjax_unsupported
32
+ head :not_acceptable
33
+ end
34
+
35
+ # Call in a before_filter or in an action to disable pjax on an action.
36
+ #
37
+ # Examples
38
+ #
39
+ # before_filter :prevent_pjax!
40
+ #
41
+ # def login
42
+ # prevent_pjax!
43
+ # # ...
44
+ # end
45
+ #
46
+ def prevent_pjax!
47
+ raise PjaxUnsupported if pjax_request?
48
+ end
49
+
26
50
  def strip_pjax_param
27
51
  params.delete(:_pjax)
28
- request.env['QUERY_STRING'].sub!(/_pjax=[^&]+&?/, '')
52
+ request.env['QUERY_STRING'] = request.env['QUERY_STRING'].sub(/_pjax=[^&]+&?/, '')
29
53
 
30
54
  request.env.delete('rack.request.query_string')
31
55
  request.env.delete('rack.request.query_hash')
@@ -23,7 +23,7 @@
23
23
  // the options object.
24
24
  //
25
25
  // Returns the jQuery object
26
- $.fn.pjax = function( container, options ) {
26
+ function fnPjax(container, options) {
27
27
  return this.live('click.pjax', function(event){
28
28
  handleClick(event, container, options)
29
29
  })
@@ -82,12 +82,11 @@ function handleClick(event, container, options) {
82
82
  fragment: null
83
83
  }
84
84
 
85
- $.pjax($.extend({}, defaults, options))
85
+ pjax($.extend({}, defaults, options))
86
86
 
87
87
  event.preventDefault()
88
88
  }
89
89
 
90
-
91
90
  // Loads a URL with ajax, puts the response body inside a container,
92
91
  // then pushState()'s the loaded URL.
93
92
  //
@@ -107,7 +106,7 @@ function handleClick(event, container, options) {
107
106
  // console.log( xhr.readyState )
108
107
  //
109
108
  // Returns whatever $.ajax returns.
110
- var pjax = $.pjax = function( options ) {
109
+ function pjax(options) {
111
110
  options = $.extend(true, {}, $.ajaxSettings, pjax.defaults, options)
112
111
 
113
112
  if ($.isFunction(options.url)) {
@@ -146,6 +145,12 @@ var pjax = $.pjax = function( options ) {
146
145
  var timeoutTimer
147
146
 
148
147
  options.beforeSend = function(xhr, settings) {
148
+ // No timeout for non-GET requests
149
+ // Its not safe to request the resource again with a fallback method.
150
+ if (settings.type !== 'GET') {
151
+ settings.timeout = 0
152
+ }
153
+
149
154
  if (settings.timeout > 0) {
150
155
  timeoutTimer = setTimeout(function() {
151
156
  if (fire('pjax:timeout', [xhr, options]))
@@ -302,6 +307,121 @@ var pjax = $.pjax = function( options ) {
302
307
  return pjax.xhr
303
308
  }
304
309
 
310
+ // Public: Reload current page with pjax.
311
+ //
312
+ // Returns whatever $.pjax returns.
313
+ function pjaxReload(container, options) {
314
+ var defaults = {
315
+ url: window.location.href,
316
+ push: false,
317
+ replace: true,
318
+ scrollTo: false
319
+ }
320
+
321
+ return pjax($.extend(defaults, optionsFor(container, options)))
322
+ }
323
+
324
+ // popstate handler takes care of the back and forward buttons
325
+ //
326
+ // You probably shouldn't use pjax on pages with other pushState
327
+ // stuff yet.
328
+ function onPjaxPopstate(event) {
329
+ var state = event.state
330
+
331
+ if (state && state.container) {
332
+ var container = $(state.container)
333
+ if (container.length) {
334
+ var contents = cacheMapping[state.id]
335
+
336
+ if (pjax.state) {
337
+ // Since state ids always increase, we can deduce the history
338
+ // direction from the previous state.
339
+ var direction = pjax.state.id < state.id ? 'forward' : 'back'
340
+
341
+ // Cache current container before replacement and inform the
342
+ // cache which direction the history shifted.
343
+ cachePop(direction, pjax.state.id, container.clone().contents())
344
+ }
345
+
346
+ var popstateEvent = $.Event('pjax:popstate', {
347
+ state: state,
348
+ direction: direction
349
+ })
350
+ container.trigger(popstateEvent)
351
+
352
+ var options = {
353
+ id: state.id,
354
+ url: state.url,
355
+ container: container,
356
+ push: false,
357
+ fragment: state.fragment,
358
+ timeout: state.timeout,
359
+ scrollTo: false
360
+ }
361
+
362
+ if (contents) {
363
+ // pjax event is deprecated
364
+ $(document).trigger('pjax', [null, options])
365
+ container.trigger('pjax:start', [null, options])
366
+ // end.pjax event is deprecated
367
+ container.trigger('start.pjax', [null, options])
368
+
369
+ if (state.title) document.title = state.title
370
+ container.html(contents)
371
+ pjax.state = state
372
+
373
+ container.trigger('pjax:end', [null, options])
374
+ // end.pjax event is deprecated
375
+ container.trigger('end.pjax', [null, options])
376
+ } else {
377
+ pjax(options)
378
+ }
379
+
380
+ // Force reflow/relayout before the browser tries to restore the
381
+ // scroll position.
382
+ container[0].offsetHeight
383
+ } else {
384
+ window.location = location.href
385
+ }
386
+ }
387
+ }
388
+
389
+ // Fallback version of main pjax function for browsers that don't
390
+ // support pushState.
391
+ //
392
+ // Returns nothing since it retriggers a hard form submission.
393
+ function fallbackPjax(options) {
394
+ var url = $.isFunction(options.url) ? options.url() : options.url,
395
+ method = options.type ? options.type.toUpperCase() : 'GET'
396
+
397
+ var form = $('<form>', {
398
+ method: method === 'GET' ? 'GET' : 'POST',
399
+ action: url,
400
+ style: 'display:none'
401
+ })
402
+
403
+ if (method !== 'GET' && method !== 'POST') {
404
+ form.append($('<input>', {
405
+ type: 'hidden',
406
+ name: '_method',
407
+ value: method.toLowerCase()
408
+ }))
409
+ }
410
+
411
+ var data = options.data
412
+ if (typeof data === 'string') {
413
+ $.each(data.split('&'), function(index, value) {
414
+ var pair = value.split('=')
415
+ form.append($('<input>', {type: 'hidden', name: pair[0], value: pair[1]}))
416
+ })
417
+ } else if (typeof data === 'object') {
418
+ for (key in data)
419
+ form.append($('<input>', {type: 'hidden', name: key, value: data[key]}))
420
+ }
421
+
422
+ $(document.body).append(form)
423
+ form.submit()
424
+ }
305
425
 
306
426
  // Internal: Generate unique id for state object.
307
427
  //
@@ -476,35 +596,11 @@ function extractContainer(data, xhr, options) {
476
596
  return obj
477
597
  }
478
598
 
479
- // Public: Reload current page with pjax.
480
- //
481
- // Returns whatever $.pjax returns.
482
- pjax.reload = function(container, options) {
483
- var defaults = {
484
- url: window.location.href,
485
- push: false,
486
- replace: true,
487
- scrollTo: false
488
- }
489
-
490
- return $.pjax($.extend(defaults, optionsFor(container, options)))
491
- }
492
-
493
-
494
- pjax.defaults = {
495
- timeout: 650,
496
- push: true,
497
- replace: false,
498
- type: 'GET',
499
- dataType: 'html',
500
- scrollTo: 0,
501
- maxCacheLength: 20
502
- }
503
-
504
599
  // Internal: History DOM caching class.
505
600
  var cacheMapping = {}
506
601
  var cacheForwardStack = []
507
602
  var cacheBackStack = []
603
+
508
604
  // Push previous state id and container contents into the history
509
605
  // cache. Should be called in conjunction with `pushState` to save the
510
606
  // previous container contents.
@@ -526,6 +622,7 @@ function cachePush(id, value) {
526
622
  while (cacheBackStack.length > pjax.defaults.maxCacheLength)
527
623
  delete cacheMapping[cacheBackStack.shift()]
528
624
  }
625
+
529
626
  // Shifts cache from directional history cache. Should be
530
627
  // called on `popstate` with the previous state id and container
531
628
  // contents.
@@ -552,75 +649,54 @@ function cachePop(direction, id, value) {
552
649
  delete cacheMapping[id]
553
650
  }
554
651
 
555
-
556
- // Export $.pjax.click
557
- pjax.click = handleClick
558
-
559
-
560
- // popstate handler takes care of the back and forward buttons
652
+ // Install pjax functions on $.pjax to enable pushState behavior.
561
653
  //
562
- // You probably shouldn't use pjax on pages with other pushState
563
- // stuff yet.
564
- $(window).bind('popstate', function(event){
565
- var state = event.state
566
-
567
- if (state && state.container) {
568
- var container = $(state.container)
569
- if (container.length) {
570
- var contents = cacheMapping[state.id]
571
-
572
- if (pjax.state) {
573
- // Since state ids always increase, we can deduce the history
574
- // direction from the previous state.
575
- var direction = pjax.state.id < state.id ? 'forward' : 'back'
576
-
577
- // Cache current container before replacement and inform the
578
- // cache which direction the history shifted.
579
- cachePop(direction, pjax.state.id, container.clone().contents())
580
- }
581
-
582
- var popstateEvent = $.Event('pjax:popstate', {
583
- state: state,
584
- direction: direction
585
- })
586
- container.trigger(popstateEvent)
587
-
588
- var options = {
589
- id: state.id,
590
- url: state.url,
591
- container: container,
592
- push: false,
593
- fragment: state.fragment,
594
- timeout: state.timeout,
595
- scrollTo: false
596
- }
597
-
598
- if (contents) {
599
- // pjax event is deprecated
600
- $(document).trigger('pjax', [null, options])
601
- container.trigger('pjax:start', [null, options])
602
- // end.pjax event is deprecated
603
- container.trigger('start.pjax', [null, options])
604
-
605
- if (state.title) document.title = state.title
606
- container.html(contents)
607
- pjax.state = state
608
-
609
- container.trigger('pjax:end', [null, options])
610
- // end.pjax event is deprecated
611
- container.trigger('end.pjax', [null, options])
612
- } else {
613
- $.pjax(options)
614
- }
615
-
616
- // Force reflow/relayout before the browser tries to restore the
617
- // scroll position.
618
- container[0].offsetHeight
619
- } else {
620
- window.location = location.href
621
- }
654
+ // Does nothing if already enabled.
655
+ //
656
+ // Examples
657
+ //
658
+ // $.pjax.enable()
659
+ //
660
+ // Returns nothing.
661
+ function enable() {
662
+ $.fn.pjax = fnPjax
663
+ $.pjax = pjax
664
+ $.pjax.enable = $.noop
665
+ $.pjax.disable = disable
666
+ $.pjax.click = handleClick
667
+ $.pjax.reload = pjaxReload
668
+ $.pjax.defaults = {
669
+ timeout: 650,
670
+ push: true,
671
+ replace: false,
672
+ type: 'GET',
673
+ dataType: 'html',
674
+ scrollTo: 0,
675
+ maxCacheLength: 20
622
676
  }
623
- })
677
+ $(window).bind('popstate.pjax', onPjaxPopstate)
678
+ }
679
+
680
+ // Disable pushState behavior.
681
+ //
682
+ // This is the case when a browser doesn't support pushState. It is
683
+ // sometimes useful to disable pushState for debugging on a modern
684
+ // browser.
685
+ //
686
+ // Examples
687
+ //
688
+ // $.pjax.disable()
689
+ //
690
+ // Returns nothing.
691
+ function disable() {
692
+ $.fn.pjax = function() { return this }
693
+ $.pjax = fallbackPjax
694
+ $.pjax.enable = enable
695
+ $.pjax.disable = $.noop
696
+ $.pjax.click = $.noop
697
+ $.pjax.reload = window.location.reload
698
+ $(window).unbind('popstate.pjax', onPjaxPopstate)
699
+ }
624
700
 
625
701
 
626
702
  // Add the state property to jQuery's event object so we can use it in
@@ -628,50 +704,12 @@ $(window).bind('popstate', function(event){
628
704
  if ( $.inArray('state', $.event.props) < 0 )
629
705
  $.event.props.push('state')
630
706
 
631
-
632
- // Is pjax supported by this browser?
707
+ // Is pjax supported by this browser?
633
708
  $.support.pjax =
634
- window.history && window.history.pushState && window.history.replaceState
709
+ window.history && window.history.pushState && window.history.replaceState &&
635
710
  // pushState isn't reliable on iOS until 5.
636
- && !navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]|WebApps\/.+CFNetwork)/)
637
-
638
- // Fall back to normalcy for older browsers.
639
- if ( !$.support.pjax ) {
640
- $.pjax = function( options ) {
641
- var url = $.isFunction(options.url) ? options.url() : options.url,
642
- method = options.type ? options.type.toUpperCase() : 'GET'
643
-
644
- var form = $('<form>', {
645
- method: method === 'GET' ? 'GET' : 'POST',
646
- action: url,
647
- style: 'display:none'
648
- })
649
-
650
- if (method !== 'GET' && method !== 'POST') {
651
- form.append($('<input>', {
652
- type: 'hidden',
653
- name: '_method',
654
- value: method.toLowerCase()
655
- }))
656
- }
657
-
658
- var data = options.data
659
- if (typeof data === 'string') {
660
- $.each(data.split('&'), function(index, value) {
661
- var pair = value.split('=')
662
- form.append($('<input>', {type: 'hidden', name: pair[0], value: pair[1]}))
663
- })
664
- } else if (typeof data === 'object') {
665
- for (key in data)
666
- form.append($('<input>', {type: 'hidden', name: key, value: data[key]}))
667
- }
711
+ !navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]|WebApps\/.+CFNetwork)/)
668
712
 
669
- $(document.body).append(form)
670
- form.submit()
671
- }
672
- $.pjax.click = $.noop
673
- $.pjax.reload = window.location.reload
674
- $.fn.pjax = function() { return this }
675
- }
713
+ $.support.pjax ? enable() : disable()
676
714
 
677
715
  })(jQuery);
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pjax_rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-07-09 00:00:00.000000000 Z
12
+ date: 2012-08-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: jquery-rails