angular-turbolinks 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +20 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +47 -0
- data/Rakefile +1 -0
- data/angular-turbolinks.gemspec +25 -0
- data/lib/angular/turbolinks.rb +8 -0
- data/lib/angular/turbolinks/version.rb +5 -0
- data/lib/assets/javascripts/angular-turbolinks.js.coffee +193 -0
- metadata +109 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 154e3cc700a4bc97f9bf09d61400d4c72a10a050
|
4
|
+
data.tar.gz: c544bdd9905690010f65bd0216b29a637801672a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 79ea116909e48bc963193b8de2faf5e5bee3bca335d301be57ae335af52f201d945c467497419d0eee39f725c9747de439cc6454df6711218fa0745ca0b0e1ca
|
7
|
+
data.tar.gz: f62564cb01d3f3b2203c478b4d9dd29c199f6e11ba5a991e58e87bff82c702792933fb94bcb50c8cc84dfc4a87cb7ed0711a1ff32b9a413dee15f9ca0512e26e
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Cary Dunn
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# Angular::Turbolinks
|
2
|
+
|
3
|
+
##### Add angular-turbolinks and turbolinks to your Gemfile
|
4
|
+
```sh
|
5
|
+
gem "turbolinks"
|
6
|
+
gem "angular-turbolinks"
|
7
|
+
```
|
8
|
+
|
9
|
+
##### Add angular-turbolinks to your sprockets
|
10
|
+
```sh
|
11
|
+
//= require angular-turbolinks
|
12
|
+
```
|
13
|
+
|
14
|
+
##### Add 'ngTurbolinks' and 'ngRoute' to your angular.module
|
15
|
+
```sh
|
16
|
+
var app = angular.module('...', [..., 'ngRoute', 'ngTurbolinks'])
|
17
|
+
```
|
18
|
+
|
19
|
+
##### Make sure HTML5 mode is enabled...
|
20
|
+
```sh
|
21
|
+
app.config([
|
22
|
+
"$locationProvider", function($locationProvider) {
|
23
|
+
$locationProvider.html5Mode(true);
|
24
|
+
}
|
25
|
+
]);
|
26
|
+
```
|
27
|
+
|
28
|
+
##### Move angular bootstrapping to turbolinks event
|
29
|
+
```sh
|
30
|
+
$(document).on('ready page:load', ->
|
31
|
+
angular.bootstrap($("body"), ['app'])
|
32
|
+
).on('page:before-change', ->
|
33
|
+
angular.element("body").scope().$broadcast("$destroy")
|
34
|
+
)
|
35
|
+
```
|
36
|
+
|
37
|
+
##### TODO
|
38
|
+
* add support for turbolinks redirection logic
|
39
|
+
|
40
|
+
##### Caveats
|
41
|
+
* This is a first stab just to try to get around the '10 $digest() iterations reached using $locationWatch' errors I was receiving when using turbolinks with angular
|
42
|
+
* https://github.com/angular/angular.js/issues/3915
|
43
|
+
* https://github.com/angular/angular.js/issues/2815 (among others)
|
44
|
+
* none of the suggested fixes worked for me and this was happening on chrome
|
45
|
+
* This approach uses the angular $location/$locationProvider services for click tracking and pushState, steals the $locationChangeStart event and runs the changed url through turbolinks methods
|
46
|
+
* Does not support any of the turbolinks caching
|
47
|
+
* Eventually im hoping angular $locationWatch can play nice with external plugins using pushState...
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'angular/turbolinks/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "angular-turbolinks"
|
8
|
+
spec.version = Angular::Turbolinks::VERSION
|
9
|
+
spec.authors = ["Cary Dunn"]
|
10
|
+
spec.email = ["cary.dunn@gmail.com"]
|
11
|
+
spec.description = %q{Rails Turbolinks + Angular}
|
12
|
+
spec.summary = %q{Rails Turbolinks + Angular}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_dependency 'coffee-rails'
|
24
|
+
spec.add_dependency 'turbolinks'
|
25
|
+
end
|
@@ -0,0 +1,193 @@
|
|
1
|
+
angular.module('ngTurbolinks', []).run(($location, $rootScope, $http, $q)->
|
2
|
+
|
3
|
+
loadedAssets = null
|
4
|
+
createDocument = null
|
5
|
+
xhr_req = null
|
6
|
+
referer = null
|
7
|
+
|
8
|
+
triggerEvent = (name, data) ->
|
9
|
+
event = document.createEvent 'Events'
|
10
|
+
event.data = data if data
|
11
|
+
event.initEvent name, true, true
|
12
|
+
document.dispatchEvent event
|
13
|
+
|
14
|
+
popCookie = (name) ->
|
15
|
+
value = document.cookie.match(new RegExp(name+"=(\\w+)"))?[1].toUpperCase() or ''
|
16
|
+
document.cookie = name + '=; expires=Thu, 01-Jan-70 00:00:01 GMT; path=/'
|
17
|
+
value
|
18
|
+
|
19
|
+
rememberReferer = ->
|
20
|
+
referer = document.location.href
|
21
|
+
|
22
|
+
processResponse = (responseText, status, headers)->
|
23
|
+
clientOrServerError = ->
|
24
|
+
400 <= status < 600
|
25
|
+
|
26
|
+
validContent = ->
|
27
|
+
headers()["content-type"].match /^(?:text\/html|application\/xhtml\+xml|application\/xml)(?:;|$)/
|
28
|
+
|
29
|
+
extractTrackAssets = (doc) ->
|
30
|
+
for node in doc.head.childNodes when node.getAttribute?('data-turbolinks-track')?
|
31
|
+
node.getAttribute('src') or node.getAttribute('href')
|
32
|
+
|
33
|
+
assetsChanged = (doc) ->
|
34
|
+
loadedAssets ||= extractTrackAssets document
|
35
|
+
fetchedAssets = extractTrackAssets doc
|
36
|
+
fetchedAssets.length isnt loadedAssets.length or intersection(fetchedAssets, loadedAssets).length isnt loadedAssets.length
|
37
|
+
|
38
|
+
intersection = (a, b) ->
|
39
|
+
[a, b] = [b, a] if a.length > b.length
|
40
|
+
value for value in a when value in b
|
41
|
+
|
42
|
+
if not clientOrServerError() and validContent()
|
43
|
+
doc = createDocument responseText
|
44
|
+
if doc and !assetsChanged doc
|
45
|
+
return doc
|
46
|
+
|
47
|
+
browserCompatibleDocumentParser = ->
|
48
|
+
createDocumentUsingParser = (html) ->
|
49
|
+
(new DOMParser).parseFromString html, 'text/html'
|
50
|
+
|
51
|
+
createDocumentUsingDOM = (html) ->
|
52
|
+
doc = document.implementation.createHTMLDocument ''
|
53
|
+
doc.documentElement.innerHTML = html
|
54
|
+
doc
|
55
|
+
|
56
|
+
createDocumentUsingWrite = (html) ->
|
57
|
+
doc = document.implementation.createHTMLDocument ''
|
58
|
+
doc.open 'replace'
|
59
|
+
doc.write html
|
60
|
+
doc.close()
|
61
|
+
doc
|
62
|
+
|
63
|
+
# Use createDocumentUsingParser if DOMParser is defined and natively
|
64
|
+
# supports 'text/html' parsing (Firefox 12+, IE 10)
|
65
|
+
#
|
66
|
+
# Use createDocumentUsingDOM if createDocumentUsingParser throws an exception
|
67
|
+
# due to unsupported type 'text/html' (Firefox < 12, Opera)
|
68
|
+
#
|
69
|
+
# Use createDocumentUsingWrite if:
|
70
|
+
# - DOMParser isn't defined
|
71
|
+
# - createDocumentUsingParser returns null due to unsupported type 'text/html' (Chrome, Safari)
|
72
|
+
# - createDocumentUsingDOM doesn't create a valid HTML document (safeguarding against potential edge cases)
|
73
|
+
try
|
74
|
+
if window.DOMParser
|
75
|
+
testDoc = createDocumentUsingParser '<html><body><p>test'
|
76
|
+
createDocumentUsingParser
|
77
|
+
catch e
|
78
|
+
testDoc = createDocumentUsingDOM '<html><body><p>test'
|
79
|
+
createDocumentUsingDOM
|
80
|
+
finally
|
81
|
+
unless testDoc?.body?.childNodes.length is 1
|
82
|
+
return createDocumentUsingWrite
|
83
|
+
|
84
|
+
extractTitleAndBody = (doc) ->
|
85
|
+
title = doc.querySelector 'title'
|
86
|
+
[ title?.textContent, removeNoscriptTags(doc.body), CSRFToken.get(doc).token, 'runScripts' ]
|
87
|
+
|
88
|
+
CSRFToken =
|
89
|
+
get: (doc = document) ->
|
90
|
+
node: tag = doc.querySelector 'meta[name="csrf-token"]'
|
91
|
+
token: tag?.getAttribute? 'content'
|
92
|
+
|
93
|
+
update: (latest) ->
|
94
|
+
current = @get()
|
95
|
+
if current.token? and latest? and current.token isnt latest
|
96
|
+
current.node.setAttribute 'content', latest
|
97
|
+
|
98
|
+
changePage = (title, body, csrfToken, runScripts) ->
|
99
|
+
document.title = title
|
100
|
+
document.documentElement.replaceChild body, document.body
|
101
|
+
CSRFToken.update csrfToken if csrfToken?
|
102
|
+
executeScriptTags() if runScripts
|
103
|
+
currentState = window.history.state
|
104
|
+
triggerEvent 'page:change'
|
105
|
+
triggerEvent 'page:update'
|
106
|
+
|
107
|
+
executeScriptTags = ->
|
108
|
+
scripts = Array::slice.call document.body.querySelectorAll 'script:not([data-turbolinks-eval="false"])'
|
109
|
+
for script in scripts when script.type in ['', 'text/javascript']
|
110
|
+
copy = document.createElement 'script'
|
111
|
+
copy.setAttribute attr.name, attr.value for attr in script.attributes
|
112
|
+
copy.appendChild document.createTextNode script.innerHTML
|
113
|
+
{ parentNode, nextSibling } = script
|
114
|
+
parentNode.removeChild script
|
115
|
+
parentNode.insertBefore copy, nextSibling
|
116
|
+
return
|
117
|
+
|
118
|
+
removeNoscriptTags = (node) ->
|
119
|
+
node.innerHTML = node.innerHTML.replace /<noscript[\S\s]*?<\/noscript>/ig, ''
|
120
|
+
node
|
121
|
+
|
122
|
+
fetch = (url)->
|
123
|
+
rememberReferer()
|
124
|
+
|
125
|
+
xhr_req.resolve() if xhr_req
|
126
|
+
xhr_req = $q.defer()
|
127
|
+
|
128
|
+
$http.get(url, {
|
129
|
+
headers: {
|
130
|
+
'Accept' : 'text/html, application/xhtml+xml, application/xml'
|
131
|
+
'X-XHR-Referer' : referer
|
132
|
+
},
|
133
|
+
timeout: xhr_req.promise
|
134
|
+
}).success((data, status, headers)->
|
135
|
+
if doc = processResponse(data, status, headers)
|
136
|
+
changePage extractTitleAndBody(doc)...
|
137
|
+
#reflectRedirectedUrl()
|
138
|
+
triggerEvent 'page:load'
|
139
|
+
else
|
140
|
+
document.location.href = url
|
141
|
+
).error(->
|
142
|
+
console.log 'error'
|
143
|
+
)
|
144
|
+
|
145
|
+
# Handle bug in Firefox 26/27 where history.state is initially undefined
|
146
|
+
historyStateIsDefined =
|
147
|
+
window.history.state != undefined or navigator.userAgent.match /Firefox\/2[6|7]/
|
148
|
+
|
149
|
+
browserSupportsPushState =
|
150
|
+
window.history and window.history.pushState and window.history.replaceState and historyStateIsDefined
|
151
|
+
|
152
|
+
browserIsntBuggy =
|
153
|
+
!navigator.userAgent.match /CriOS\//
|
154
|
+
|
155
|
+
requestMethodIsSafe =
|
156
|
+
popCookie('request_method') in ['GET','']
|
157
|
+
|
158
|
+
browserSupportsTurbolinks = browserSupportsPushState and browserIsntBuggy and requestMethodIsSafe
|
159
|
+
|
160
|
+
browserSupportsCustomEvents =
|
161
|
+
document.addEventListener and document.createEvent
|
162
|
+
|
163
|
+
installDocumentReadyPageEventTriggers = ->
|
164
|
+
document.addEventListener 'DOMContentLoaded', ( ->
|
165
|
+
triggerEvent 'page:change'
|
166
|
+
triggerEvent 'page:update'
|
167
|
+
), true
|
168
|
+
|
169
|
+
installJqueryAjaxSuccessPageUpdateTrigger = ->
|
170
|
+
if typeof jQuery isnt 'undefined'
|
171
|
+
jQuery(document).on 'ajaxSuccess', (event, xhr, settings) ->
|
172
|
+
return unless jQuery.trim xhr.responseText
|
173
|
+
triggerEvent 'page:update'
|
174
|
+
|
175
|
+
if browserSupportsCustomEvents
|
176
|
+
installDocumentReadyPageEventTriggers()
|
177
|
+
installJqueryAjaxSuccessPageUpdateTrigger()
|
178
|
+
|
179
|
+
if browserSupportsTurbolinks
|
180
|
+
visit = fetch
|
181
|
+
createDocument = browserCompatibleDocumentParser()
|
182
|
+
else
|
183
|
+
visit = (url) ->
|
184
|
+
document.location.href = url
|
185
|
+
|
186
|
+
$rootScope.$on("$locationChangeStart", (event, url, prev_url)->
|
187
|
+
if url == prev_url
|
188
|
+
event.preventDefault()
|
189
|
+
return false
|
190
|
+
|
191
|
+
visit(url)
|
192
|
+
)
|
193
|
+
)
|
metadata
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: angular-turbolinks
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Cary Dunn
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-01-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: coffee-rails
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: turbolinks
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: Rails Turbolinks + Angular
|
70
|
+
email:
|
71
|
+
- cary.dunn@gmail.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- .gitignore
|
77
|
+
- Gemfile
|
78
|
+
- LICENSE.txt
|
79
|
+
- README.md
|
80
|
+
- Rakefile
|
81
|
+
- angular-turbolinks.gemspec
|
82
|
+
- lib/angular/turbolinks.rb
|
83
|
+
- lib/angular/turbolinks/version.rb
|
84
|
+
- lib/assets/javascripts/angular-turbolinks.js.coffee
|
85
|
+
homepage: ''
|
86
|
+
licenses:
|
87
|
+
- MIT
|
88
|
+
metadata: {}
|
89
|
+
post_install_message:
|
90
|
+
rdoc_options: []
|
91
|
+
require_paths:
|
92
|
+
- lib
|
93
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - '>='
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0'
|
98
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - '>='
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
requirements: []
|
104
|
+
rubyforge_project:
|
105
|
+
rubygems_version: 2.1.11
|
106
|
+
signing_key:
|
107
|
+
specification_version: 4
|
108
|
+
summary: Rails Turbolinks + Angular
|
109
|
+
test_files: []
|