turbolinks 2.3.0 → 2.4.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
2
  SHA1:
3
- metadata.gz: 9287368c8706ded534c851a89ad71ea39b090a64
4
- data.tar.gz: ecea87430693e024274720d50e3eea02b43cfe1a
3
+ metadata.gz: 5fba03f6769899590a73f8d2f6f1e2b1756335cc
4
+ data.tar.gz: fcb4ee4559b15a8d3037ccd75636a8de9513365e
5
5
  SHA512:
6
- metadata.gz: dbe8bda0952f6f8f19fc8e0192a1c8305286c14393db2c3a2f5cde40839e1e390d8f301948da3fcbbbdf802960ba28ec6ed414a55f6aeb7dd54be250a4a20d31
7
- data.tar.gz: d0de51d589fc9d6553d4815f8a4e2593a3a7cc7a63e461f5c24f5765347d0e1153a592328f587e31ffa8c762840f7cc6fdd24ed186d0eb2642763211f9c86fd0
6
+ metadata.gz: 0cf63e984ac0cfed94831716ecfaf3db1be2045e83e07d9b8e4304ce6639135ddee36876b6c945fcec564730d33bb5f00e1c66d456218bc0db054568a2ef7790
7
+ data.tar.gz: 6733c1c8dc08e44e4271962e50ed4891ed96e0ba30a531c08025b001ecf700d0b4c5ef082891d62391d91c9a8f0bf81f7ce3e1838bf8774b582302289d42660c
data/README.md CHANGED
@@ -33,8 +33,9 @@ With Turbolinks pages will change without a full reload, so you can't rely on `D
33
33
  * `page:before-change` a Turbolinks-enabled link has been clicked *(see below for more details)*
34
34
  * `page:fetch` starting to fetch a new target page
35
35
  * `page:receive` the page has been fetched from the server, but not yet parsed
36
- * `page:change` the page has been parsed and changed to the new version and on DOMContentLoaded
37
- * `page:update` is triggered whenever page:change is PLUS on jQuery's ajaxSucess, if jQuery is available (otherwise you can manually trigger it when calling XMLHttpRequest in your own code)
36
+ * `page:before-unload` the page has been parsed and is about to be changed
37
+ * `page:change` the page has been changed to the new version (and on DOMContentLoaded)
38
+ * `page:update` is triggered alongside both page:change and jQuery's ajaxSuccess (if jQuery is available - otherwise you can manually trigger it when calling XMLHttpRequest in your own code)
38
39
  * `page:load` is fired at the end of the loading process.
39
40
 
40
41
  Handlers bound to the `page:before-change` event may return `false`, which will cancel the Turbolinks process.
