pjax_rails 0.4.0 → 0.5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 097fb0fd95fb00440599199ed502f5c2a25267e7
4
- data.tar.gz: eb7d75f29172e733146484d404c436a692c5c8e8
2
+ SHA256:
3
+ metadata.gz: e64970b670dfdbc7ccfbdfeba5c68017592043da6a44dfca4bf802f38488d992
4
+ data.tar.gz: 3086bae93e9e83e8205d1f69890abe1e0ff75a99377601feb2661f83a2f275d7
5
5
  SHA512:
6
- metadata.gz: c4dde951c62dbff0cc9b0d9b7628a6a09e8d8c06ea8b1f0cdde2b8f1f01af7ff9dd619a1b29c7c737517441fefd4accab2e6f7d3478e1d41effa7799f00b0494
7
- data.tar.gz: 0e810bd47708b355702028630a96c91a722548768fc9f9693d11088c2ae9ca80741981af24821cfa0c697dde7c29f912e7068e35eaf716f9019dd662d4dc2354
6
+ metadata.gz: cdb57cac74960ca7a13a480866417c960a96e462ea216e5821d0ac696bd5b539f99e30bf930b57c143af75bb176266d9292a0b684d31e7189e5cb8934d1d11b5
7
+ data.tar.gz: 6368084ddad6e1ea125b884aff74108f7ae4c418384a5d9137c5607175db33a826a3f199b1f812a65c7b79d469764cfd16e775d8cb666170b0a1868a017848fc
@@ -7,8 +7,13 @@ module Pjax
7
7
 
8
8
  rescue_from Pjax::Unsupported, :with => :pjax_unsupported
9
9
 
10
- before_filter :strip_pjax_param, :if => :pjax_request?
11
- before_filter :set_pjax_url, :if => :pjax_request?
10
+ if respond_to? :before_action
11
+ before_action :strip_pjax_param, :if => :pjax_request?
12
+ before_action :set_pjax_url, :if => :pjax_request?
13
+ else
14
+ before_filter :strip_pjax_param, :if => :pjax_request?
15
+ before_filter :set_pjax_url, :if => :pjax_request?
16
+ end
12
17
  end
13
18
 
14
19
  class Error < StandardError; end
@@ -32,11 +37,11 @@ module Pjax
32
37
  head :not_acceptable
33
38
  end
34
39
 
35
- # Call in a before_filter or in an action to disable pjax on an action.
40
+ # Call in a before_action or in an action to disable pjax on an action.
36
41
  #
37
42
  # Examples
38
43
  #
39
- # before_filter :prevent_pjax!
44
+ # before_action :prevent_pjax!
40
45
  #
41
46
  # def login
42
47
  # prevent_pjax!
@@ -1,6 +1,8 @@
1
- // jquery.pjax.js
2
- // copyright chris wanstrath
3
- // https://github.com/defunkt/jquery-pjax
1
+ /*!
2
+ * Copyright 2012, Chris Wanstrath
3
+ * Released under the MIT License
4
+ * https://github.com/defunkt/jquery-pjax
5
+ */
4
6
 
