pjax_rails 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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: