breezy 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +3 -3
- data/app/views/breezy/response.html.erb +0 -0
- data/lib/assets/javascripts/breezy/component_url.coffee +61 -0
- data/lib/assets/javascripts/breezy/controller.coffee +197 -0
- data/lib/assets/javascripts/breezy/csrf_token.coffee +9 -0
- data/lib/assets/javascripts/breezy/doubly_linked_list.coffee +57 -0
- data/lib/assets/javascripts/breezy/parallel_queue.coffee +35 -0
- data/lib/assets/javascripts/breezy/progress_bar.coffee +139 -0
- data/lib/assets/javascripts/breezy/remote.coffee +136 -0
- data/lib/assets/javascripts/breezy/snapshot.coffee +100 -0
- data/lib/assets/javascripts/breezy/start.coffee +60 -0
- data/lib/assets/javascripts/breezy/utils.coffee +174 -0
- data/lib/breezy/version.rb +1 -1
- data/lib/breezy_template/search_extension.rb +39 -14
- data/lib/generators/breezy/install/install_generator.rb +61 -0
- data/lib/generators/breezy/install/templates/Default.js.jsx +7 -0
- data/lib/generators/breezy/install/templates/View.js.jsx +7 -0
- data/lib/generators/breezy/install/templates/boot.js +5 -0
- data/lib/generators/breezy/view/templates/view.js.breezy +15 -0
- data/lib/generators/breezy/view/templates/view.js.jsx +8 -0
- data/lib/generators/breezy/view/view_generator.rb +35 -0
- data/test/breezy_template_test.rb +49 -5
- metadata +20 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a241ba166d21aca45e519db2413f0fc9d47c6209
|
4
|
+
data.tar.gz: 697ff9e8f58420f08dc10d01c185f200b00a6357
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ad4a653fc55253f0ae7ca1f381f3ff34db917460a62b52cbe9dfe766335003b1b978adfb041fe8ff563e82ae0288db986d8b70c61f5d9b5272c65efefcd3f0e9
|
7
|
+
data.tar.gz: 8f10b7d1ed970640d8472991697f4ceb4883c46757831fbe89db4dfe44f61a2db5b73f05bfa6534b29655aab2b7d4d2d1aaf6eff388518a305f24c24249f81f0
|
data/README.md
CHANGED
@@ -104,7 +104,7 @@ rails g breezy:view Posts new index
|
|
104
104
|
```
|
105
105
|
|
106
106
|
# Navigation and Forms
|
107
|
-
Breezy intercepts all clicks on `<a>` and all submits on `<form>` elements enabled with `data-bz-remote`. Breezy will `preventDefault` then make the same request using XMLHttpRequest with a content type of `.js`. If there's an existing request, Breezy will cancel it unless the `data-bz-async` option is used.
|
107
|
+
Breezy intercepts all clicks on `<a>` and all submits on `<form>` elements enabled with `data-bz-remote`. Breezy will `preventDefault` then make the same request using XMLHttpRequest with a content type of `.js`. If there's an existing request, Breezy will cancel it unless the `data-bz-remote-async` option is used.
|
108
108
|
|
109
109
|
Once the response loads, a `breezy:load` event will be fired with the JS object that you created with BreezyTemplates. If you used the installation generator, the event will be set for you in the `<head>` element of your layout:
|
110
110
|
|
@@ -123,8 +123,8 @@ document.addEventListener('breezy:load', function(event){
|
|
123
123
|
Attribute | default value | description
|
124
124
|
-------------------|--------------------------|------------
|
125
125
|
`data-bz-remote` | For `<a>` the default is `get`. For forms, the default is `post` if a method is not specified. | Use this to create seamless page to page transitions. Works for both links and forms. You can specify the request method by giving it a value, e.g `<a href='foobar' data-bz-remote=post>`. For forms, the request method of the form is used. `<form action=foobar method='post' data-bz-remote>`.
|
126
|
-
`data-bz-async` | `false` | Fires off an async request. Responses are pushed into a queue will be evaluated in order of click or submit.
|
127
|
-
`data-bz-push-state` | `true` | Captures the element's URL in the browsers history. Normally used with `data-bz-async`.
|
126
|
+
`data-bz-remote-async` | `false` | Fires off an async request. Responses are pushed into a queue will be evaluated in order of click or submit.
|
127
|
+
`data-bz-push-state` | `true` | Captures the element's URL in the browsers history. Normally used with `data-bz-remote-async`.
|
128
128
|
`data-bz-silent` | false | To be used with the `breezy_silent?` ruby helper. Useful if you don't want to perform a redirect or render. Just return a 204, and Breezy will not fire a `breezy:load` event.
|
129
129
|
|
130
130
|
|
File without changes
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# The ComponentUrl class converts a basic URL string into an object
|
2
|
+
# that behaves similarly to document.location.
|
3
|
+
#
|
4
|
+
# If an instance is created from a relative URL, the current document
|
5
|
+
# is used to fill in the missing attributes (protocol, host, port).
|
6
|
+
uniqueId = ->
|
7
|
+
new Date().getTime().toString(36)
|
8
|
+
|
9
|
+
class Breezy.ComponentUrl
|
10
|
+
constructor: (@original = document.location.href) ->
|
11
|
+
return @original if @original.constructor is Breezy.ComponentUrl
|
12
|
+
@_parse()
|
13
|
+
|
14
|
+
withoutHash: -> @href.replace(@hash, '').replace('#', '')
|
15
|
+
|
16
|
+
# Intention revealing function alias
|
17
|
+
withoutHashForIE10compatibility: -> @withoutHash()
|
18
|
+
|
19
|
+
hasNoHash: -> @hash.length is 0
|
20
|
+
|
21
|
+
crossOrigin: ->
|
22
|
+
@origin isnt (new Breezy.ComponentUrl).origin
|
23
|
+
|
24
|
+
formatForXHR: (options = {}) ->
|
25
|
+
(if options.cache then @withMimeBust() else @withAntiCacheParam()).withoutHashForIE10compatibility()
|
26
|
+
|
27
|
+
withMimeBust: ->
|
28
|
+
new Breezy.ComponentUrl(
|
29
|
+
if /([?&])__=[^&]*/.test @absolute
|
30
|
+
@absolute
|
31
|
+
else
|
32
|
+
new Breezy.ComponentUrl(@withoutHash() + (if /\?/.test(@absolute) then "&" else "?") + "__=0" + @hash)
|
33
|
+
)
|
34
|
+
|
35
|
+
withAntiCacheParam: ->
|
36
|
+
new Breezy.ComponentUrl(
|
37
|
+
if /([?&])_=[^&]*/.test @absolute
|
38
|
+
@absolute.replace /([?&])_=[^&]*/, "$1_=#{uniqueId()}"
|
39
|
+
else
|
40
|
+
new Breezy.ComponentUrl(@withoutHash() + (if /\?/.test(@absolute) then "&" else "?") + "_=#{uniqueId()}" + @hash)
|
41
|
+
)
|
42
|
+
|
43
|
+
_parse: ->
|
44
|
+
(@link ?= document.createElement 'a').href = @original
|
45
|
+
{ @href, @protocol, @host, @hostname, @port, @pathname, @search, @hash } = @link
|
46
|
+
|
47
|
+
if @protocol == ':'
|
48
|
+
@protocol = document.location.protocol
|
49
|
+
if @protocol == ''
|
50
|
+
@protocol = document.location.protocol
|
51
|
+
if @port == ''
|
52
|
+
@port = document.location.port
|
53
|
+
if @hostname == ''
|
54
|
+
@hostname = document.location.hostname
|
55
|
+
if @pathname == ''
|
56
|
+
@pathname = '/'
|
57
|
+
|
58
|
+
@origin = [@protocol, '//', @hostname].join ''
|
59
|
+
@origin += ":#{@port}" unless @port.length is 0
|
60
|
+
@relative = [@pathname, @search, @hash].join ''
|
61
|
+
@absolute = @href
|
@@ -0,0 +1,197 @@
|
|
1
|
+
#= require breezy/doubly_linked_list
|
2
|
+
#= require breezy/snapshot
|
3
|
+
#= require breezy/progress_bar
|
4
|
+
#= require breezy/parallel_queue
|
5
|
+
#= require breezy/component_url
|
6
|
+
|
7
|
+
PAGE_CACHE_SIZE = 20
|
8
|
+
|
9
|
+
class Breezy.Controller
|
10
|
+
constructor: ->
|
11
|
+
@atomCache = {}
|
12
|
+
@history = new Breezy.Snapshot(this)
|
13
|
+
@transitionCacheEnabled = false
|
14
|
+
@requestCachingEnabled = true
|
15
|
+
|
16
|
+
@progressBar = new Breezy.ProgressBar 'html'
|
17
|
+
@pq = new Breezy.ParallelQueue
|
18
|
+
@http = null
|
19
|
+
|
20
|
+
@history.rememberCurrentUrlAndState()
|
21
|
+
|
22
|
+
currentPage: =>
|
23
|
+
@history.currentPage
|
24
|
+
|
25
|
+
request: (url, options = {}) =>
|
26
|
+
options = Breezy.Utils.reverseMerge options,
|
27
|
+
pushState: true
|
28
|
+
|
29
|
+
url = new Breezy.ComponentUrl url
|
30
|
+
return if @pageChangePrevented(url.absolute, options.target)
|
31
|
+
|
32
|
+
if url.crossOrigin()
|
33
|
+
document.location.href = url.absolute
|
34
|
+
return
|
35
|
+
|
36
|
+
@history.cacheCurrentPage()
|
37
|
+
if @progressBar? and !options.async
|
38
|
+
@progressBar?.start()
|
39
|
+
restorePoint = @history.transitionCacheFor(url.absolute)
|
40
|
+
|
41
|
+
if @transitionCacheEnabled and restorePoint and restorePoint.transition_cache
|
42
|
+
@history.reflectNewUrl(url)
|
43
|
+
@restore(restorePoint)
|
44
|
+
options.showProgressBar = false
|
45
|
+
|
46
|
+
options.cacheRequest ?= @requestCachingEnabled
|
47
|
+
options.showProgressBar ?= true
|
48
|
+
|
49
|
+
Breezy.Utils.triggerEvent Breezy.EVENTS.FETCH, url: url.absolute, options.target
|
50
|
+
|
51
|
+
if options.async
|
52
|
+
options.showProgressBar = false
|
53
|
+
req = @createRequest(url, options)
|
54
|
+
req.onError = ->
|
55
|
+
Breezy.Utils.triggerEvent Breezy.EVENTS.ERROR, null, options.target
|
56
|
+
@pq.push(req)
|
57
|
+
req.send(options.payload)
|
58
|
+
else
|
59
|
+
@pq.drain()
|
60
|
+
@http?.abort()
|
61
|
+
@http = @createRequest(url, options)
|
62
|
+
@http.send(options.payload)
|
63
|
+
|
64
|
+
enableTransitionCache: (enable = true) =>
|
65
|
+
@transitionCacheEnabled = enable
|
66
|
+
|
67
|
+
disableRequestCaching: (disable = true) =>
|
68
|
+
@requestCachingEnabled = not disable
|
69
|
+
disable
|
70
|
+
|
71
|
+
restore: (cachedPage, options = {}) =>
|
72
|
+
@http?.abort()
|
73
|
+
@history.changePage(cachedPage, options)
|
74
|
+
|
75
|
+
@progressBar?.done()
|
76
|
+
Breezy.Utils.triggerEvent Breezy.EVENTS.RESTORE
|
77
|
+
Breezy.Utils.triggerEvent Breezy.EVENTS.LOAD, cachedPage
|
78
|
+
|
79
|
+
replace: (nextPage, options = {}) =>
|
80
|
+
Breezy.Utils.withDefaults(nextPage, @history.currentBrowserState)
|
81
|
+
@history.changePage(nextPage, options)
|
82
|
+
Breezy.Utils.triggerEvent Breezy.EVENTS.LOAD, @currentPage()
|
83
|
+
|
84
|
+
crossOriginRedirect: =>
|
85
|
+
redirect = @http.getResponseHeader('Location')
|
86
|
+
crossOrigin = (new Breezy.ComponentUrl(redirect)).crossOrigin()
|
87
|
+
|
88
|
+
if redirect? and crossOrigin
|
89
|
+
redirect
|
90
|
+
|
91
|
+
pageChangePrevented: (url, target) =>
|
92
|
+
!Breezy.Utils.triggerEvent Breezy.EVENTS.BEFORE_CHANGE, url: url, target
|
93
|
+
|
94
|
+
cache: (key, value) =>
|
95
|
+
return @atomCache[key] if value == null
|
96
|
+
@atomCache[key] ||= value
|
97
|
+
|
98
|
+
# Events
|
99
|
+
onLoadEnd: => @http = null
|
100
|
+
|
101
|
+
onLoad: (xhr, url, options) =>
|
102
|
+
Breezy.Utils.triggerEvent Breezy.EVENTS.RECEIVE, url: url.absolute, options.target
|
103
|
+
nextPage = @processResponse(xhr)
|
104
|
+
if xhr.status == 0
|
105
|
+
return
|
106
|
+
|
107
|
+
if nextPage
|
108
|
+
if options.async && url.pathname != @currentPage().pathname
|
109
|
+
|
110
|
+
unless options.ignoreSamePathConstraint
|
111
|
+
@progressBar?.done()
|
112
|
+
Breezy.Utils.warn("Async response path is different from current page path")
|
113
|
+
return
|
114
|
+
|
115
|
+
if options.pushState
|
116
|
+
@history.reflectNewUrl url
|
117
|
+
|
118
|
+
Breezy.Utils.withDefaults(nextPage, @history.currentBrowserState)
|
119
|
+
|
120
|
+
if nextPage.action != 'graft'
|
121
|
+
@history.changePage(nextPage, options)
|
122
|
+
Breezy.Utils.triggerEvent Breezy.EVENTS.LOAD, @currentPage()
|
123
|
+
else
|
124
|
+
##clean this up
|
125
|
+
@history.graftByKeypath("data.#{nextPage.path}", nextPage.data)
|
126
|
+
|
127
|
+
if options.showProgressBar
|
128
|
+
@progressBar?.done()
|
129
|
+
@history.constrainPageCacheTo()
|
130
|
+
else
|
131
|
+
if options.async
|
132
|
+
Breezy.Utils.triggerEvent Breezy.EVENTS.ERROR, xhr, options.target
|
133
|
+
else
|
134
|
+
@progressBar?.done()
|
135
|
+
document.location.href = @crossOriginRedirect() or url.absolute
|
136
|
+
|
137
|
+
onProgress: (event) =>
|
138
|
+
@progressBar.advanceFromEvent(event)
|
139
|
+
|
140
|
+
onError: (url) =>
|
141
|
+
document.location.href = url.absolute
|
142
|
+
|
143
|
+
createRequest: (url, opts)=>
|
144
|
+
jsAccept = 'text/javascript, application/x-javascript, application/javascript'
|
145
|
+
requestMethod = opts.requestMethod || 'GET'
|
146
|
+
|
147
|
+
xhr = new XMLHttpRequest
|
148
|
+
xhr.open requestMethod, url.formatForXHR(cache: opts.cacheRequest), true
|
149
|
+
xhr.setRequestHeader 'Accept', jsAccept
|
150
|
+
xhr.setRequestHeader 'X-XHR-Referer', document.location.href
|
151
|
+
xhr.setRequestHeader 'X-Silent', opts.silent if opts.silent
|
152
|
+
xhr.setRequestHeader 'X-Requested-With', 'XMLHttpRequest'
|
153
|
+
xhr.setRequestHeader 'Content-Type', opts.contentType if opts.contentType
|
154
|
+
|
155
|
+
csrfToken = Breezy.CSRFToken.get().token
|
156
|
+
xhr.setRequestHeader('X-CSRF-Token', csrfToken) if csrfToken
|
157
|
+
|
158
|
+
if !opts.silent
|
159
|
+
xhr.onload = =>
|
160
|
+
self = ` this `
|
161
|
+
redirectedUrl = self.getResponseHeader 'X-XHR-Redirected-To'
|
162
|
+
actualUrl = redirectedUrl || url
|
163
|
+
@onLoad(self, actualUrl, opts)
|
164
|
+
else
|
165
|
+
xhr.onload = =>
|
166
|
+
@progressBar?.done()
|
167
|
+
|
168
|
+
xhr.onprogress = @onProgress if @progressBar and opts.showProgressBar
|
169
|
+
xhr.onloadend = @onLoadEnd
|
170
|
+
xhr.onerror = =>
|
171
|
+
@onError(url)
|
172
|
+
xhr
|
173
|
+
|
174
|
+
processResponse: (xhr) ->
|
175
|
+
if @hasValidResponse(xhr)
|
176
|
+
return @responseContent(xhr)
|
177
|
+
|
178
|
+
hasValidResponse: (xhr) ->
|
179
|
+
not @clientOrServerError(xhr) and @validContent(xhr) and not @downloadingFile(xhr)
|
180
|
+
|
181
|
+
responseContent: (xhr) ->
|
182
|
+
new Function("'use strict'; return " + xhr.responseText )()
|
183
|
+
|
184
|
+
clientOrServerError: (xhr) ->
|
185
|
+
400 <= xhr.status < 600
|
186
|
+
|
187
|
+
validContent: (xhr) ->
|
188
|
+
contentType = xhr.getResponseHeader('Content-Type')
|
189
|
+
jsContent = /^(?:text\/javascript|application\/x-javascript|application\/javascript)(?:;|$)/
|
190
|
+
|
191
|
+
contentType? and contentType.match jsContent
|
192
|
+
|
193
|
+
downloadingFile: (xhr) ->
|
194
|
+
(disposition = xhr.getResponseHeader('Content-Disposition'))? and
|
195
|
+
disposition.match /^attachment/
|
196
|
+
|
197
|
+
|
@@ -0,0 +1,9 @@
|
|
1
|
+
class Breezy.CSRFToken
|
2
|
+
@get: (doc = document) ->
|
3
|
+
node: tag = doc.querySelector 'meta[name="csrf-token"]'
|
4
|
+
token: tag?.getAttribute? 'content'
|
5
|
+
|
6
|
+
@update: (latest) ->
|
7
|
+
current = @get()
|
8
|
+
if current.token? and latest? and current.token isnt latest
|
9
|
+
current.node.setAttribute 'content', latest
|
@@ -0,0 +1,57 @@
|
|
1
|
+
class Breezy.DoublyLinkedList
|
2
|
+
constructor: ->
|
3
|
+
@head = @tail = null
|
4
|
+
@length = 0
|
5
|
+
|
6
|
+
createNode: (obj) ->
|
7
|
+
return {prev: null, element: obj, next: null}
|
8
|
+
|
9
|
+
push: (obj) ->
|
10
|
+
if (@tail)
|
11
|
+
ele = @createNode(obj)
|
12
|
+
ele.prev = @tail
|
13
|
+
@tail = @tail.next = ele
|
14
|
+
@length += 1
|
15
|
+
else
|
16
|
+
@head = @tail = @createNode(obj)
|
17
|
+
@length += 1
|
18
|
+
|
19
|
+
pop: ()->
|
20
|
+
if (@tail)
|
21
|
+
element = @tail
|
22
|
+
@tail = element.prev
|
23
|
+
element.prev = null
|
24
|
+
if (@tail)
|
25
|
+
@tail.next = null
|
26
|
+
if (@head == element)
|
27
|
+
@head = null
|
28
|
+
@length -= 1
|
29
|
+
|
30
|
+
element.element
|
31
|
+
else
|
32
|
+
null
|
33
|
+
|
34
|
+
shift: ()->
|
35
|
+
if (@head)
|
36
|
+
element = @head
|
37
|
+
@head = element.next
|
38
|
+
element.next = null
|
39
|
+
if (@head)
|
40
|
+
@head.prev = null
|
41
|
+
if (@tail == element)
|
42
|
+
@tail = null
|
43
|
+
@length -= 1
|
44
|
+
|
45
|
+
element.element
|
46
|
+
else
|
47
|
+
null
|
48
|
+
|
49
|
+
unshift: (obj)->
|
50
|
+
if (@head)
|
51
|
+
ele = @createNode(obj)
|
52
|
+
ele.next = @head
|
53
|
+
@head = @head.prev = ele
|
54
|
+
@length += 1
|
55
|
+
else
|
56
|
+
@head = @tail = @createNode(obj)
|
57
|
+
@length += 1
|
@@ -0,0 +1,35 @@
|
|
1
|
+
#= require breezy/doubly_linked_list
|
2
|
+
|
3
|
+
class Breezy.ParallelQueue
|
4
|
+
constructor: ->
|
5
|
+
@dll = new Breezy.DoublyLinkedList
|
6
|
+
@active = true
|
7
|
+
|
8
|
+
push:(xhr)->
|
9
|
+
@dll.push(xhr)
|
10
|
+
xhr._originalOnLoad = xhr.onload.bind(xhr)
|
11
|
+
|
12
|
+
xhr.onload = =>
|
13
|
+
if @active
|
14
|
+
xhr._isDone = true
|
15
|
+
node = @dll.head
|
16
|
+
while(node)
|
17
|
+
qxhr = node.element
|
18
|
+
if !qxhr._isDone
|
19
|
+
node = null
|
20
|
+
else
|
21
|
+
node = node.next
|
22
|
+
@dll.shift()
|
23
|
+
qxhr._originalOnLoad()
|
24
|
+
|
25
|
+
drain: ->
|
26
|
+
@active = false
|
27
|
+
node = @dll.head
|
28
|
+
while(node)
|
29
|
+
qxhr = node.element
|
30
|
+
qxhr.abort()
|
31
|
+
qxhr._isDone = true
|
32
|
+
node = node.next
|
33
|
+
@dll = new Breezy.DoublyLinkedList
|
34
|
+
@active = true
|
35
|
+
|
@@ -0,0 +1,139 @@
|
|
1
|
+
class Breezy.ProgressBar
|
2
|
+
className = 'breezy-progress-bar'
|
3
|
+
# Setting the opacity to a value < 1 fixes a display issue in Safari 6 and
|
4
|
+
# iOS 6 where the progress bar would fill the entire page.
|
5
|
+
originalOpacity = 0.99
|
6
|
+
|
7
|
+
constructor: (@elementSelector) ->
|
8
|
+
@value = 0
|
9
|
+
@content = ''
|
10
|
+
@speed = 300
|
11
|
+
@opacity = originalOpacity
|
12
|
+
@delay = 400
|
13
|
+
@active = null
|
14
|
+
@install()
|
15
|
+
|
16
|
+
install: ->
|
17
|
+
if @active
|
18
|
+
return
|
19
|
+
|
20
|
+
@active = true
|
21
|
+
@element = document.querySelector(@elementSelector)
|
22
|
+
@element.classList.add(className)
|
23
|
+
@styleElement = document.createElement('style')
|
24
|
+
document.head.appendChild(@styleElement)
|
25
|
+
@_updateStyle()
|
26
|
+
|
27
|
+
uninstall: ->
|
28
|
+
if !@active
|
29
|
+
return
|
30
|
+
|
31
|
+
@active = false
|
32
|
+
@element.classList.remove(className)
|
33
|
+
document.head.removeChild(@styleElement)
|
34
|
+
|
35
|
+
start: ({delay} = {})->
|
36
|
+
clearTimeout(@displayTimeout)
|
37
|
+
if @delay
|
38
|
+
@display = false
|
39
|
+
@displayTimeout = setTimeout =>
|
40
|
+
@display = true
|
41
|
+
, @delay
|
42
|
+
else
|
43
|
+
@display = true
|
44
|
+
|
45
|
+
if @value > 0
|
46
|
+
@_reset()
|
47
|
+
@_reflow()
|
48
|
+
|
49
|
+
@advanceTo(5)
|
50
|
+
|
51
|
+
advanceTo: (value) ->
|
52
|
+
if value > @value <= 100
|
53
|
+
@value = value
|
54
|
+
@_updateStyle()
|
55
|
+
|
56
|
+
if @value is 100
|
57
|
+
@_stopTrickle()
|
58
|
+
else if @value > 0
|
59
|
+
@_startTrickle()
|
60
|
+
|
61
|
+
advanceFromEvent: (event) =>
|
62
|
+
percent = if event.lengthComputable
|
63
|
+
event.loaded / event.total * 100
|
64
|
+
else
|
65
|
+
@value + (100 - @value) / 10
|
66
|
+
@advanceTo(percent)
|
67
|
+
|
68
|
+
done: ->
|
69
|
+
if @value > 0
|
70
|
+
@advanceTo(100)
|
71
|
+
@_finish()
|
72
|
+
|
73
|
+
setDelay: (milliseconds) =>
|
74
|
+
@delay = milliseconds
|
75
|
+
|
76
|
+
_finish: ->
|
77
|
+
@fadeTimer = setTimeout =>
|
78
|
+
@opacity = 0
|
79
|
+
@_updateStyle()
|
80
|
+
, @speed / 2
|
81
|
+
|
82
|
+
@resetTimer = setTimeout(@_reset, @speed)
|
83
|
+
|
84
|
+
_reflow: ->
|
85
|
+
@element.offsetHeight
|
86
|
+
|
87
|
+
_reset: =>
|
88
|
+
@_stopTimers()
|
89
|
+
@value = 0
|
90
|
+
@opacity = originalOpacity
|
91
|
+
@_withSpeed(0, => @_updateStyle(true))
|
92
|
+
|
93
|
+
_stopTimers: ->
|
94
|
+
@_stopTrickle()
|
95
|
+
clearTimeout(@fadeTimer)
|
96
|
+
clearTimeout(@resetTimer)
|
97
|
+
|
98
|
+
_startTrickle: ->
|
99
|
+
return if @trickleTimer
|
100
|
+
@trickleTimer = setTimeout(@_trickle, @speed)
|
101
|
+
|
102
|
+
_stopTrickle: ->
|
103
|
+
clearTimeout(@trickleTimer)
|
104
|
+
delete @trickleTimer
|
105
|
+
|
106
|
+
_trickle: =>
|
107
|
+
@advanceTo(@value + Math.random() / 2)
|
108
|
+
@trickleTimer = setTimeout(@_trickle, @speed)
|
109
|
+
|
110
|
+
_withSpeed: (speed, fn) ->
|
111
|
+
originalSpeed = @speed
|
112
|
+
@speed = speed
|
113
|
+
result = fn()
|
114
|
+
@speed = originalSpeed
|
115
|
+
result
|
116
|
+
|
117
|
+
_updateStyle: (forceRepaint = false) ->
|
118
|
+
@_changeContentToForceRepaint() if forceRepaint
|
119
|
+
@styleElement.textContent = @_createCSSRule()
|
120
|
+
|
121
|
+
_changeContentToForceRepaint: ->
|
122
|
+
@content = if @content is '' then ' ' else ''
|
123
|
+
|
124
|
+
_createCSSRule: ->
|
125
|
+
"""
|
126
|
+
#{@elementSelector}.#{className}::before {
|
127
|
+
content: '#{@content}';
|
128
|
+
position: fixed;
|
129
|
+
top: 0;
|
130
|
+
left: 0;
|
131
|
+
z-index: 2000;
|
132
|
+
background-color: #0076ff;
|
133
|
+
height: 3px;
|
134
|
+
opacity: #{@opacity};
|
135
|
+
width: #{if @display then @value else 0}%;
|
136
|
+
transition: width #{@speed}ms ease-out, opacity #{@speed / 2}ms ease-in;
|
137
|
+
transform: translate3d(0,0,0);
|
138
|
+
}
|
139
|
+
"""
|