5
7
  (function($){
6
8
 
@@ -17,9 +19,7 @@
17
19
  // pjax specific options:
18
20
  //
19
21
  //
20
- // container - Where to stick the response body. Usually a String selector.
21
- // $(container).html(xhr.responseBody)
22
- // (default: current jquery context)
22
+ // container - String selector for the element where to place the response body.
23
23
  // push - Whether to pushState the URL. Defaults to true (of course).
24
24
  // replace - Want to use replaceState instead? That's cool.
25
25
  //
@@ -28,11 +28,13 @@
28
28
  //
29
29
  // Returns the jQuery object
30
30
  function fnPjax(selector, container, options) {
31
- var context = this
31
+ options = optionsFor(container, options)
32
32
  return this.on('click.pjax', selector, function(event) {
33
- var opts = $.extend({}, optionsFor(container, options))
34
- if (!opts.container)
35
- opts.container = $(this).attr('data-pjax') || context
33
+ var opts = options
34
+ if (!opts.container) {
35
+ opts = $.extend({}, options)
36
+ opts.container = $(this).attr('data-pjax')
37
+ }
36
38
  handleClick(event, opts)
37
39
  })
38
40
  }
@@ -50,16 +52,12 @@ function fnPjax(selector, container, options) {
50
52
  // // is the same as
51
53
  // $(document).pjax('a')
52
54
  //
53
- // $(document).on('click', 'a', function(event) {
54
- // var container = $(this).closest('[data-pjax-container]')
55
- // $.pjax.click(event, container)
56
- // })
57
- //
58
55
  // Returns nothing.
59
56
  function handleClick(event, container, options) {
60
57
  options = optionsFor(container, options)
61
58
 
62
59
  var link = event.currentTarget
60
+ var $link = $(link)
63
61
 
64
62
  if (link.tagName.toUpperCase() !== 'A')
65
63
  throw "$.fn.pjax or $.pjax.click requires an anchor element"
@@ -73,28 +71,28 @@ function handleClick(event, container, options) {
73
71
  if ( location.protocol !== link.protocol || location.hostname !== link.hostname )
74
72
  return
75
73
 
76
- // Ignore anchors on the same page
77
- if (link.hash && link.href.replace(link.hash, '') ===
78
- location.href.replace(location.hash, ''))
74
+ // Ignore case when a hash is being tacked on the current URL
75
+ if ( link.href.indexOf('#') > -1 && stripHash(link) == stripHash(location) )
79
76
  return
80
77
 
81
- // Ignore empty anchor "foo.html#"
82
- if (link.href === location.href + '#')
78
+ // Ignore event with default prevented
79
+ if (event.isDefaultPrevented())
83
80
  return
84
81
 
85
82
  var defaults = {
86
83
  url: link.href,
87
- container: $(link).attr('data-pjax'),
84
+ container: $link.attr('data-pjax'),
88
85
  target: link
89
86
  }
90
87
 
91
88
  var opts = $.extend({}, defaults, options)
92
89
  var clickEvent = $.Event('pjax:click')
93
- $(link).trigger(clickEvent, [opts])
90
+ $link.trigger(clickEvent, [opts])
94
91
 
95
92
  if (!clickEvent.isDefaultPrevented()) {
96
93
  pjax(opts)
97
94
  event.preventDefault()
95
+ $link.trigger('pjax:clicked', [opts])
98
96
  }
99
97
  }
100
98
 
@@ -108,8 +106,7 @@ function handleClick(event, container, options) {
108
106
  // Examples
109
107
  //
110
108
  // $(document).on('submit', 'form', function(event) {
111
- // var container = $(this).closest('[data-pjax-container]')
112
- // $.pjax.submit(event, container)
109
+ // $.pjax.submit(event, '[data-pjax-container]')
113
110
  // })
114
111
  //
115
112
  // Returns nothing.
@@ -117,18 +114,32 @@ function handleSubmit(event, container, options) {
117
114
  options = optionsFor(container, options)
118
115
 
119
116
  var form = event.currentTarget
117
+ var $form = $(form)
120
118
 
121
119
  if (form.tagName.toUpperCase() !== 'FORM')
122
120
  throw "$.pjax.submit requires a form element"
123
121
 
124
122
  var defaults = {
125
- type: form.method.toUpperCase(),
126
- url: form.action,
127
- data: $(form).serializeArray(),
128
- container: $(form).attr('data-pjax'),
123
+ type: ($form.attr('method') || 'GET').toUpperCase(),
124
+ url: $form.attr('action'),
125
+ container: $form.attr('data-pjax'),
129
126
  target: form
130
127
  }
131
128
 
129
+ if (defaults.type !== 'GET' && window.FormData !== undefined) {
130
+ defaults.data = new FormData(form)
131
+ defaults.processData = false
132
+ defaults.contentType = false
133
+ } else {
134
+ // Can't handle file uploads, exit
135
+ if ($form.find(':file').length) {
136
+ return
137
+ }
138
+
139
+ // Fallback to manually serializing the fields
140
+ defaults.data = $form.serializeArray()
141
+ }
142
+
132
143
  pjax($.extend({}, defaults, options))
133
144
 
134
145
  event.preventDefault()
@@ -142,8 +153,7 @@ function handleSubmit(event, container, options) {
142
153
  //
143
154
  // Accepts these extra keys:
144
155
  //
145
- // container - Where to stick the response body.
146
- // $(container).html(xhr.responseBody)
156
+ // container - String selector for where to stick the response body.
147
157
  // push - Whether to pushState the URL. Defaults to true (of course).
148
158
  // replace - Want to use replaceState instead? That's cool.
149
159
  //
@@ -160,21 +170,32 @@ function pjax(options) {
160
170
  options.url = options.url()
161
171
  }
162
172
 
163
- var target = options.target
164
-
165
173
  var hash = parseURL(options.url).hash
166
174
 
167
- var context = options.context = findContainerFor(options.container)
175
+ var containerType = $.type(options.container)
176
+ if (containerType !== 'string') {
177
+ throw "expected string value for 'container' option; got " + containerType
178
+ }
179
+ var context = options.context = $(options.container)
180
+ if (!context.length) {
181
+ throw "the container selector '" + options.container + "' did not match anything"
182
+ }
168
183
 
169
184
  // We want the browser to maintain two separate internal caches: one
170
185
  // for pjax'd partial page loads and one for normal page loads.
171
186
  // Without adding this secret parameter, some browsers will often
172
187
  // confuse the two.
173
188
  if (!options.data) options.data = {}
174
- options.data._pjax = context.selector
189
+ if ($.isArray(options.data)) {
190
+ options.data.push({name: '_pjax', value: options.container})
191
+ } else {
192
+ options.data._pjax = options.container
193
+ }
175
194
 
176
- function fire(type, args) {
177
- var event = $.Event(type, { relatedTarget: target })
195
+ function fire(type, args, props) {
196
+ if (!props) props = {}
197
+ props.relatedTarget = options.target
198
+ var event = $.Event(type, props)
178
199
  context.trigger(event, args)
179
200
  return !event.isDefaultPrevented()
180
201
  }
@@ -189,7 +210,7 @@ function pjax(options) {
189
210
  }
190
211
 
191
212
  xhr.setRequestHeader('X-PJAX', 'true')
192
- xhr.setRequestHeader('X-PJAX-Container', context.selector)
213
+ xhr.setRequestHeader('X-PJAX-Container', options.container)
193
214
 
194
215
  if (!fire('pjax:beforeSend', [xhr, settings]))
195
216
  return false
@@ -204,7 +225,9 @@ function pjax(options) {
204
225
  settings.timeout = 0
205
226
  }
206
227
 
207
- options.requestUrl = parseURL(settings.url).href
228
+ var url = parseURL(settings.url)
229
+ if (hash) url.hash = hash
230
+ options.requestUrl = stripInternalParams(url)
208
231
  }
209
232
 
210
233
  options.complete = function(xhr, textStatus) {
@@ -226,9 +249,11 @@ function pjax(options) {
226
249
  }
227
250
 
228
251
  options.success = function(data, status, xhr) {
252
+ var previousState = pjax.state
253
+
229
254
  // If $.pjax.defaults.version is a function, invoke it first.
230
255
  // Otherwise it can be a static string.
231
- var currentVersion = (typeof $.pjax.defaults.version === 'function') ?
256
+ var currentVersion = typeof $.pjax.defaults.version === 'function' ?
232
257
  $.pjax.defaults.version() :
233
258
  $.pjax.defaults.version
234
259
 
@@ -236,6 +261,12 @@ function pjax(options) {
236
261
 
237
262
  var container = extractContainer(data, xhr, options)
238
263
 
264
+ var url = parseURL(container.url)
265
+ if (hash) {
266
+ url.hash = hash
267
+ container.url = url.href
268
+ }
269
+
239
270
  // If there is a layout version mismatch, hard load the new url
240
271
  if (currentVersion && latestVersion && currentVersion !== latestVersion) {
241
272
  locationReplace(container.url)
@@ -252,7 +283,7 @@ function pjax(options) {
252
283
  id: options.id || uniqueId(),
253
284
  url: container.url,
254
285
  title: container.title,
255
- container: context.selector,
286
+ container: options.container,
256
287
  fragment: options.fragment,
257
288
  timeout: options.timeout
258
289
  }
@@ -261,10 +292,22 @@ function pjax(options) {
261
292
  window.history.replaceState(pjax.state, container.title, container.url)
262
293
  }
263
294
 
295
+ // Only blur the focus if the focused element is within the container.
296
+ var blurFocus = $.contains(context, document.activeElement)
297
+
264
298
  // Clear out any focused controls before inserting new page contents.
265
- document.activeElement.blur()
299
+ if (blurFocus) {
300
+ try {
301
+ document.activeElement.blur()
302
+ } catch (e) { /* ignore */ }
303
+ }
266
304
 
267
305
  if (container.title) document.title = container.title
306
+
307
+ fire('pjax:beforeReplace', [container.contents, options], {
308
+ state: pjax.state,
309
+ previousState: previousState
310
+ })
268
311
  context.html(container.contents)
269
312
 
270
313
  // FF bug: Won't autofocus fields that are inserted via JS.
@@ -274,33 +317,22 @@ function pjax(options) {
274
317
  // http://www.w3.org/html/wg/drafts/html/master/forms.html
275
318
  var autofocusEl = context.find('input[autofocus], textarea[autofocus]').last()[0]
276
319
  if (autofocusEl && document.activeElement !== autofocusEl) {
277
- autofocusEl.focus();
320
+ autofocusEl.focus()
278
321
  }
279
322
 
280
323
  executeScriptTags(container.scripts)
281
324
 
282
- // Scroll to top by default
283
- if (typeof options.scrollTo === 'number')
284
- $(window).scrollTop(options.scrollTo)
285
-
286
- // If the URL has a hash in it, make sure the browser
287
- // knows to navigate to the hash.
288
- if ( hash !== '' ) {
289
- // Avoid using simple hash set here. Will add another history
290
- // entry. Replace the url with replaceState and scroll to target
291
- // by hand.
292
- //
293
- // window.location.hash = hash
294
- var url = parseURL(container.url)
295
- url.hash = hash
296
-
297
- pjax.state.url = url.href
298
- window.history.replaceState(pjax.state, container.title, url.href)
325
+ var scrollTo = options.scrollTo
299
326
 
300
- var target = $(url.hash)
301
- if (target.length) $(window).scrollTop(target.offset().top)
327
+ // Ensure browser scrolls to the element referenced by the URL anchor
328
+ if (hash) {
329
+ var name = decodeURIComponent(hash.slice(1))
330
+ var target = document.getElementById(name) || document.getElementsByName(name)[0]
331
+ if (target) scrollTo = $(target).offset().top
302
332
  }
303
333
 
334
+ if (typeof scrollTo == 'number') $(window).scrollTop(scrollTo)
335
+
304
336
  fire('pjax:success', [data, status, xhr, options])
305
337
  }
306
338
 
@@ -314,7 +346,7 @@ function pjax(options) {
314
346
  id: uniqueId(),
315
347
  url: window.location.href,
316
348
  title: document.title,
317
- container: context.selector,
349
+ container: options.container,
318
350
  fragment: options.fragment,
319
351
  timeout: options.timeout
320
352
  }
@@ -322,11 +354,7 @@ function pjax(options) {
322
354
  }
323
355
 
324
356
  // Cancel the current request if we're already pjaxing
325
- var xhr = pjax.xhr
326
- if ( xhr && xhr.readyState < 4) {
327
- xhr.onreadystatechange = $.noop
328
- xhr.abort()
329
- }
357
+ abortXHR(pjax.xhr)
330
358
 
331
359
  pjax.options = options
332
360
  var xhr = pjax.xhr = $.ajax(options)
@@ -334,9 +362,9 @@ function pjax(options) {
334
362
  if (xhr.readyState > 0) {
335
363
  if (options.push && !options.replace) {
336
364
  // Cache current container element before replacing it
337
- cachePush(pjax.state.id, context.clone().contents())
365
+ cachePush(pjax.state.id, [options.container, cloneContents(context)])
338
366
 
339
- window.history.pushState(null, "", stripPjaxParam(options.requestUrl))
367
+ window.history.pushState(null, "", options.requestUrl)
340
368
  }
341
369
 
342
370
  fire('pjax:start', [xhr, options])
@@ -367,7 +395,7 @@ function pjaxReload(container, options) {
367
395
  //
368
396
  // Returns nothing.
369
397
  function locationReplace(url) {
370
- window.history.replaceState(null, "", "#")
398
+ window.history.replaceState(null, "", pjax.state.url)
371
399
  window.location.replace(url)
372
400
  }
373
401
 
@@ -393,7 +421,15 @@ if ('state' in window.history) {
393
421
  // You probably shouldn't use pjax on pages with other pushState
394
422
  // stuff yet.
395
423
  function onPjaxPopstate(event) {
424
+
425
+ // Hitting back or forward should override any pending PJAX request.
426
+ if (!initialPop) {
427
+ abortXHR(pjax.xhr)
428
+ }
429
+
430
+ var previousState = pjax.state
396
431
  var state = event.state
432
+ var direction
397
433
 
398
434
  if (state && state.container) {
399
435
  // When coming forward from a separate history session, will get an
@@ -401,22 +437,24 @@ function onPjaxPopstate(event) {
401
437
  // page.
402
438
  if (initialPop && initialURL == state.url) return
403
439
 
404
- // If popping back to the same state, just skip.
405
- // Could be clicking back from hashchange rather than a pushState.
406
- if (pjax.state.id === state.id) return
440
+ if (previousState) {
441
+ // If popping back to the same state, just skip.
442
+ // Could be clicking back from hashchange rather than a pushState.
443
+ if (previousState.id === state.id) return
407
444
 
408
- var container = $(state.container)
409
- if (container.length) {
410
- var direction, contents = cacheMapping[state.id]
445
+ // Since state IDs always increase, we can deduce the navigation direction
446
+ direction = previousState.id < state.id ? 'forward' : 'back'
447
+ }
411
448
 
412
- if (pjax.state) {
413
- // Since state ids always increase, we can deduce the history
414
- // direction from the previous state.
415
- direction = pjax.state.id < state.id ? 'forward' : 'back'
449
+ var cache = cacheMapping[state.id] || []
450
+ var containerSelector = cache[0] || state.container
451
+ var container = $(containerSelector), contents = cache[1]
416
452
 
453
+ if (container.length) {
454
+ if (previousState) {
417
455
  // Cache current container before replacement and inform the
418
456
  // cache which direction the history shifted.
419
- cachePop(direction, pjax.state.id, container.clone().contents())
457
+ cachePop(direction, previousState.id, [containerSelector, cloneContents(container)])
420
458
  }
421
459
 
422
460
  var popstateEvent = $.Event('pjax:popstate', {
@@ -428,7 +466,7 @@ function onPjaxPopstate(event) {
428
466
  var options = {
429
467
  id: state.id,
430
468
  url: state.url,
431
- container: container,
469
+ container: containerSelector,
432
470
  push: false,
433
471
  fragment: state.fragment,
434
472
  timeout: state.timeout,
@@ -438,9 +476,14 @@ function onPjaxPopstate(event) {
438
476
  if (contents) {
439
477
  container.trigger('pjax:start', [null, options])
440
478
 
479
+ pjax.state = state
441
480
  if (state.title) document.title = state.title
481
+ var beforeReplaceEvent = $.Event('pjax:beforeReplace', {
482
+ state: state,
483
+ previousState: previousState
484
+ })
485
+ container.trigger(beforeReplaceEvent, [contents, options])
442
486
  container.html(contents)
443
- pjax.state = state
444
487
 
445
488
  container.trigger('pjax:end', [null, options])
446
489
  } else {
@@ -449,7 +492,7 @@ function onPjaxPopstate(event) {
449
492
 
450
493
  // Force reflow/relayout before the browser tries to restore the
451
494
  // scroll position.
452
- container[0].offsetHeight
495
+ container[0].offsetHeight // eslint-disable-line no-unused-expressions
453
496
  } else {
454
497
  locationReplace(location.href)
455
498
  }
@@ -485,7 +528,12 @@ function fallbackPjax(options) {
485
528
  var pair = value.split('=')
486
529
  form.append($('<input>', {type: 'hidden', name: pair[0], value: pair[1]}))
487
530
  })
531
+ } else if ($.isArray(data)) {
532
+ $.each(data, function(index, value) {
533
+ form.append($('<input>', {type: 'hidden', name: value.name, value: value.value}))
534
+ })
488
535
  } else if (typeof data === 'object') {
536
+ var key
489
537
  for (key in data)
490
538
  form.append($('<input>', {type: 'hidden', name: key, value: data[key]}))
491
539
  }
@@ -494,6 +542,15 @@ function fallbackPjax(options) {
494
542
  form.submit()
495
543
  }
496
544
 
545
+ // Internal: Abort an XmlHttpRequest if it hasn't been completed,
546
+ // also removing its event handlers.
547
+ function abortXHR(xhr) {
548
+ if ( xhr && xhr.readyState < 4) {
549
+ xhr.onreadystatechange = $.noop
550
+ xhr.abort()
551
+ }
552
+ }
553
+
497
554
  // Internal: Generate unique id for state object.
498
555
  //
499
556
  // Use a timestamp instead of a counter since ids should still be
@@ -504,16 +561,22 @@ function uniqueId() {
504
561
  return (new Date).getTime()
505
562
  }
506
563
 
507
- // Internal: Strips _pjax param from url
508
- //
509
- // url - String
564
+ function cloneContents(container) {
565
+ var cloned = container.clone()
566
+ // Unmark script tags as already being eval'd so they can get executed again
567
+ // when restored from cache. HAXX: Uses jQuery internal method.
568
+ cloned.find('script').each(function(){
569
+ if (!this.src) $._data(this, 'globalEval', false)
570
+ })
571
+ return cloned.contents()
572
+ }
573
+
574
+ // Internal: Strip internal query params from parsed URL.
510
575
  //
511
- // Returns String.
512
- function stripPjaxParam(url) {
513
- return url
514
- .replace(/\?_pjax=[^&]+&?/, '?')
515
- .replace(/_pjax=[^&]+&?/, '')
516
- .replace(/[\?&]$/, '')
576
+ // Returns sanitized url.href String.
577
+ function stripInternalParams(url) {
578
+ url.search = url.search.replace(/([?&])(_pjax|_)=[^&]*/g, '').replace(/^&/, '')
579
+ return url.href.replace(/\?($|#)/, '$1')
517
580
  }
518
581
 
519
582
  // Internal: Parse URL components and returns a Locationish object.
@@ -527,6 +590,16 @@ function parseURL(url) {
527
590
  return a
528
591
  }
529
592
 
593
+ // Internal: Return the `href` component of given URL object with the hash
594
+ // portion removed.
595
+ //
596
+ // location - Location or HTMLAnchorElement
597
+ //
598
+ // Returns String
599
+ function stripHash(location) {
600
+ return location.href.replace(/#.*/, '')
601
+ }
602
+
530
603
  // Internal: Build options Object for arguments.
531
604
  //
532
605
  // For convenience the first parameter can be either the container or
@@ -545,44 +618,14 @@ function parseURL(url) {
545
618
  //
546
619
  // Returns options Object.
547
620
  function optionsFor(container, options) {
548
- // Both container and options
549
- if ( container && options )
621
+ if (container && options) {
622
+ options = $.extend({}, options)
550
623
  options.container = container
551
-
552
- // First argument is options Object
553
- else if ( $.isPlainObject(container) )
554
- options = container
555
-
556
- // Only container
557
- else
558
- options = {container: container}
559
-
560
- // Find and validate container
561
- if (options.container)
562
- options.container = findContainerFor(options.container)
563
-
564
- return options
565
- }
566
-
567
- // Internal: Find container element for a variety of inputs.
568
- //
569
- // Because we can't persist elements using the history API, we must be
570
- // able to find a String selector that will consistently find the Element.
571
- //
572
- // container - A selector String, jQuery object, or DOM Element.
573
- //
574
- // Returns a jQuery object whose context is `document` and has a selector.
575
- function findContainerFor(container) {
576
- container = $(container)
577
-
578
- if ( !container.length ) {
579
- throw "no pjax container for " + container.selector
580
- } else if ( container.selector !== '' && container.context === document ) {
624
+ return options
625
+ } else if ($.isPlainObject(container)) {
581
626
  return container
582
- } else if ( container.attr('id') ) {
583
- return $('#' + container.attr('id'))
584
627
  } else {
585
- throw "cant get selector for pjax container!"
628
+ return {container: container}
586
629
  }
587
630
  }
588
631
 
@@ -596,7 +639,7 @@ function findContainerFor(container) {
596
639
  //
597
640
  // Returns a jQuery object.
598
641
  function findAll(elems, selector) {
599
- return elems.filter(selector).add(elems.find(selector));
642
+ return elems.filter(selector).add(elems.find(selector))
600
643
  }
601
644
 
602
645
  function parseHTML(html) {
@@ -615,18 +658,21 @@ function parseHTML(html) {
615
658
  //
616
659
  // Returns an Object with url, title, and contents keys.
617
660
  function extractContainer(data, xhr, options) {
618
- var obj = {}
661
+ var obj = {}, fullDocument = /<html/i.test(data)
619
662
 
620
663
  // Prefer X-PJAX-URL header if it was set, otherwise fallback to
621
664
  // using the original requested url.
622
- obj.url = stripPjaxParam(xhr.getResponseHeader('X-PJAX-URL') || options.requestUrl)
665
+ var serverUrl = xhr.getResponseHeader('X-PJAX-URL')
666
+ obj.url = serverUrl ? stripInternalParams(parseURL(serverUrl)) : options.requestUrl
623
667
 
668
+ var $head, $body
624
669
  // Attempt to parse response html into elements
625
- if (/<html/i.test(data)) {
626
- var $head = $(parseHTML(data.match(/<head[^>]*>([\s\S.]*)<\/head>/i)[0]))
627
- var $body = $(parseHTML(data.match(/<body[^>]*>([\s\S.]*)<\/body>/i)[0]))
670
+ if (fullDocument) {
671
+ $body = $(parseHTML(data.match(/<body[^>]*>([\s\S.]*)<\/body>/i)[0]))
672
+ var head = data.match(/<head[^>]*>([\s\S.]*)<\/head>/i)
673
+ $head = head != null ? $(parseHTML(head[0])) : $body
628
674
  } else {
629
- var $head = $body = $(parseHTML(data))
675
+ $head = $body = $(parseHTML(data))
630
676
  }
631
677
 
632
678
  // If response data is empty, return fast
@@ -638,16 +684,15 @@ function extractContainer(data, xhr, options) {
638
684
  obj.title = findAll($head, 'title').last().text()
639
685
 
640
686
  if (options.fragment) {
687
+ var $fragment = $body
641
688
  // If they specified a fragment, look for it in the response
642
689
  // and pull it out.
643
- if (options.fragment === 'body') {
644
- var $fragment = $body
645
- } else {
646
- var $fragment = findAll($body, options.fragment).first()
690
+ if (options.fragment !== 'body') {
691
+ $fragment = findAll($fragment, options.fragment).first()
647
692
  }
648
693
 
649
694
  if ($fragment.length) {
650
- obj.contents = $fragment.contents()
695
+ obj.contents = options.fragment === 'body' ? $fragment : $fragment.contents()
651
696
 
652
697
  // If there's no title, look for data-title and title attributes
653
698
  // on the fragment
@@ -655,7 +700,7 @@ function extractContainer(data, xhr, options) {
655
700
  obj.title = $fragment.attr('title') || $fragment.data('title')
656
701
  }
657
702
 
658
- } else if (!/<html/i.test(data)) {
703
+ } else if (!fullDocument) {
659
704
  obj.contents = $body
660
705
  }
661
706
 
@@ -699,7 +744,8 @@ function executeScriptTags(scripts) {
699
744
  if (matchedScripts.length) return
700
745
 
701
746
  var script = document.createElement('script')
702
- script.type = $(this).attr('type')
747
+ var type = $(this).attr('type')
748
+ if (type) script.type = type
703
749
  script.src = $(this).attr('src')
704
750
  document.head.appendChild(script)
705
751
  })
@@ -722,14 +768,11 @@ function cachePush(id, value) {
722
768
  cacheMapping[id] = value
723
769
  cacheBackStack.push(id)
724
770
 
725
- // Remove all entires in forward history stack after pushing
726
- // a new page.
727
- while (cacheForwardStack.length)
728
- delete cacheMapping[cacheForwardStack.shift()]
771
+ // Remove all entries in forward history stack after pushing a new page.
772
+ trimCacheStack(cacheForwardStack, 0)
729
773
 
730
774
  // Trim back history stack to max cache length.
731
- while (cacheBackStack.length > pjax.defaults.maxCacheLength)
732
- delete cacheMapping[cacheBackStack.shift()]
775
+ trimCacheStack(cacheBackStack, pjax.defaults.maxCacheLength)
733
776
  }
734
777
 
735
778
  // Shifts cache from directional history cache. Should be
@@ -754,8 +797,23 @@ function cachePop(direction, id, value) {
754
797
  }
755
798
 
756
799
  pushStack.push(id)
757
- if (id = popStack.pop())
758
- delete cacheMapping[id]
800
+ id = popStack.pop()
801
+ if (id) delete cacheMapping[id]
802
+
803
+ // Trim whichever stack we just pushed to to max cache length.
804
+ trimCacheStack(pushStack, pjax.defaults.maxCacheLength)
805
+ }
806
+
807
+ // Trim a cache stack (either cacheBackStack or cacheForwardStack) to be no
808
+ // longer than the specified length, deleting cached DOM elements as necessary.
809
+ //
810
+ // stack - Array of state IDs
811
+ // length - Maximum length to trim to
812
+ //
813
+ // Returns nothing.
814
+ function trimCacheStack(stack, length) {
815
+ while (stack.length > length)
816
+ delete cacheMapping[stack.shift()]
759
817
  }
760
818
 
761
819
  // Public: Find version identifier for the initial page load.
@@ -824,15 +882,22 @@ function disable() {
824
882
 
825
883
  // Add the state property to jQuery's event object so we can use it in
826
884
  // $(window).bind('popstate')
827
- if ( $.inArray('state', $.event.props) < 0 )
885
+ if ($.event.props && $.inArray('state', $.event.props) < 0) {
828
886
  $.event.props.push('state')
887
+ } else if (!('state' in $.Event.prototype)) {
888
+ $.event.addProp('state')
889
+ }
829
890
 
830
891
  // Is pjax supported by this browser?
831
892
  $.support.pjax =
832
893
  window.history && window.history.pushState && window.history.replaceState &&
833
894
  // pushState isn't reliable on iOS until 5.
834
- !navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]|WebApps\/.+CFNetwork)/)
895
+ !navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]\D|WebApps\/.+CFNetwork)/)
835
896
 
836
- $.support.pjax ? enable() : disable()
897
+ if ($.support.pjax) {
898
+ enable()
899
+ } else {
900
+ disable()
901
+ }
837
902
 
838
- })(jQuery);
903
+ })(jQuery)
metadata CHANGED
@@ -1,103 +1,97 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pjax_rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson (PJAX by Chris Wanstrath)
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-12-12 00:00:00.000000000 Z
11
+ date: 2018-11-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - '>='
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '3.2'
20
- - - <
21
- - !ruby/object:Gem::Version
22
- version: '5.0'
23
20
  type: :runtime
24
21
  prerelease: false
25
22
  version_requirements: !ruby/object:Gem::Requirement
26
23
  requirements:
27
- - - '>='
24
+ - - ">="
28
25
  - !ruby/object:Gem::Version
29
26
  version: '3.2'
30
- - - <
31
- - !ruby/object:Gem::Version
32
- version: '5.0'
33
27
  - !ruby/object:Gem::Dependency
34
28
  name: jquery-rails
35
29
  requirement: !ruby/object:Gem::Requirement
36
30
  requirements:
37
- - - '>='
31
+ - - ">="
38
32
  - !ruby/object:Gem::Version
39
33
  version: '0'
40
34
  type: :runtime
41
35
  prerelease: false
42
36
  version_requirements: !ruby/object:Gem::Requirement
43
37
  requirements:
44
- - - '>='
38
+ - - ">="
45
39
  - !ruby/object:Gem::Version
46
40
  version: '0'
47
41
  - !ruby/object:Gem::Dependency
48
42
  name: rake
49
43
  requirement: !ruby/object:Gem::Requirement
50
44
  requirements:
51
- - - '>='
45
+ - - ">="
52
46
  - !ruby/object:Gem::Version
53
47
  version: '0'
54
48
  type: :development
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
51
  requirements:
58
- - - '>='
52
+ - - ">="
59
53
  - !ruby/object:Gem::Version
60
54
  version: '0'
61
55
  - !ruby/object:Gem::Dependency
62
56
  name: rails
63
57
  requirement: !ruby/object:Gem::Requirement
64
58
  requirements:
65
- - - '>='
59
+ - - ">="
66
60
  - !ruby/object:Gem::Version
67
61
  version: '0'
68
62
  type: :development
69
63
  prerelease: false
70
64
  version_requirements: !ruby/object:Gem::Requirement
71
65
  requirements:
72
- - - '>='
66
+ - - ">="
73
67
  - !ruby/object:Gem::Version
74
68
  version: '0'
75
69
  - !ruby/object:Gem::Dependency
76
70
  name: capybara
77
71
  requirement: !ruby/object:Gem::Requirement
78
72
  requirements:
79
- - - '>='
73
+ - - ">="
80
74
  - !ruby/object:Gem::Version
81
75
  version: '0'
82
76
  type: :development
83
77
  prerelease: false
84
78
  version_requirements: !ruby/object:Gem::Requirement
85
79
  requirements:
86
- - - '>='
80
+ - - ">="
87
81
  - !ruby/object:Gem::Version
88
82
  version: '0'
89
83
  - !ruby/object:Gem::Dependency
90
84
  name: poltergeist
91
85
  requirement: !ruby/object:Gem::Requirement
92
86
  requirements:
93
- - - '>='
87
+ - - ">="
94
88
  - !ruby/object:Gem::Version
95
89
  version: '0'
96
90
  type: :development
97
91
  prerelease: false
98
92
  version_requirements: !ruby/object:Gem::Requirement
99
93
  requirements:
100
- - - '>='
94
+ - - ">="
101
95
  - !ruby/object:Gem::Version
102
96
  version: '0'
103
97
  description:
@@ -109,8 +103,9 @@ files:
109
103
  - lib/pjax.rb
110
104
  - lib/pjax_rails.rb
111
105
  - vendor/assets/javascripts/jquery.pjax.js
112
- homepage:
113
- licenses: []
106
+ homepage: https://github.com/rails/pjax_rails
107
+ licenses:
108
+ - MIT
114
109
  metadata: {}
115
110
  post_install_message:
116
111
  rdoc_options: []
@@ -118,19 +113,18 @@ require_paths:
118
113
  - lib
119
114
  required_ruby_version: !ruby/object:Gem::Requirement
120
115
  requirements:
121
- - - '>='
116
+ - - ">="
122
117
  - !ruby/object:Gem::Version
123
118
  version: '0'
124
119
  required_rubygems_version: !ruby/object:Gem::Requirement
125
120
  requirements:
126
- - - '>='
121
+ - - ">="
127
122
  - !ruby/object:Gem::Version
128
123
  version: '0'
129
124
  requirements: []
130
125
  rubyforge_project:
131
- rubygems_version: 2.1.11
126
+ rubygems_version: 2.7.6
132
127
  signing_key:
133
128
  specification_version: 4
134
- summary: PJAX integration for Rails 3.1+
129
+ summary: PJAX integration for Rails 3.2+
135
130
  test_files: []
136
- has_rdoc: