turbolinks 2.2.1 → 2.2.2

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: 4b7c786dc5fe799560978873952c05876224aa42
4
- data.tar.gz: f79c665b9dadef0b27cb3d252c9773f90b57e6c7
3
+ metadata.gz: a29652f5af8636c6c8e56062fcaee11ec2997453
4
+ data.tar.gz: c90a5773bee34e8d043876e5a3b4f4520bda207c
5
5
  SHA512:
6
- metadata.gz: 652de423718540abd5069b448a58c6dde9c7c2e92e2ef01c1e48b3356ecee0e2cd706db3084a42489ac88415e3861b72304bc939f7d775891fe3be0e356664c7
7
- data.tar.gz: 39eb0d8d11d6e73b69da4a191dbf99143f4b2b3c4ff0f730684536982808010899fe0cd1e22599e015c28d14899a446b54d8d5cf16420645870040104a552163
6
+ metadata.gz: 6d2be7089afe4cc2c492090bd411cc325900df7515875f9917ab76ce29316eb2f8a358e39381d04aa73fdc3a4eb542ea261dd2126cab16bcd66d844b02ac4207
7
+ data.tar.gz: 2dff851cfb527fdc0e6b3bb262dfb0baaea02f602b0f170da6cd020d9c45412c9346e877230d42af72b2754d11125034c61ba8721faa5a7127a10163d2ed12a4
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright 2012 David Heinemeier Hansson
1
+ Copyright 2012-2014 David Heinemeier Hansson
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -197,6 +197,7 @@ Language Ports
197
197
  * [Flask Turbolinks](https://github.com/lepture/flask-turbolinks) (Python Flask)
198
198
  * [ASP.NET MVC Turbolinks](https://github.com/kazimanzurrashid/aspnetmvcturbolinks)
199
199
  * [PHP Turbolinks Component](https://github.com/helthe/Turbolinks) (Symfony Component)
200
+ * [PHP Turbolinks Package](https://github.com/frenzyapp/turbolinks) (Laravel Package)
200
201
 
201
202
  Credits
202
203
  -------
@@ -4,7 +4,6 @@ transitionCacheEnabled = false
4
4
 
5
5
  currentState = null
6
6
  loadedAssets = null
7
- htmlExtensions = ['html']
8
7
 
9
8
  referer = null
10
9
 
@@ -13,11 +12,13 @@ xhr = null
13
12
 
14
13
 
15
14
  fetch = (url) ->
15
+ url = new ComponentUrl url
16
+
16
17
  rememberReferer()
17
18
  cacheCurrentPage()
18
19
  reflectNewUrl url
19
20
 
20
- if transitionCacheEnabled and cachedPage = transitionCacheFor(url)
21
+ if transitionCacheEnabled and cachedPage = transitionCacheFor(url.absolute)
21
22
  fetchHistory cachedPage
22
23
  fetchReplacement url
23
24
  else
@@ -31,11 +32,11 @@ enableTransitionCache = (enable = true) ->
31
32
  transitionCacheEnabled = enable
32
33
 
33
34
  fetchReplacement = (url, onLoadFunction = =>) ->
34
- triggerEvent 'page:fetch', url: url
35
+ triggerEvent 'page:fetch', url: url.absolute
35
36
 
36
37
  xhr?.abort()
37
38
  xhr = new XMLHttpRequest
38
- xhr.open 'GET', removeHashForIE10compatiblity(url), true
39
+ xhr.open 'GET', url.withoutHashForIE10compatibility(), true
39
40
  xhr.setRequestHeader 'Accept', 'text/html, application/xhtml+xml, application/xml'
40
41
  xhr.setRequestHeader 'X-XHR-Referer', referer
41
42
 
@@ -48,10 +49,10 @@ fetchReplacement = (url, onLoadFunction = =>) ->
48
49
  onLoadFunction()
49
50
  triggerEvent 'page:load'
50
51
  else
51
- document.location.href = url
52
+ document.location.href = url.absolute
52
53
 
53
54
  xhr.onloadend = -> xhr = null
54
- xhr.onerror = -> document.location.href = url
55
+ xhr.onerror = -> document.location.href = url.absolute
55
56
 
56
57
  xhr.send()
57
58
 
@@ -63,8 +64,10 @@ fetchHistory = (cachedPage) ->
63
64
 
64
65
 
65
66
  cacheCurrentPage = ->
66
- pageCache[currentState.url] =
67
- url: document.location.href,
67
+ currentStateUrl = new ComponentUrl currentState.url
68
+
69
+ pageCache[currentStateUrl.absolute] =
70
+ url: currentStateUrl.relative,
68
71
  body: document.body,
69
72
  title: document.title,
70
73
  positionY: window.pageYOffset,
@@ -113,13 +116,14 @@ removeNoscriptTags = (node) ->
113
116
  node
114
117
 
115
118
  reflectNewUrl = (url) ->
116
- if url isnt referer
117
- window.history.pushState { turbolinks: true, url: url }, '', url
119
+ if (url = new ComponentUrl url).absolute isnt referer
120
+ window.history.pushState { turbolinks: true, url: url.absolute }, '', url.absolute
118
121
 
119
122
  reflectRedirectedUrl = ->
120
123
  if location = xhr.getResponseHeader 'X-XHR-Redirected-To'
121
- preservedHash = if removeHash(location) is location then document.location.hash else ''
122
- window.history.replaceState currentState, '', location + preservedHash
124
+ location = new ComponentUrl location
125
+ preservedHash = if location.hasNoHash() then document.location.hash else ''
126
+ window.history.replaceState currentState, '', location.href + preservedHash
123
127
 
124
128
  rememberReferer = ->
125
129
  referer = document.location.href
@@ -140,17 +144,6 @@ resetScrollPosition = ->
140
144
  window.scrollTo 0, 0
141
145
 
142
146
 
143
- # Intention revealing function alias
144
- removeHashForIE10compatiblity = (url) ->
145
- removeHash url
146
-
147
- removeHash = (url) ->
148
- link = url
149
- unless url.href?
150
- link = document.createElement 'A'
151
- link.href = url
152
- link.href.replace link.hash, ''
153
-
154
147
  popCookie = (name) ->
155
148
  value = document.cookie.match(new RegExp(name+"=(\\w+)"))?[1].toUpperCase() or ''
156
149
  document.cookie = name + '=; expires=Thu, 01-Jan-70 00:00:01 GMT; path=/'
@@ -242,53 +235,108 @@ browserCompatibleDocumentParser = ->
242
235
  return createDocumentUsingWrite
243
236
 
244
237
 
245
- installClickHandlerLast = (event) ->
246
- unless event.defaultPrevented
247
- document.removeEventListener 'click', handleClick, false
248
- document.addEventListener 'click', handleClick, false
249
-
250
- handleClick = (event) ->
251
- unless event.defaultPrevented
252
- link = extractLink event
253
- if link.nodeName is 'A' and !ignoreClick(event, link)
254
- visit link.href unless pageChangePrevented()
255
- event.preventDefault()
256
-
257
-
258
- extractLink = (event) ->
259
- link = event.target
260
- link = link.parentNode until !link.parentNode or link.nodeName is 'A'
261
- link
262
-
263
- crossOriginLink = (link) ->
264
- location.protocol isnt link.protocol or location.host isnt link.host
265
-
266
- anchoredLink = (link) ->
267
- ((link.hash and removeHash(link)) is removeHash(location)) or
268
- (link.href is location.href + '#')
269
-
270
- nonHtmlLink = (link) ->
271
- url = removeHash link
272
- url.match(/\.[a-z]+(\?.*)?$/g) and not url.match(new RegExp("\\.(?:#{htmlExtensions.join('|')})?(\\?.*)?$", 'g'))
273
-
274
- noTurbolink = (link) ->
275
- until ignore or link is document
276
- ignore = link.getAttribute('data-no-turbolink')?
277
- link = link.parentNode
278
- ignore
279
-
280
- targetLink = (link) ->
281
- link.target.length isnt 0
282
-
283
- nonStandardClick = (event) ->
284
- event.which > 1 or event.metaKey or event.ctrlKey or event.shiftKey or event.altKey
285
-
286
- ignoreClick = (event, link) ->
287
- crossOriginLink(link) or anchoredLink(link) or nonHtmlLink(link) or noTurbolink(link) or targetLink(link) or nonStandardClick(event)
288
-
289
- allowLinkExtensions = (extensions...) ->
290
- htmlExtensions.push extension for extension in extensions
291
- htmlExtensions
238
+ # The ComponentUrl class converts a basic URL string into an object
239
+ # that behaves similarly to document.location.
240
+ #
241
+ # If an instance is created from a relative URL, the current document
242
+ # is used to fill in the missing attributes (protocol, host, port).
243
+ class ComponentUrl
244
+ constructor: (@original = document.location.href) ->
245
+ return @original if @original.constructor is ComponentUrl
246
+ @_parse()
247
+
248
+ withoutHash: -> @href.replace @hash, ''
249
+
250
+ # Intention revealing function alias
251
+ withoutHashForIE10compatibility: -> @withoutHash()
252
+
253
+ hasNoHash: -> @hash.length is 0
254
+
255
+ _parse: ->
256
+ (@link ?= document.createElement 'a').href = @original
257
+ { @href, @protocol, @host, @hostname, @port, @pathname, @search, @hash } = @link
258
+ @origin = [@protocol, '//', @hostname].join ''
259
+ @origin += ":#{@port}" unless @port.length is 0
260
+ @relative = [@pathname, @search, @hash].join ''
261
+ @absolute = @href
262
+
263
+ # The Link class derives from the ComponentUrl class, but is built from an
264
+ # existing link element. Provides verification functionality for Turbolinks
265
+ # to use in determining whether it should process the link when clicked.
266
+ class Link extends ComponentUrl
267
+ @HTML_EXTENSIONS: ['html']
268
+
269
+ @allowExtensions: (extensions...) ->
270
+ Link.HTML_EXTENSIONS.push extension for extension in extensions
271
+ Link.HTML_EXTENSIONS
272
+
273
+ constructor: (@link) ->
274
+ return @link if @link.constructor is Link
275
+ @original = @link.href
276
+ super
277
+
278
+ shouldIgnore: ->
279
+ @_crossOrigin() or
280
+ @_anchored() or
281
+ @_nonHtml() or
282
+ @_optOut() or
283
+ @_target()
284
+
285
+ _crossOrigin: ->
286
+ @origin isnt (new ComponentUrl).origin
287
+
288
+ _anchored: ->
289
+ ((@hash and @withoutHash()) is (current = new ComponentUrl).withoutHash()) or
290
+ (@href is current.href + '#')
291
+
292
+ _nonHtml: ->
293
+ @pathname.match(/\.[a-z]+$/g) and not @pathname.match(new RegExp("\\.(?:#{Link.HTML_EXTENSIONS.join('|')})?$", 'g'))
294
+
295
+ _optOut: ->
296
+ link = @link
297
+ until ignore or link is document
298
+ ignore = link.getAttribute('data-no-turbolink')?
299
+ link = link.parentNode
300
+ ignore
301
+
302
+ _target: ->
303
+ @link.target.length isnt 0
304
+
305
+
306
+ # The Click class handles clicked links, verifying if Turbolinks should
307
+ # take control by inspecting both the event and the link. If it should,
308
+ # the page change process is initiated. If not, control is passed back
309
+ # to the browser for default functionality.
310
+ class Click
311
+ @installHandlerLast: (event) ->
312
+ unless event.defaultPrevented
313
+ document.removeEventListener 'click', Click.handle, false
314
+ document.addEventListener 'click', Click.handle, false
315
+
316
+ @handle: (event) ->
317
+ new Click event
318
+
319
+ constructor: (@event) ->
320
+ return if @event.defaultPrevented
321
+ @_extractLink()
322
+ if @_validForTurbolinks()
323
+ visit @link.href unless pageChangePrevented()
324
+ @event.preventDefault()
325
+
326
+ _extractLink: ->
327
+ link = @event.target
328
+ link = link.parentNode until !link.parentNode or link.nodeName is 'A'
329
+ @link = new Link(link) if link.nodeName is 'A' and link.href.length isnt 0
330
+
331
+ _validForTurbolinks: ->
332
+ @link? and not (@link.shouldIgnore() or @_nonStandardClick())
333
+
334
+ _nonStandardClick: ->
335
+ @event.which > 1 or
336
+ @event.metaKey or
337
+ @event.ctrlKey or
338
+ @event.shiftKey or
339
+ @event.altKey
292
340
 
293
341
 
294
342
  # Delay execution of function long enough to miss the popstate event
@@ -310,7 +358,7 @@ installJqueryAjaxSuccessPageUpdateTrigger = ->
310
358
 
311
359
  installHistoryChangeHandler = (event) ->
312
360
  if event.state?.turbolinks
313
- if cachedPage = pageCache[event.state.url]
361
+ if cachedPage = pageCache[(new ComponentUrl(event.state.url)).absolute]
314
362
  cacheCurrentPage()
315
363
  fetchHistory cachedPage
316
364
  else
@@ -321,7 +369,7 @@ initializeTurbolinks = ->
321
369
  rememberCurrentState()
322
370
  createDocument = browserCompatibleDocumentParser()
323
371
 
324
- document.addEventListener 'click', installClickHandlerLast, true
372
+ document.addEventListener 'click', Click.installHandlerLast, true
325
373
 
326
374
  bypassOnLoadPopstate ->
327
375
  window.addEventListener 'popstate', installHistoryChangeHandler, false
@@ -361,4 +409,4 @@ else
361
409
  # Turbolinks.enableTransitionCache()
362
410
  # Turbolinks.allowLinkExtensions('md')
363
411
  # Turbolinks.supported
364
- @Turbolinks = { visit, pagesCached, enableTransitionCache, allowLinkExtensions, supported: browserSupportsTurbolinks }
412
+ @Turbolinks = { visit, pagesCached, enableTransitionCache, allowLinkExtensions: Link.allowExtensions, supported: browserSupportsTurbolinks }
data/lib/turbolinks.rb CHANGED
@@ -8,21 +8,25 @@ require 'turbolinks/redirection'
8
8
  module Turbolinks
9
9
  class Engine < ::Rails::Engine
10
10
  initializer :turbolinks do |config|
11
- ActionController::Base.class_eval do
12
- include XHRHeaders, Cookies, XDomainBlocker, Redirection
13
- before_filter :set_xhr_redirected_to, :set_request_method_cookie
14
- after_filter :abort_xdomain_redirect
15
- end
11
+ ActiveSupport.on_load(:action_controller) do
12
+ ActionController::Base.class_eval do
13
+ include XHRHeaders, Cookies, XDomainBlocker, Redirection
14
+ before_filter :set_xhr_redirected_to, :set_request_method_cookie
15
+ after_filter :abort_xdomain_redirect
16
+ end
16
17
 
17
- ActionDispatch::Request.class_eval do
18
- def referer
19
- self.headers['X-XHR-Referer'] || super
18
+ ActionDispatch::Request.class_eval do
19
+ def referer
20
+ self.headers['X-XHR-Referer'] || super
21
+ end
22
+ alias referrer referer
20
23
  end
21
- alias referrer referer
22
24
  end
23
25
 
24
- (ActionView::RoutingUrlFor rescue ActionView::Helpers::UrlHelper).module_eval do
25
- include XHRUrlFor
26
+ ActiveSupport.on_load(:action_view) do
27
+ (ActionView::RoutingUrlFor rescue ActionView::Helpers::UrlHelper).module_eval do
28
+ include XHRUrlFor
29
+ end
26
30
  end
27
31
  end
28
32
  end
@@ -1,3 +1,3 @@
1
1
  module Turbolinks
2
- VERSION = '2.2.1'
2
+ VERSION = '2.2.2'
3
3
  end
@@ -7,14 +7,9 @@ module Turbolinks
7
7
  base.alias_method_chain :url_for, :xhr_referer
8
8
  end
9
9
 
10
- def url_for_with_xhr_referer(options)
11
- options = (xhr_referer || options) if options == :back
10
+ def url_for_with_xhr_referer(options = {})
11
+ options = (controller.request.headers["X-XHR-Referer"] || options) if options == :back
12
12
  url_for_without_xhr_referer options
13
13
  end
14
-
15
- private
16
- def xhr_referer
17
- controller.request.headers["X-XHR-Referer"]
18
- end
19
14
  end
20
- end
15
+ end
data/test/config.ru CHANGED
@@ -15,6 +15,10 @@ map "/500" do
15
15
  # throw Internal Server Error (500)
16
16
  end
17
17
 
18
+ map "/withoutextension" do
19
+ run Rack::File.new(File.join(Root, "test", "withoutextension"), "Content-Type" => "text/html")
20
+ end
21
+
18
22
  map "/" do
19
23
  run Rack::Directory.new(File.join(Root, "test"))
20
24
  end
data/test/index.html CHANGED
@@ -22,6 +22,8 @@
22
22
  <ul style="margin-top:200px;">
23
23
  <li><a href="/other.html">Other page</a></li>
24
24
  <li><a href="/other.html"><span>Wrapped link</span></a></li>
25
+ <li><a href="/withoutextension">Without extension</a></li>
26
+ <li><a href="/withoutextension?sort=user.name">Without extension with query params</a></li>
25
27
  <li><a href="http://www.google.com/">Cross origin</a></li>
26
28
  <li><a href="/other.html" onclick="if(!confirm('follow link?')) { return false}">Confirm Fire Order</a></li>
27
29
  <li><a href="/reload.html"><span>New assets track </span></a></li>
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title>Home</title>
6
+ <script type="text/javascript" src="/js/turbolinks.js"></script>
7
+ <script type="text/javascript">
8
+ document.addEventListener("page:change", function() {
9
+ console.log("page changed");
10
+ });
11
+
12
+ document.addEventListener("page:update", function() {
13
+ console.log("page updated");
14
+ });
15
+
16
+ document.addEventListener("page:restore", function() {
17
+ console.log("page restored");
18
+ });
19
+ </script>
20
+ </head>
21
+ <body class="page-other">
22
+ <ul>
23
+ <li><a href="/index.html">Home</a></li>
24
+ </ul>
25
+ </body>
26
+ </html>
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.2.1
4
+ version: 2.2.2
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-01-31 00:00:00.000000000 Z
11
+ date: 2014-04-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: coffee-rails
@@ -47,6 +47,7 @@ files:
47
47
  - test/offline.html
48
48
  - test/other.html
49
49
  - test/reload.html
50
+ - test/withoutextension
50
51
  homepage: https://github.com/rails/turbolinks/
51
52
  licenses:
52
53
  - MIT