turbolinks 2.5.4 → 5.0.0.beta1
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 +4 -4
- data/{MIT-LICENSE → LICENSE} +1 -1
- data/README.md +0 -241
- data/lib/turbolinks.rb +16 -31
- data/lib/turbolinks/redirection.rb +40 -7
- data/lib/turbolinks/version.rb +1 -1
- metadata +9 -25
- data/lib/assets/javascripts/turbolinks.js.coffee +0 -558
- data/lib/turbolinks/cookies.rb +0 -15
- data/lib/turbolinks/x_domain_blocker.rb +0 -21
- data/lib/turbolinks/xhr_headers.rb +0 -46
- data/lib/turbolinks/xhr_url_for.rb +0 -22
- data/test/config.ru +0 -55
- data/test/dummy.gif +0 -0
- data/test/index.html +0 -51
- data/test/manifest.appcache +0 -10
- data/test/offline.html +0 -19
- data/test/other.html +0 -26
- data/test/redirect1.html +0 -15
- data/test/redirect2.html +0 -11
- data/test/reload.html +0 -18
- data/test/withoutextension +0 -26
@@ -1,558 +0,0 @@
|
|
1
|
-
pageCache = {}
|
2
|
-
cacheSize = 10
|
3
|
-
transitionCacheEnabled = false
|
4
|
-
progressBar = null
|
5
|
-
|
6
|
-
currentState = null
|
7
|
-
loadedAssets = null
|
8
|
-
|
9
|
-
referer = null
|
10
|
-
|
11
|
-
xhr = null
|
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'
|
23
|
-
|
24
|
-
fetch = (url) ->
|
25
|
-
url = new ComponentUrl url
|
26
|
-
|
27
|
-
rememberReferer()
|
28
|
-
cacheCurrentPage()
|
29
|
-
progressBar?.start()
|
30
|
-
|
31
|
-
if transitionCacheEnabled and cachedPage = transitionCacheFor(url.absolute)
|
32
|
-
fetchHistory cachedPage
|
33
|
-
fetchReplacement url, null, false
|
34
|
-
else
|
35
|
-
fetchReplacement url, resetScrollPosition
|
36
|
-
|
37
|
-
transitionCacheFor = (url) ->
|
38
|
-
cachedPage = pageCache[url]
|
39
|
-
cachedPage if cachedPage and !cachedPage.transitionCacheDisabled
|
40
|
-
|
41
|
-
enableTransitionCache = (enable = true) ->
|
42
|
-
transitionCacheEnabled = enable
|
43
|
-
|
44
|
-
enableProgressBar = (enable = true) ->
|
45
|
-
return unless browserSupportsTurbolinks
|
46
|
-
if enable
|
47
|
-
progressBar ?= new ProgressBar 'html'
|
48
|
-
else
|
49
|
-
progressBar?.uninstall()
|
50
|
-
progressBar = null
|
51
|
-
|
52
|
-
fetchReplacement = (url, onLoadFunction, showProgressBar = true) ->
|
53
|
-
triggerEvent EVENTS.FETCH, url: url.absolute
|
54
|
-
|
55
|
-
xhr?.abort()
|
56
|
-
xhr = new XMLHttpRequest
|
57
|
-
xhr.open 'GET', url.withoutHashForIE10compatibility(), true
|
58
|
-
xhr.setRequestHeader 'Accept', 'text/html, application/xhtml+xml, application/xml'
|
59
|
-
xhr.setRequestHeader 'X-XHR-Referer', referer
|
60
|
-
|
61
|
-
xhr.onload = ->
|
62
|
-
triggerEvent EVENTS.RECEIVE, url: url.absolute
|
63
|
-
|
64
|
-
if doc = processResponse()
|
65
|
-
reflectNewUrl url
|
66
|
-
reflectRedirectedUrl()
|
67
|
-
changePage extractTitleAndBody(doc)...
|
68
|
-
manuallyTriggerHashChangeForFirefox()
|
69
|
-
onLoadFunction?()
|
70
|
-
triggerEvent EVENTS.LOAD
|
71
|
-
else
|
72
|
-
document.location.href = crossOriginRedirect() or url.absolute
|
73
|
-
|
74
|
-
if progressBar and showProgressBar
|
75
|
-
xhr.onprogress = (event) =>
|
76
|
-
percent = if event.lengthComputable
|
77
|
-
event.loaded / event.total * 100
|
78
|
-
else
|
79
|
-
progressBar.value + (100 - progressBar.value) / 10
|
80
|
-
progressBar.advanceTo(percent)
|
81
|
-
|
82
|
-
xhr.onloadend = -> xhr = null
|
83
|
-
xhr.onerror = -> document.location.href = url.absolute
|
84
|
-
|
85
|
-
xhr.send()
|
86
|
-
|
87
|
-
fetchHistory = (cachedPage) ->
|
88
|
-
xhr?.abort()
|
89
|
-
changePage cachedPage.title, cachedPage.body
|
90
|
-
recallScrollPosition cachedPage
|
91
|
-
triggerEvent EVENTS.RESTORE
|
92
|
-
|
93
|
-
|
94
|
-
cacheCurrentPage = ->
|
95
|
-
currentStateUrl = new ComponentUrl currentState.url
|
96
|
-
|
97
|
-
pageCache[currentStateUrl.absolute] =
|
98
|
-
url: currentStateUrl.relative,
|
99
|
-
body: document.body,
|
100
|
-
title: document.title,
|
101
|
-
positionY: window.pageYOffset,
|
102
|
-
positionX: window.pageXOffset,
|
103
|
-
cachedAt: new Date().getTime(),
|
104
|
-
transitionCacheDisabled: document.querySelector('[data-no-transition-cache]')?
|
105
|
-
|
106
|
-
constrainPageCacheTo cacheSize
|
107
|
-
|
108
|
-
pagesCached = (size = cacheSize) ->
|
109
|
-
cacheSize = parseInt(size) if /^[\d]+$/.test size
|
110
|
-
|
111
|
-
constrainPageCacheTo = (limit) ->
|
112
|
-
pageCacheKeys = Object.keys pageCache
|
113
|
-
|
114
|
-
cacheTimesRecentFirst = pageCacheKeys.map (url) ->
|
115
|
-
pageCache[url].cachedAt
|
116
|
-
.sort (a, b) -> b - a
|
117
|
-
|
118
|
-
for key in pageCacheKeys when pageCache[key].cachedAt <= cacheTimesRecentFirst[limit]
|
119
|
-
triggerEvent EVENTS.EXPIRE, pageCache[key]
|
120
|
-
delete pageCache[key]
|
121
|
-
|
122
|
-
changePage = (title, body, csrfToken, runScripts) ->
|
123
|
-
triggerEvent EVENTS.BEFORE_UNLOAD
|
124
|
-
document.title = title
|
125
|
-
document.documentElement.replaceChild body, document.body
|
126
|
-
CSRFToken.update csrfToken if csrfToken?
|
127
|
-
setAutofocusElement()
|
128
|
-
executeScriptTags() if runScripts
|
129
|
-
currentState = window.history.state
|
130
|
-
progressBar?.done()
|
131
|
-
triggerEvent EVENTS.CHANGE
|
132
|
-
triggerEvent EVENTS.UPDATE
|
133
|
-
|
134
|
-
executeScriptTags = ->
|
135
|
-
scripts = Array::slice.call document.body.querySelectorAll 'script:not([data-turbolinks-eval="false"])'
|
136
|
-
for script in scripts when script.type in ['', 'text/javascript']
|
137
|
-
copy = document.createElement 'script'
|
138
|
-
copy.setAttribute attr.name, attr.value for attr in script.attributes
|
139
|
-
copy.async = false unless script.hasAttribute 'async'
|
140
|
-
copy.appendChild document.createTextNode script.innerHTML
|
141
|
-
{ parentNode, nextSibling } = script
|
142
|
-
parentNode.removeChild script
|
143
|
-
parentNode.insertBefore copy, nextSibling
|
144
|
-
return
|
145
|
-
|
146
|
-
removeNoscriptTags = (node) ->
|
147
|
-
node.innerHTML = node.innerHTML.replace /<noscript[\S\s]*?<\/noscript>/ig, ''
|
148
|
-
node
|
149
|
-
|
150
|
-
# Firefox bug: Doesn't autofocus fields that are inserted via JavaScript
|
151
|
-
setAutofocusElement = ->
|
152
|
-
autofocusElement = (list = document.querySelectorAll 'input[autofocus], textarea[autofocus]')[list.length - 1]
|
153
|
-
if autofocusElement and document.activeElement isnt autofocusElement
|
154
|
-
autofocusElement.focus()
|
155
|
-
|
156
|
-
reflectNewUrl = (url) ->
|
157
|
-
if (url = new ComponentUrl url).absolute isnt referer
|
158
|
-
window.history.pushState { turbolinks: true, url: url.absolute }, '', url.absolute
|
159
|
-
|
160
|
-
reflectRedirectedUrl = ->
|
161
|
-
if location = xhr.getResponseHeader 'X-XHR-Redirected-To'
|
162
|
-
location = new ComponentUrl location
|
163
|
-
preservedHash = if location.hasNoHash() then document.location.hash else ''
|
164
|
-
window.history.replaceState window.history.state, '', location.href + preservedHash
|
165
|
-
|
166
|
-
crossOriginRedirect = ->
|
167
|
-
redirect if (redirect = xhr.getResponseHeader('Location'))? and (new ComponentUrl(redirect)).crossOrigin()
|
168
|
-
|
169
|
-
rememberReferer = ->
|
170
|
-
referer = document.location.href
|
171
|
-
|
172
|
-
rememberCurrentUrl = ->
|
173
|
-
window.history.replaceState { turbolinks: true, url: document.location.href }, '', document.location.href
|
174
|
-
|
175
|
-
rememberCurrentState = ->
|
176
|
-
currentState = window.history.state
|
177
|
-
|
178
|
-
# Unlike other browsers, Firefox doesn't trigger hashchange after changing the
|
179
|
-
# location (via pushState) to an anchor on a different page. For example:
|
180
|
-
#
|
181
|
-
# /pages/one => /pages/two#with-hash
|
182
|
-
#
|
183
|
-
# By forcing Firefox to trigger hashchange, the rest of the code can rely on more
|
184
|
-
# consistent behavior across browsers.
|
185
|
-
manuallyTriggerHashChangeForFirefox = ->
|
186
|
-
if navigator.userAgent.match(/Firefox/) and !(url = (new ComponentUrl)).hasNoHash()
|
187
|
-
window.history.replaceState currentState, '', url.withoutHash()
|
188
|
-
document.location.hash = url.hash
|
189
|
-
|
190
|
-
recallScrollPosition = (page) ->
|
191
|
-
window.scrollTo page.positionX, page.positionY
|
192
|
-
|
193
|
-
resetScrollPosition = ->
|
194
|
-
if document.location.hash
|
195
|
-
document.location.href = document.location.href
|
196
|
-
else
|
197
|
-
window.scrollTo 0, 0
|
198
|
-
|
199
|
-
|
200
|
-
clone = (original) ->
|
201
|
-
return original if not original? or typeof original isnt 'object'
|
202
|
-
copy = new original.constructor()
|
203
|
-
copy[key] = clone value for key, value of original
|
204
|
-
copy
|
205
|
-
|
206
|
-
popCookie = (name) ->
|
207
|
-
value = document.cookie.match(new RegExp(name+"=(\\w+)"))?[1].toUpperCase() or ''
|
208
|
-
document.cookie = name + '=; expires=Thu, 01-Jan-70 00:00:01 GMT; path=/'
|
209
|
-
value
|
210
|
-
|
211
|
-
triggerEvent = (name, data) ->
|
212
|
-
if typeof Prototype isnt 'undefined'
|
213
|
-
Event.fire document, name, data, true
|
214
|
-
|
215
|
-
event = document.createEvent 'Events'
|
216
|
-
event.data = data if data
|
217
|
-
event.initEvent name, true, true
|
218
|
-
document.dispatchEvent event
|
219
|
-
|
220
|
-
pageChangePrevented = (url) ->
|
221
|
-
!triggerEvent EVENTS.BEFORE_CHANGE, url: url
|
222
|
-
|
223
|
-
processResponse = ->
|
224
|
-
clientOrServerError = ->
|
225
|
-
400 <= xhr.status < 600
|
226
|
-
|
227
|
-
validContent = ->
|
228
|
-
(contentType = xhr.getResponseHeader('Content-Type'))? and
|
229
|
-
contentType.match /^(?:text\/html|application\/xhtml\+xml|application\/xml)(?:;|$)/
|
230
|
-
|
231
|
-
extractTrackAssets = (doc) ->
|
232
|
-
for node in doc.querySelector('head').childNodes when node.getAttribute?('data-turbolinks-track')?
|
233
|
-
node.getAttribute('src') or node.getAttribute('href')
|
234
|
-
|
235
|
-
assetsChanged = (doc) ->
|
236
|
-
loadedAssets ||= extractTrackAssets document
|
237
|
-
fetchedAssets = extractTrackAssets doc
|
238
|
-
fetchedAssets.length isnt loadedAssets.length or intersection(fetchedAssets, loadedAssets).length isnt loadedAssets.length
|
239
|
-
|
240
|
-
intersection = (a, b) ->
|
241
|
-
[a, b] = [b, a] if a.length > b.length
|
242
|
-
value for value in a when value in b
|
243
|
-
|
244
|
-
if not clientOrServerError() and validContent()
|
245
|
-
doc = createDocument xhr.responseText
|
246
|
-
if doc and !assetsChanged doc
|
247
|
-
return doc
|
248
|
-
|
249
|
-
extractTitleAndBody = (doc) ->
|
250
|
-
title = doc.querySelector 'title'
|
251
|
-
[ title?.textContent, removeNoscriptTags(doc.querySelector('body')), CSRFToken.get(doc).token, 'runScripts' ]
|
252
|
-
|
253
|
-
CSRFToken =
|
254
|
-
get: (doc = document) ->
|
255
|
-
node: tag = doc.querySelector 'meta[name="csrf-token"]'
|
256
|
-
token: tag?.getAttribute? 'content'
|
257
|
-
|
258
|
-
update: (latest) ->
|
259
|
-
current = @get()
|
260
|
-
if current.token? and latest? and current.token isnt latest
|
261
|
-
current.node.setAttribute 'content', latest
|
262
|
-
|
263
|
-
createDocument = (html) ->
|
264
|
-
doc = document.documentElement.cloneNode()
|
265
|
-
doc.innerHTML = html
|
266
|
-
doc.head = doc.querySelector 'head'
|
267
|
-
doc.body = doc.querySelector 'body'
|
268
|
-
doc
|
269
|
-
|
270
|
-
# The ComponentUrl class converts a basic URL string into an object
|
271
|
-
# that behaves similarly to document.location.
|
272
|
-
#
|
273
|
-
# If an instance is created from a relative URL, the current document
|
274
|
-
# is used to fill in the missing attributes (protocol, host, port).
|
275
|
-
class ComponentUrl
|
276
|
-
constructor: (@original = document.location.href) ->
|
277
|
-
return @original if @original.constructor is ComponentUrl
|
278
|
-
@_parse()
|
279
|
-
|
280
|
-
withoutHash: -> @href.replace(@hash, '').replace('#', '')
|
281
|
-
|
282
|
-
# Intention revealing function alias
|
283
|
-
withoutHashForIE10compatibility: -> @withoutHash()
|
284
|
-
|
285
|
-
hasNoHash: -> @hash.length is 0
|
286
|
-
|
287
|
-
crossOrigin: ->
|
288
|
-
@origin isnt (new ComponentUrl).origin
|
289
|
-
|
290
|
-
_parse: ->
|
291
|
-
(@link ?= document.createElement 'a').href = @original
|
292
|
-
{ @href, @protocol, @host, @hostname, @port, @pathname, @search, @hash } = @link
|
293
|
-
@origin = [@protocol, '//', @hostname].join ''
|
294
|
-
@origin += ":#{@port}" unless @port.length is 0
|
295
|
-
@relative = [@pathname, @search, @hash].join ''
|
296
|
-
@absolute = @href
|
297
|
-
|
298
|
-
# The Link class derives from the ComponentUrl class, but is built from an
|
299
|
-
# existing link element. Provides verification functionality for Turbolinks
|
300
|
-
# to use in determining whether it should process the link when clicked.
|
301
|
-
class Link extends ComponentUrl
|
302
|
-
@HTML_EXTENSIONS: ['html']
|
303
|
-
|
304
|
-
@allowExtensions: (extensions...) ->
|
305
|
-
Link.HTML_EXTENSIONS.push extension for extension in extensions
|
306
|
-
Link.HTML_EXTENSIONS
|
307
|
-
|
308
|
-
constructor: (@link) ->
|
309
|
-
return @link if @link.constructor is Link
|
310
|
-
@original = @link.href
|
311
|
-
@originalElement = @link
|
312
|
-
@link = @link.cloneNode false
|
313
|
-
super
|
314
|
-
|
315
|
-
shouldIgnore: ->
|
316
|
-
@crossOrigin() or
|
317
|
-
@_anchored() or
|
318
|
-
@_nonHtml() or
|
319
|
-
@_optOut() or
|
320
|
-
@_target()
|
321
|
-
|
322
|
-
_anchored: ->
|
323
|
-
(@hash.length > 0 or @href.charAt(@href.length - 1) is '#') and
|
324
|
-
(@withoutHash() is (new ComponentUrl).withoutHash())
|
325
|
-
|
326
|
-
_nonHtml: ->
|
327
|
-
@pathname.match(/\.[a-z]+$/g) and not @pathname.match(new RegExp("\\.(?:#{Link.HTML_EXTENSIONS.join('|')})?$", 'g'))
|
328
|
-
|
329
|
-
_optOut: ->
|
330
|
-
link = @originalElement
|
331
|
-
until ignore or link is document
|
332
|
-
ignore = link.getAttribute('data-no-turbolink')?
|
333
|
-
link = link.parentNode
|
334
|
-
ignore
|
335
|
-
|
336
|
-
_target: ->
|
337
|
-
@link.target.length isnt 0
|
338
|
-
|
339
|
-
|
340
|
-
# The Click class handles clicked links, verifying if Turbolinks should
|
341
|
-
# take control by inspecting both the event and the link. If it should,
|
342
|
-
# the page change process is initiated. If not, control is passed back
|
343
|
-
# to the browser for default functionality.
|
344
|
-
class Click
|
345
|
-
@installHandlerLast: (event) ->
|
346
|
-
unless event.defaultPrevented
|
347
|
-
document.removeEventListener 'click', Click.handle, false
|
348
|
-
document.addEventListener 'click', Click.handle, false
|
349
|
-
|
350
|
-
@handle: (event) ->
|
351
|
-
new Click event
|
352
|
-
|
353
|
-
constructor: (@event) ->
|
354
|
-
return if @event.defaultPrevented
|
355
|
-
@_extractLink()
|
356
|
-
if @_validForTurbolinks()
|
357
|
-
visit @link.href unless pageChangePrevented(@link.absolute)
|
358
|
-
@event.preventDefault()
|
359
|
-
|
360
|
-
_extractLink: ->
|
361
|
-
link = @event.target
|
362
|
-
link = link.parentNode until !link.parentNode or link.nodeName is 'A'
|
363
|
-
@link = new Link(link) if link.nodeName is 'A' and link.href.length isnt 0
|
364
|
-
|
365
|
-
_validForTurbolinks: ->
|
366
|
-
@link? and not (@link.shouldIgnore() or @_nonStandardClick())
|
367
|
-
|
368
|
-
_nonStandardClick: ->
|
369
|
-
@event.which > 1 or
|
370
|
-
@event.metaKey or
|
371
|
-
@event.ctrlKey or
|
372
|
-
@event.shiftKey or
|
373
|
-
@event.altKey
|
374
|
-
|
375
|
-
|
376
|
-
class ProgressBar
|
377
|
-
className = 'turbolinks-progress-bar'
|
378
|
-
|
379
|
-
constructor: (@elementSelector) ->
|
380
|
-
@value = 0
|
381
|
-
@content = ''
|
382
|
-
@speed = 300
|
383
|
-
# Setting the opacity to a value < 1 fixes a display issue in Safari 6 and
|
384
|
-
# iOS 6 where the progress bar would fill the entire page.
|
385
|
-
@opacity = 0.99
|
386
|
-
@install()
|
387
|
-
|
388
|
-
install: ->
|
389
|
-
@element = document.querySelector(@elementSelector)
|
390
|
-
@element.classList.add(className)
|
391
|
-
@styleElement = document.createElement('style')
|
392
|
-
document.head.appendChild(@styleElement)
|
393
|
-
@_updateStyle()
|
394
|
-
|
395
|
-
uninstall: ->
|
396
|
-
@element.classList.remove(className)
|
397
|
-
document.head.removeChild(@styleElement)
|
398
|
-
|
399
|
-
start: ->
|
400
|
-
@advanceTo(5)
|
401
|
-
|
402
|
-
advanceTo: (value) ->
|
403
|
-
if value > @value <= 100
|
404
|
-
@value = value
|
405
|
-
@_updateStyle()
|
406
|
-
|
407
|
-
if @value is 100
|
408
|
-
@_stopTrickle()
|
409
|
-
else if @value > 0
|
410
|
-
@_startTrickle()
|
411
|
-
|
412
|
-
done: ->
|
413
|
-
if @value > 0
|
414
|
-
@advanceTo(100)
|
415
|
-
@_reset()
|
416
|
-
|
417
|
-
_reset: ->
|
418
|
-
originalOpacity = @opacity
|
419
|
-
|
420
|
-
setTimeout =>
|
421
|
-
@opacity = 0
|
422
|
-
@_updateStyle()
|
423
|
-
, @speed / 2
|
424
|
-
|
425
|
-
setTimeout =>
|
426
|
-
@value = 0
|
427
|
-
@opacity = originalOpacity
|
428
|
-
@_withSpeed(0, => @_updateStyle(true))
|
429
|
-
, @speed
|
430
|
-
|
431
|
-
_startTrickle: ->
|
432
|
-
return if @trickling
|
433
|
-
@trickling = true
|
434
|
-
setTimeout(@_trickle, @speed)
|
435
|
-
|
436
|
-
_stopTrickle: ->
|
437
|
-
delete @trickling
|
438
|
-
|
439
|
-
_trickle: =>
|
440
|
-
return unless @trickling
|
441
|
-
@advanceTo(@value + Math.random() / 2)
|
442
|
-
setTimeout(@_trickle, @speed)
|
443
|
-
|
444
|
-
_withSpeed: (speed, fn) ->
|
445
|
-
originalSpeed = @speed
|
446
|
-
@speed = speed
|
447
|
-
result = fn()
|
448
|
-
@speed = originalSpeed
|
449
|
-
result
|
450
|
-
|
451
|
-
_updateStyle: (forceRepaint = false) ->
|
452
|
-
@_changeContentToForceRepaint() if forceRepaint
|
453
|
-
@styleElement.textContent = @_createCSSRule()
|
454
|
-
|
455
|
-
_changeContentToForceRepaint: ->
|
456
|
-
@content = if @content is '' then ' ' else ''
|
457
|
-
|
458
|
-
_createCSSRule: ->
|
459
|
-
"""
|
460
|
-
#{@elementSelector}.#{className}::before {
|
461
|
-
content: '#{@content}';
|
462
|
-
position: fixed;
|
463
|
-
top: 0;
|
464
|
-
left: 0;
|
465
|
-
z-index: 2000;
|
466
|
-
background-color: #0076ff;
|
467
|
-
height: 3px;
|
468
|
-
opacity: #{@opacity};
|
469
|
-
width: #{@value}%;
|
470
|
-
transition: width #{@speed}ms ease-out, opacity #{@speed / 2}ms ease-in;
|
471
|
-
transform: translate3d(0,0,0);
|
472
|
-
}
|
473
|
-
"""
|
474
|
-
|
475
|
-
|
476
|
-
# Delay execution of function long enough to miss the popstate event
|
477
|
-
# some browsers fire on the initial page load.
|
478
|
-
bypassOnLoadPopstate = (fn) ->
|
479
|
-
setTimeout fn, 500
|
480
|
-
|
481
|
-
installDocumentReadyPageEventTriggers = ->
|
482
|
-
document.addEventListener 'DOMContentLoaded', ( ->
|
483
|
-
triggerEvent EVENTS.CHANGE
|
484
|
-
triggerEvent EVENTS.UPDATE
|
485
|
-
), true
|
486
|
-
|
487
|
-
installJqueryAjaxSuccessPageUpdateTrigger = ->
|
488
|
-
if typeof jQuery isnt 'undefined'
|
489
|
-
jQuery(document).on 'ajaxSuccess', (event, xhr, settings) ->
|
490
|
-
return unless jQuery.trim xhr.responseText
|
491
|
-
triggerEvent EVENTS.UPDATE
|
492
|
-
|
493
|
-
installHistoryChangeHandler = (event) ->
|
494
|
-
if event.state?.turbolinks
|
495
|
-
if cachedPage = pageCache[(new ComponentUrl(event.state.url)).absolute]
|
496
|
-
cacheCurrentPage()
|
497
|
-
fetchHistory cachedPage
|
498
|
-
else
|
499
|
-
visit event.target.location.href
|
500
|
-
|
501
|
-
initializeTurbolinks = ->
|
502
|
-
rememberCurrentUrl()
|
503
|
-
rememberCurrentState()
|
504
|
-
|
505
|
-
document.addEventListener 'click', Click.installHandlerLast, true
|
506
|
-
|
507
|
-
window.addEventListener 'hashchange', (event) ->
|
508
|
-
rememberCurrentUrl()
|
509
|
-
rememberCurrentState()
|
510
|
-
, false
|
511
|
-
bypassOnLoadPopstate ->
|
512
|
-
window.addEventListener 'popstate', installHistoryChangeHandler, false
|
513
|
-
|
514
|
-
# Handle bug in Firefox 26/27 where history.state is initially undefined
|
515
|
-
historyStateIsDefined =
|
516
|
-
window.history.state != undefined or navigator.userAgent.match /Firefox\/2[6|7]/
|
517
|
-
|
518
|
-
browserSupportsPushState =
|
519
|
-
window.history and window.history.pushState and window.history.replaceState and historyStateIsDefined
|
520
|
-
|
521
|
-
browserIsntBuggy =
|
522
|
-
!navigator.userAgent.match /CriOS\//
|
523
|
-
|
524
|
-
requestMethodIsSafe =
|
525
|
-
popCookie('request_method') in ['GET','']
|
526
|
-
|
527
|
-
browserSupportsTurbolinks = browserSupportsPushState and browserIsntBuggy and requestMethodIsSafe
|
528
|
-
|
529
|
-
browserSupportsCustomEvents =
|
530
|
-
document.addEventListener and document.createEvent
|
531
|
-
|
532
|
-
if browserSupportsCustomEvents
|
533
|
-
installDocumentReadyPageEventTriggers()
|
534
|
-
installJqueryAjaxSuccessPageUpdateTrigger()
|
535
|
-
|
536
|
-
if browserSupportsTurbolinks
|
537
|
-
visit = fetch
|
538
|
-
initializeTurbolinks()
|
539
|
-
else
|
540
|
-
visit = (url) -> document.location.href = url
|
541
|
-
|
542
|
-
# Public API
|
543
|
-
# Turbolinks.visit(url)
|
544
|
-
# Turbolinks.pagesCached()
|
545
|
-
# Turbolinks.pagesCached(20)
|
546
|
-
# Turbolinks.enableTransitionCache()
|
547
|
-
# Turbolinks.allowLinkExtensions('md')
|
548
|
-
# Turbolinks.supported
|
549
|
-
# Turbolinks.EVENTS
|
550
|
-
@Turbolinks = {
|
551
|
-
visit,
|
552
|
-
pagesCached,
|
553
|
-
enableTransitionCache,
|
554
|
-
enableProgressBar,
|
555
|
-
allowLinkExtensions: Link.allowExtensions,
|
556
|
-
supported: browserSupportsTurbolinks,
|
557
|
-
EVENTS: clone(EVENTS)
|
558
|
-
}
|