@@ -42,6 +43,7 @@ Handlers bound to the `page:before-change` event may return `false`, which will
42
43
  By default, Turbolinks caches 10 of these page loads. It listens to the [popstate](https://developer.mozilla.org/en-US/docs/DOM/Manipulating_the_browser_history#The_popstate_event) event and attempts to restore page state from the cache when it's triggered. When `popstate` is fired the following process happens:
43
44
 
44
45
  ***Restore* a cached page from the client-side cache:**
46
+ * `page:before-unload` page has been fetched from the cache and is about to be changed
45
47
  * `page:change` page has changed to the cached page.
46
48
  * `page:restore` is fired at the end of restore process.
47
49
 
@@ -10,6 +10,16 @@ referer = null
10
10
  createDocument = null
11
11
  xhr = null
12
12
 
13
+ EVENTS =
14
+ BEFORE_CHANGE: 'page:before-change'
15
+ FETCH: 'page:fetch'
16
+ RECEIVE: 'page:receive'
17
+ CHANGE: 'page:change'
18
+ UPDATE: 'page:update'
19
+ LOAD: 'page:load'
20
+ RESTORE: 'page:restore'
21
+ BEFORE_UNLOAD: 'page:before-unload'
22
+ EXPIRE: 'page:expire'
13
23
 
14
24
  fetch = (url) ->
15
25
  url = new ComponentUrl url
@@ -30,8 +40,8 @@ transitionCacheFor = (url) ->
30
40
  enableTransitionCache = (enable = true) ->
31
41
  transitionCacheEnabled = enable
32
42
 
33
- fetchReplacement = (url, onLoadFunction = =>) ->
34
- triggerEvent 'page:fetch', url: url.absolute
43
+ fetchReplacement = (url, onLoadFunction = =>) ->
44
+ triggerEvent EVENTS.FETCH, url: url.absolute
35
45
 
36
46
  xhr?.abort()
37
47
  xhr = new XMLHttpRequest
@@ -40,7 +50,7 @@ fetchReplacement = (url, onLoadFunction = =>) ->
40
50
  xhr.setRequestHeader 'X-XHR-Referer', referer
41
51
 
42
52
  xhr.onload = ->
43
- triggerEvent 'page:receive', url: url.absolute
53
+ triggerEvent EVENTS.RECEIVE, url: url.absolute
44
54
 
45
55
  if doc = processResponse()
46
56
  reflectNewUrl url
@@ -48,7 +58,7 @@ fetchReplacement = (url, onLoadFunction = =>) ->
48
58
  manuallyTriggerHashChangeForFirefox()
49
59
  reflectRedirectedUrl()
50
60
  onLoadFunction()
51
- triggerEvent 'page:load'
61
+ triggerEvent EVENTS.LOAD
52
62
  else
53
63
  document.location.href = url.absolute
54
64
 
@@ -61,7 +71,7 @@ fetchHistory = (cachedPage) ->
61
71
  xhr?.abort()
62
72
  changePage cachedPage.title, cachedPage.body
63
73
  recallScrollPosition cachedPage
64
- triggerEvent 'page:restore'
74
+ triggerEvent EVENTS.RESTORE
65
75
 
66
76
 
67
77
  cacheCurrentPage = ->
@@ -89,24 +99,26 @@ constrainPageCacheTo = (limit) ->
89
99
  .sort (a, b) -> b - a
90
100
 
91
101
  for key in pageCacheKeys when pageCache[key].cachedAt <= cacheTimesRecentFirst[limit]
92
- triggerEvent 'page:expire', pageCache[key]
102
+ triggerEvent EVENTS.EXPIRE, pageCache[key]
93
103
  delete pageCache[key]
94
104
 
95
105
  changePage = (title, body, csrfToken, runScripts) ->
106
+ triggerEvent EVENTS.BEFORE_UNLOAD
96
107
  document.title = title
97
108
  document.documentElement.replaceChild body, document.body
98
109
  CSRFToken.update csrfToken if csrfToken?
99
110
  setAutofocusElement()
100
111
  executeScriptTags() if runScripts
101
112
  currentState = window.history.state
102
- triggerEvent 'page:change'
103
- triggerEvent 'page:update'
113
+ triggerEvent EVENTS.CHANGE
114
+ triggerEvent EVENTS.UPDATE
104
115
 
105
116
  executeScriptTags = ->
106
117
  scripts = Array::slice.call document.body.querySelectorAll 'script:not([data-turbolinks-eval="false"])'
107
118
  for script in scripts when script.type in ['', 'text/javascript']
108
119
  copy = document.createElement 'script'
109
120
  copy.setAttribute attr.name, attr.value for attr in script.attributes
121
+ copy.async = false unless script.hasAttribute 'async'
110
122
  copy.appendChild document.createTextNode script.innerHTML
111
123
  { parentNode, nextSibling } = script
112
124
  parentNode.removeChild script
@@ -164,29 +176,39 @@ resetScrollPosition = ->
164
176
  window.scrollTo 0, 0
165
177
 
166
178
 
179
+ clone = (original) ->
180
+ return original if not original? or typeof original isnt 'object'
181
+ copy = new original.constructor()
182
+ copy[key] = clone value for key, value of original
183
+ copy
184
+
167
185
  popCookie = (name) ->
168
186
  value = document.cookie.match(new RegExp(name+"=(\\w+)"))?[1].toUpperCase() or ''
169
187
  document.cookie = name + '=; expires=Thu, 01-Jan-70 00:00:01 GMT; path=/'
170
188
  value
171
189
 
172
190
  triggerEvent = (name, data) ->
191
+ if typeof Prototype isnt 'undefined'
192
+ Event.fire document, name, data, true
193
+
173
194
  event = document.createEvent 'Events'
174
195
  event.data = data if data
175
196
  event.initEvent name, true, true
176
197
  document.dispatchEvent event
177
198
 
178
- pageChangePrevented = ->
179
- !triggerEvent 'page:before-change'
199
+ pageChangePrevented = (url) ->
200
+ !triggerEvent EVENTS.BEFORE_CHANGE, url: url
180
201
 
181
202
  processResponse = ->
182
203
  clientOrServerError = ->
183
204
  400 <= xhr.status < 600
184
205
 
185
206
  validContent = ->
186
- xhr.getResponseHeader('Content-Type').match /^(?:text\/html|application\/xhtml\+xml|application\/xml)(?:;|$)/
207
+ (contentType = xhr.getResponseHeader('Content-Type'))? and
208
+ contentType.match /^(?:text\/html|application\/xhtml\+xml|application\/xml)(?:;|$)/
187
209
 
188
210
  extractTrackAssets = (doc) ->
189
- for node in doc.head.childNodes when node.getAttribute?('data-turbolinks-track')?
211
+ for node in doc.querySelector('head').childNodes when node.getAttribute?('data-turbolinks-track')?
190
212
  node.getAttribute('src') or node.getAttribute('href')
191
213
 
192
214
  assetsChanged = (doc) ->
@@ -205,7 +227,7 @@ processResponse = ->
205
227
 
206
228
  extractTitleAndBody = (doc) ->
207
229
  title = doc.querySelector 'title'
208
- [ title?.textContent, removeNoscriptTags(doc.body), CSRFToken.get(doc).token, 'runScripts' ]
230
+ [ title?.textContent, removeNoscriptTags(doc.querySelector('body')), CSRFToken.get(doc).token, 'runScripts' ]
209
231
 
210
232
  CSRFToken =
211
233
  get: (doc = document) ->
@@ -233,6 +255,15 @@ browserCompatibleDocumentParser = ->
233
255
  doc.close()
234
256
  doc
235
257
 
258
+ createDocumentUsingFragment = (html) ->
259
+ head = html.match(/<head[^>]*>([\s\S.]*)<\/head>/i)?[0] or '<head></head>'
260
+ body = html.match(/<body[^>]*>([\s\S.]*)<\/body>/i)?[0] or '<body></body>'
261
+ htmlWrapper = document.createElement 'html'
262
+ htmlWrapper.innerHTML = head + body
263
+ doc = document.createDocumentFragment()
264
+ doc.appendChild htmlWrapper
265
+ doc
266
+
236
267
  # Use createDocumentUsingParser if DOMParser is defined and natively
237
268
  # supports 'text/html' parsing (Firefox 12+, IE 10)
238
269
  #
@@ -243,16 +274,32 @@ browserCompatibleDocumentParser = ->
243
274
  # - DOMParser isn't defined
244
275
  # - createDocumentUsingParser returns null due to unsupported type 'text/html' (Chrome, Safari)
245
276
  # - createDocumentUsingDOM doesn't create a valid HTML document (safeguarding against potential edge cases)
277
+ #
278
+ # Use createDocumentUsingFragment if the previously selected parser does not
279
+ # correctly parse <form> tags. (Safari 7.1+ - see github.com/rails/turbolinks/issues/408)
280
+ buildTestsUsing = (createMethod) ->
281
+ buildTest = (fallback, passes) ->
282
+ passes: passes()
283
+ fallback: fallback
284
+
285
+ structureTest = buildTest createDocumentUsingWrite, =>
286
+ (createMethod '<html><body><p>test')?.body?.childNodes.length is 1
287
+
288
+ formNestingTest = buildTest createDocumentUsingFragment, =>
289
+ (createMethod '<html><body><form></form><div></div></body></html>')?.body?.childNodes.length is 2
290
+
291
+ [structureTest, formNestingTest]
292
+
246
293
  try
247
294
  if window.DOMParser
248
- testDoc = createDocumentUsingParser '<html><body><p>test'
295
+ docTests = buildTestsUsing createDocumentUsingParser
249
296
  createDocumentUsingParser
250
297
  catch e
251
- testDoc = createDocumentUsingDOM '<html><body><p>test'
298
+ docTests = buildTestsUsing createDocumentUsingDOM
252
299
  createDocumentUsingDOM
253
300
  finally
254
- unless testDoc?.body?.childNodes.length is 1
255
- return createDocumentUsingWrite
301
+ for docTest in docTests
302
+ return docTest.fallback unless docTest.passes
256
303
 
257
304
 
258
305
  # The ComponentUrl class converts a basic URL string into an object
@@ -265,7 +312,7 @@ class ComponentUrl
265
312
  return @original if @original.constructor is ComponentUrl
266
313
  @_parse()
267
314
 
268
- withoutHash: -> @href.replace @hash, ''
315
+ withoutHash: -> @href.replace(@hash, '').replace('#', '')
269
316
 
270
317
  # Intention revealing function alias
271
318
  withoutHashForIE10compatibility: -> @withoutHash()
@@ -293,6 +340,8 @@ class Link extends ComponentUrl
293
340
  constructor: (@link) ->
294
341
  return @link if @link.constructor is Link
295
342
  @original = @link.href
343
+ @originalElement = @link
344
+ @link = @link.cloneNode false
296
345
  super
297
346
 
298
347
  shouldIgnore: ->
@@ -306,14 +355,14 @@ class Link extends ComponentUrl
306
355
  @origin isnt (new ComponentUrl).origin
307
356
 
308
357
  _anchored: ->
309
- ((@hash and @withoutHash()) is (current = new ComponentUrl).withoutHash()) or
310
- (@href is current.href + '#')
358
+ (@hash.length > 0 or @href.charAt(@href.length - 1) is '#') and
359
+ (@withoutHash() is (new ComponentUrl).withoutHash())
311
360
 
312
361
  _nonHtml: ->
313
362
  @pathname.match(/\.[a-z]+$/g) and not @pathname.match(new RegExp("\\.(?:#{Link.HTML_EXTENSIONS.join('|')})?$", 'g'))
314
363
 
315
364
  _optOut: ->
316
- link = @link
365
+ link = @originalElement
317
366
  until ignore or link is document
318
367
  ignore = link.getAttribute('data-no-turbolink')?
319
368
  link = link.parentNode
@@ -340,7 +389,7 @@ class Click
340
389
  return if @event.defaultPrevented
341
390
  @_extractLink()
342
391
  if @_validForTurbolinks()
343
- visit @link.href unless pageChangePrevented()
392
+ visit @link.href unless pageChangePrevented(@link.absolute)
344
393
  @event.preventDefault()
345
394
 
346
395
  _extractLink: ->
@@ -366,15 +415,15 @@ bypassOnLoadPopstate = (fn) ->
366
415
 
367
416
  installDocumentReadyPageEventTriggers = ->
368
417
  document.addEventListener 'DOMContentLoaded', ( ->
369
- triggerEvent 'page:change'
370
- triggerEvent 'page:update'
418
+ triggerEvent EVENTS.CHANGE
419
+ triggerEvent EVENTS.UPDATE
371
420
  ), true
372
421
 
373
422
  installJqueryAjaxSuccessPageUpdateTrigger = ->
374
423
  if typeof jQuery isnt 'undefined'
375
424
  jQuery(document).on 'ajaxSuccess', (event, xhr, settings) ->
376
425
  return unless jQuery.trim xhr.responseText
377
- triggerEvent 'page:update'
426
+ triggerEvent EVENTS.UPDATE
378
427
 
379
428
  installHistoryChangeHandler = (event) ->
380
429
  if event.state?.turbolinks
@@ -433,4 +482,12 @@ else
433
482
  # Turbolinks.enableTransitionCache()
434
483
  # Turbolinks.allowLinkExtensions('md')
435
484
  # Turbolinks.supported
436
- @Turbolinks = { visit, pagesCached, enableTransitionCache, allowLinkExtensions: Link.allowExtensions, supported: browserSupportsTurbolinks }
485
+ # Turbolinks.EVENTS
486
+ @Turbolinks = {
487
+ visit,
488
+ pagesCached,
489
+ enableTransitionCache,
490
+ allowLinkExtensions: Link.allowExtensions,
491
+ supported: browserSupportsTurbolinks,
492
+ EVENTS: clone(EVENTS)
493
+ }
@@ -1,10 +1,15 @@
1
1
  module Turbolinks
2
- # Sets a request_method cookie containing the request method of the current request.
3
- # The Turbolinks script will not initialize if this cookie is set to anything other than GET.
2
+ # For non-GET requests, sets a request_method cookie containing
3
+ # the request method of the current request. The Turbolinks script
4
+ # will not initialize if this cookie is set.
4
5
  module Cookies
5
6
  private
6
7
  def set_request_method_cookie
7
- cookies[:request_method] = request.request_method
8
+ if request.get?
9
+ cookies.delete(:request_method)
10
+ else
11
+ cookies[:request_method] = request.request_method
12
+ end
8
13
  end
9
14
  end
10
- end
15
+ end
@@ -1,3 +1,3 @@
1
1
  module Turbolinks
2
- VERSION = '2.3.0'
2
+ VERSION = '2.4.0'
3
3
  end
@@ -25,12 +25,12 @@ module Turbolinks
25
25
 
26
26
  private
27
27
  def store_for_turbolinks(url)
28
- session[:_turbolinks_redirect_to] = url if request.headers["X-XHR-Referer"]
28
+ session[:_turbolinks_redirect_to] = url if session && request.headers["X-XHR-Referer"]
29
29
  url
30
30
  end
31
31
 
32
32
  def set_xhr_redirected_to
33
- if session[:_turbolinks_redirect_to]
33
+ if session && session[:_turbolinks_redirect_to]
34
34
  response.headers['X-XHR-Redirected-To'] = session.delete :_turbolinks_redirect_to
35
35
  end
36
36
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: turbolinks
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.0
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-08-21 00:00:00.000000000 Z
11
+ date: 2014-10-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: coffee-rails