cis_pace 0.0.0
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 +7 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +59 -0
- data/README.md +0 -0
- data/Rakefile +2 -0
- data/app/assets/javascripts/pace.coffee +632 -0
- data/app/assets/javascripts/pace.js +756 -0
- data/app/assets/javascripts/pace.min.js +2 -0
- data/app/assets/stylesheets/pace-theme-barber-shop.css +72 -0
- data/app/assets/stylesheets/pace-theme-big-counter.css +35 -0
- data/app/assets/stylesheets/pace-theme-bounce.css +231 -0
- data/app/assets/stylesheets/pace-theme-center-circle.css +110 -0
- data/app/assets/stylesheets/pace-theme-corner-indicator.css +70 -0
- data/app/assets/stylesheets/pace-theme-fill-left.css +22 -0
- data/app/assets/stylesheets/pace-theme-flash.css +81 -0
- data/app/assets/stylesheets/pace-theme-flat-top.css +33 -0
- data/app/assets/stylesheets/pace-theme-mac-osx.css +84 -0
- data/app/assets/stylesheets/pace-theme-minimal.css +18 -0
- data/lib/cis_pace.rb +18 -0
- metadata +103 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d4f9c3b5dc070c772d46da7c38841120b6679d9d
|
4
|
+
data.tar.gz: 5091b1e9200753af1565083dce9c866a0af1a417
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 140feded1c01846a2982dc9a904075bcba202b8015565550c2ef02bb8a8faa1b787d1ea47a9a2f9aa3e142c2716418c6eebc05635d68a5f6655e45989a6ea889
|
7
|
+
data.tar.gz: b5165fcdac9ede349969e9f4840aa5a5f74282638a354d2af4ea7d54627f9439efe96c140b14cd8ed8148a3703fa24a5deaf9b57047fe72fc339b079df8b922d
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
cis_pace (0.0.0)
|
5
|
+
coffee-rails
|
6
|
+
jquery-rails
|
7
|
+
railties
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: http://rubygems.org/
|
11
|
+
specs:
|
12
|
+
actionpack (4.0.0)
|
13
|
+
activesupport (= 4.0.0)
|
14
|
+
builder (~> 3.1.0)
|
15
|
+
erubis (~> 2.7.0)
|
16
|
+
rack (~> 1.5.2)
|
17
|
+
rack-test (~> 0.6.2)
|
18
|
+
activesupport (4.0.0)
|
19
|
+
i18n (~> 0.6, >= 0.6.4)
|
20
|
+
minitest (~> 4.2)
|
21
|
+
multi_json (~> 1.3)
|
22
|
+
thread_safe (~> 0.1)
|
23
|
+
tzinfo (~> 0.3.37)
|
24
|
+
atomic (1.1.14)
|
25
|
+
builder (3.1.4)
|
26
|
+
coffee-rails (4.0.0)
|
27
|
+
coffee-script (>= 2.2.0)
|
28
|
+
railties (>= 4.0.0.beta, < 5.0)
|
29
|
+
coffee-script (2.2.0)
|
30
|
+
coffee-script-source
|
31
|
+
execjs
|
32
|
+
coffee-script-source (1.6.3)
|
33
|
+
erubis (2.7.0)
|
34
|
+
execjs (2.0.2)
|
35
|
+
i18n (0.6.5)
|
36
|
+
jquery-rails (3.0.4)
|
37
|
+
railties (>= 3.0, < 5.0)
|
38
|
+
thor (>= 0.14, < 2.0)
|
39
|
+
minitest (4.7.5)
|
40
|
+
multi_json (1.8.2)
|
41
|
+
rack (1.5.2)
|
42
|
+
rack-test (0.6.2)
|
43
|
+
rack (>= 1.0)
|
44
|
+
railties (4.0.0)
|
45
|
+
actionpack (= 4.0.0)
|
46
|
+
activesupport (= 4.0.0)
|
47
|
+
rake (>= 0.8.7)
|
48
|
+
thor (>= 0.18.1, < 2.0)
|
49
|
+
rake (10.1.0)
|
50
|
+
thor (0.18.1)
|
51
|
+
thread_safe (0.1.3)
|
52
|
+
atomic
|
53
|
+
tzinfo (0.3.38)
|
54
|
+
|
55
|
+
PLATFORMS
|
56
|
+
ruby
|
57
|
+
|
58
|
+
DEPENDENCIES
|
59
|
+
cis_pace!
|
data/README.md
ADDED
File without changes
|
data/Rakefile
ADDED
@@ -0,0 +1,632 @@
|
|
1
|
+
defaultOptions =
|
2
|
+
# How long should it take for the bar to animate to a new
|
3
|
+
# point after receiving it
|
4
|
+
catchupTime: 500
|
5
|
+
|
6
|
+
# How quickly should the bar be moving before it has any progress
|
7
|
+
# info from a new source in %/ms
|
8
|
+
initialRate: .03
|
9
|
+
|
10
|
+
# What is the minimum amount of time the bar should be on the
|
11
|
+
# screen
|
12
|
+
minTime: 500
|
13
|
+
|
14
|
+
# What is the minimum amount of time the bar should sit after the last
|
15
|
+
# update before disappearing
|
16
|
+
ghostTime: 250
|
17
|
+
|
18
|
+
# Its easy for a bunch of the bar to be eaten in the first few frames
|
19
|
+
# before we know how much there is to load. This limits how much of
|
20
|
+
# the bar can be used per frame
|
21
|
+
maxProgressPerFrame: 10
|
22
|
+
|
23
|
+
# This tweaks the animation easing
|
24
|
+
easeFactor: 1.25
|
25
|
+
|
26
|
+
# Should pace automatically start when the page is loaded, or should it wait for `start` to
|
27
|
+
# be called? Always false if pace is loaded with AMD or CommonJS.
|
28
|
+
startOnPageLoad: true
|
29
|
+
|
30
|
+
# Should we restart the browser when pushState or replaceState is called? (Generally
|
31
|
+
# means ajax navigation has occured)
|
32
|
+
restartOnPushState: true
|
33
|
+
|
34
|
+
# Should we show the progress bar for every ajax request (not just regular or ajax-y page
|
35
|
+
# navigation)? Set to false to disable.
|
36
|
+
#
|
37
|
+
# If so, how many ms does the request have to be running for before we show the progress?
|
38
|
+
restartOnRequestAfter: 500
|
39
|
+
|
40
|
+
# What element should the pace element be appended to on the page?
|
41
|
+
target: 'body'
|
42
|
+
|
43
|
+
elements:
|
44
|
+
# How frequently in ms should we check for the elements being tested for
|
45
|
+
# using the element monitor?
|
46
|
+
checkInterval: 100
|
47
|
+
|
48
|
+
# What elements should we wait for before deciding the page is fully loaded (not required)
|
49
|
+
selectors: ['body']
|
50
|
+
|
51
|
+
eventLag:
|
52
|
+
# When we first start measuring event lag, not much is going on in the browser yet, so it's
|
53
|
+
# not uncommon for the numbers to be abnormally low for the first few samples. This configures
|
54
|
+
# how many samples we need before we consider a low number to mean completion.
|
55
|
+
minSamples: 10
|
56
|
+
|
57
|
+
# How many samples should we average to decide what the current lag is?
|
58
|
+
sampleCount: 3
|
59
|
+
|
60
|
+
# Above how many ms of lag is the CPU considered busy?
|
61
|
+
lagThreshold: 3
|
62
|
+
|
63
|
+
ajax:
|
64
|
+
# Which HTTP methods should we track?
|
65
|
+
trackMethods: ['GET']
|
66
|
+
|
67
|
+
# Should we track web socket connections?
|
68
|
+
trackWebSockets: false
|
69
|
+
|
70
|
+
now = ->
|
71
|
+
performance?.now?() ? +new Date
|
72
|
+
|
73
|
+
requestAnimationFrame = window.requestAnimationFrame or window.mozRequestAnimationFrame or
|
74
|
+
window.webkitRequestAnimationFrame or window.msRequestAnimationFrame
|
75
|
+
|
76
|
+
cancelAnimationFrame = window.cancelAnimationFrame or window.mozCancelAnimationFrame
|
77
|
+
|
78
|
+
if not requestAnimationFrame?
|
79
|
+
requestAnimationFrame = (fn) ->
|
80
|
+
setTimeout fn, 50
|
81
|
+
|
82
|
+
cancelAnimationFrame = (id) ->
|
83
|
+
clearTimeout id
|
84
|
+
|
85
|
+
runAnimation = (fn) ->
|
86
|
+
last = now()
|
87
|
+
tick = ->
|
88
|
+
diff = now() - last
|
89
|
+
last = now()
|
90
|
+
|
91
|
+
fn diff, ->
|
92
|
+
requestAnimationFrame tick
|
93
|
+
|
94
|
+
tick()
|
95
|
+
|
96
|
+
result = (obj, key, args...) ->
|
97
|
+
if typeof obj[key] is 'function'
|
98
|
+
obj[key](args...)
|
99
|
+
else
|
100
|
+
obj[key]
|
101
|
+
|
102
|
+
extend = (out, sources...) ->
|
103
|
+
for source in sources when source
|
104
|
+
for own key, val of source
|
105
|
+
if out[key]? and typeof out[key] is 'object' and val? and typeof val is 'object'
|
106
|
+
extend(out[key], val)
|
107
|
+
else
|
108
|
+
out[key] = val
|
109
|
+
out
|
110
|
+
|
111
|
+
avgAmplitude = (arr) ->
|
112
|
+
sum = count = 0
|
113
|
+
for v in arr
|
114
|
+
sum += Math.abs(v)
|
115
|
+
count++
|
116
|
+
|
117
|
+
sum / count
|
118
|
+
|
119
|
+
getFromDOM = (key='options', json=true) ->
|
120
|
+
el = document.querySelector "[data-pace-#{ key }]"
|
121
|
+
|
122
|
+
return unless el
|
123
|
+
|
124
|
+
data = el.getAttribute "data-pace-#{ key }"
|
125
|
+
|
126
|
+
return data if not json
|
127
|
+
|
128
|
+
try
|
129
|
+
return JSON.parse data
|
130
|
+
catch e
|
131
|
+
console?.error "Error parsing inline pace options", e
|
132
|
+
|
133
|
+
window.Pace ?= {}
|
134
|
+
|
135
|
+
options = Pace.options = extend defaultOptions, window.paceOptions, getFromDOM()
|
136
|
+
|
137
|
+
class NoTargetError extends Error
|
138
|
+
|
139
|
+
class Bar
|
140
|
+
constructor: ->
|
141
|
+
@progress = 0
|
142
|
+
|
143
|
+
getElement: ->
|
144
|
+
if not @el?
|
145
|
+
targetElement = document.querySelector options.target
|
146
|
+
|
147
|
+
if not targetElement
|
148
|
+
throw new NoTargetError
|
149
|
+
|
150
|
+
@el = document.createElement 'div'
|
151
|
+
@el.className = "pace pace-active"
|
152
|
+
|
153
|
+
document.body.className = document.body.className.replace 'pace-done', ''
|
154
|
+
document.body.className += ' pace-running'
|
155
|
+
|
156
|
+
@el.innerHTML = '''
|
157
|
+
<div class="pace-progress">
|
158
|
+
<div class="pace-progress-inner"></div>
|
159
|
+
</div>
|
160
|
+
<div class="pace-activity"></div>
|
161
|
+
'''
|
162
|
+
if targetElement.firstChild?
|
163
|
+
targetElement.insertBefore @el, targetElement.firstChild
|
164
|
+
else
|
165
|
+
targetElement.appendChild @el
|
166
|
+
|
167
|
+
@el
|
168
|
+
|
169
|
+
finish: ->
|
170
|
+
el = @getElement()
|
171
|
+
|
172
|
+
el.className = el.className.replace 'pace-active', ''
|
173
|
+
el.className += ' pace-inactive'
|
174
|
+
|
175
|
+
document.body.className = document.body.className.replace 'pace-running', ''
|
176
|
+
document.body.className += ' pace-done'
|
177
|
+
|
178
|
+
update: (prog) ->
|
179
|
+
@progress = prog
|
180
|
+
|
181
|
+
do @render
|
182
|
+
|
183
|
+
destroy: ->
|
184
|
+
try
|
185
|
+
@getElement().parentNode.removeChild(@getElement())
|
186
|
+
catch NoTargetError
|
187
|
+
|
188
|
+
@el = undefined
|
189
|
+
|
190
|
+
render: ->
|
191
|
+
if not document.querySelector(options.target)?
|
192
|
+
return false
|
193
|
+
|
194
|
+
el = @getElement()
|
195
|
+
|
196
|
+
el.children[0].style.width = "#{ @progress }%"
|
197
|
+
|
198
|
+
if not @lastRenderedProgress or @lastRenderedProgress|0 != @progress|0
|
199
|
+
# The whole-part of the number has changed
|
200
|
+
|
201
|
+
el.children[0].setAttribute 'data-progress-text', "#{ @progress|0 }%"
|
202
|
+
|
203
|
+
if @progress >= 100
|
204
|
+
# We cap it at 99 so we can use prefix-based attribute selectors
|
205
|
+
progressStr = '99'
|
206
|
+
else
|
207
|
+
progressStr = if @progress < 10 then "0" else ""
|
208
|
+
progressStr += @progress|0
|
209
|
+
|
210
|
+
el.children[0].setAttribute 'data-progress', "#{ progressStr }"
|
211
|
+
|
212
|
+
@lastRenderedProgress = @progress
|
213
|
+
|
214
|
+
done: ->
|
215
|
+
@progress >= 100
|
216
|
+
|
217
|
+
class Events
|
218
|
+
constructor: ->
|
219
|
+
@bindings = {}
|
220
|
+
|
221
|
+
trigger: (name, val) ->
|
222
|
+
if @bindings[name]?
|
223
|
+
for binding in @bindings[name]
|
224
|
+
binding.call @, val
|
225
|
+
|
226
|
+
on: (name, fn) ->
|
227
|
+
@bindings[name] ?= []
|
228
|
+
@bindings[name].push fn
|
229
|
+
|
230
|
+
_XMLHttpRequest = window.XMLHttpRequest
|
231
|
+
_XDomainRequest = window.XDomainRequest
|
232
|
+
_WebSocket = window.WebSocket
|
233
|
+
|
234
|
+
extendNative = (to, from) ->
|
235
|
+
for key of from::
|
236
|
+
try
|
237
|
+
val = from::[key]
|
238
|
+
|
239
|
+
if not to[key]? and typeof val isnt 'function'
|
240
|
+
to[key] = val
|
241
|
+
catch e
|
242
|
+
|
243
|
+
# We should only ever instantiate one of these
|
244
|
+
class RequestIntercept extends Events
|
245
|
+
constructor: ->
|
246
|
+
super
|
247
|
+
|
248
|
+
monitorXHR = (req) =>
|
249
|
+
_open = req.open
|
250
|
+
req.open = (type, url, async) =>
|
251
|
+
if (type ? 'GET').toUpperCase() in options.ajax.trackMethods
|
252
|
+
@trigger 'request', {type, url, request: req}
|
253
|
+
|
254
|
+
_open.apply req, arguments
|
255
|
+
|
256
|
+
window.XMLHttpRequest = (flags) ->
|
257
|
+
req = new _XMLHttpRequest(flags)
|
258
|
+
|
259
|
+
monitorXHR req
|
260
|
+
|
261
|
+
req
|
262
|
+
|
263
|
+
extendNative window.XMLHttpRequest, _XMLHttpRequest
|
264
|
+
|
265
|
+
if _XDomainRequest?
|
266
|
+
window.XDomainRequest = ->
|
267
|
+
req = new _XDomainRequest
|
268
|
+
|
269
|
+
monitorXHR req
|
270
|
+
|
271
|
+
req
|
272
|
+
|
273
|
+
extendNative window.XDomainRequest, _XDomainRequest
|
274
|
+
|
275
|
+
if _WebSocket? and options.ajax.trackWebSockets
|
276
|
+
window.WebSocket = (url, protocols) =>
|
277
|
+
req = new _WebSocket(url, protocols)
|
278
|
+
|
279
|
+
@trigger 'request', {type: 'socket', url, protocols, request: req}
|
280
|
+
|
281
|
+
req
|
282
|
+
|
283
|
+
extendNative window.WebSocket, _WebSocket
|
284
|
+
|
285
|
+
_intercept = null
|
286
|
+
getIntercept = ->
|
287
|
+
if not _intercept?
|
288
|
+
_intercept = new RequestIntercept
|
289
|
+
_intercept
|
290
|
+
|
291
|
+
if options.restartOnRequestAfter isnt false
|
292
|
+
# If we want to start the progress bar
|
293
|
+
# on every request, we need to hear the request
|
294
|
+
# and then inject it into the new ajax monitor
|
295
|
+
# start will have created.
|
296
|
+
|
297
|
+
getIntercept().on 'request', ({type, request}) ->
|
298
|
+
if not Pace.running
|
299
|
+
args = arguments
|
300
|
+
|
301
|
+
setTimeout ->
|
302
|
+
if type is 'socket'
|
303
|
+
stillActive = request.readyState < 2
|
304
|
+
else
|
305
|
+
stillActive = 0 < request.readyState < 4
|
306
|
+
|
307
|
+
if stillActive
|
308
|
+
Pace.restart()
|
309
|
+
|
310
|
+
for source in Pace.sources
|
311
|
+
if source instanceof AjaxMonitor
|
312
|
+
source.watch args...
|
313
|
+
break
|
314
|
+
, options.restartOnRequestAfter
|
315
|
+
|
316
|
+
class AjaxMonitor
|
317
|
+
constructor: ->
|
318
|
+
@elements = []
|
319
|
+
|
320
|
+
getIntercept().on 'request', => @watch arguments...
|
321
|
+
|
322
|
+
watch: ({type, request}) ->
|
323
|
+
if type is 'socket'
|
324
|
+
tracker = new SocketRequestTracker(request)
|
325
|
+
else
|
326
|
+
tracker = new XHRRequestTracker(request)
|
327
|
+
|
328
|
+
@elements.push tracker
|
329
|
+
|
330
|
+
class XHRRequestTracker
|
331
|
+
constructor: (request) ->
|
332
|
+
@progress = 0
|
333
|
+
|
334
|
+
if window.ProgressEvent?
|
335
|
+
# We're dealing with a modern browser with progress event support
|
336
|
+
|
337
|
+
size = null
|
338
|
+
request.addEventListener 'progress', (evt) =>
|
339
|
+
if evt.lengthComputable
|
340
|
+
@progress = 100 * evt.loaded / evt.total
|
341
|
+
else
|
342
|
+
# If it's chunked encoding, we have no way of knowing the total length of the
|
343
|
+
# response, all we can do is increment the progress with backoff such that we
|
344
|
+
# never hit 100% until it's done.
|
345
|
+
@progress = @progress + (100 - @progress) / 2
|
346
|
+
|
347
|
+
for event in ['load', 'abort', 'timeout', 'error']
|
348
|
+
request.addEventListener event, =>
|
349
|
+
@progress = 100
|
350
|
+
|
351
|
+
else
|
352
|
+
_onreadystatechange = request.onreadystatechange
|
353
|
+
request.onreadystatechange = =>
|
354
|
+
if request.readyState in [0, 4]
|
355
|
+
@progress = 100
|
356
|
+
else if request.readyState is 3
|
357
|
+
@progress = 50
|
358
|
+
|
359
|
+
_onreadystatechange?(arguments...)
|
360
|
+
|
361
|
+
class SocketRequestTracker
|
362
|
+
constructor: (request) ->
|
363
|
+
@progress = 0
|
364
|
+
|
365
|
+
for event in ['error', 'open']
|
366
|
+
request.addEventListener event, =>
|
367
|
+
@progress = 100
|
368
|
+
|
369
|
+
class ElementMonitor
|
370
|
+
constructor: (options={}) ->
|
371
|
+
@elements = []
|
372
|
+
|
373
|
+
options.selectors ?= []
|
374
|
+
for selector in options.selectors
|
375
|
+
@elements.push new ElementTracker selector
|
376
|
+
|
377
|
+
class ElementTracker
|
378
|
+
constructor: (@selector) ->
|
379
|
+
@progress = 0
|
380
|
+
|
381
|
+
@check()
|
382
|
+
|
383
|
+
check: ->
|
384
|
+
if document.querySelector(@selector)
|
385
|
+
@done()
|
386
|
+
else
|
387
|
+
setTimeout (=> @check()),
|
388
|
+
options.elements.checkInterval
|
389
|
+
|
390
|
+
done: ->
|
391
|
+
@progress = 100
|
392
|
+
|
393
|
+
class DocumentMonitor
|
394
|
+
states:
|
395
|
+
loading: 0
|
396
|
+
interactive: 50
|
397
|
+
complete: 100
|
398
|
+
|
399
|
+
constructor: ->
|
400
|
+
@progress = @states[document.readyState] ? 100
|
401
|
+
|
402
|
+
_onreadystatechange = document.onreadystatechange
|
403
|
+
document.onreadystatechange = =>
|
404
|
+
if @states[document.readyState]?
|
405
|
+
@progress = @states[document.readyState]
|
406
|
+
|
407
|
+
_onreadystatechange?(arguments...)
|
408
|
+
|
409
|
+
class EventLagMonitor
|
410
|
+
constructor: ->
|
411
|
+
@progress = 0
|
412
|
+
|
413
|
+
avg = 0
|
414
|
+
|
415
|
+
samples = []
|
416
|
+
|
417
|
+
points = 0
|
418
|
+
last = now()
|
419
|
+
interval = setInterval =>
|
420
|
+
diff = now() - last - 50
|
421
|
+
last = now()
|
422
|
+
|
423
|
+
samples.push diff
|
424
|
+
|
425
|
+
if samples.length > options.eventLag.sampleCount
|
426
|
+
samples.shift()
|
427
|
+
|
428
|
+
avg = avgAmplitude samples
|
429
|
+
|
430
|
+
if ++points >= options.eventLag.minSamples and avg < options.eventLag.lagThreshold
|
431
|
+
@progress = 100
|
432
|
+
|
433
|
+
clearInterval interval
|
434
|
+
else
|
435
|
+
@progress = 100 * (3 / (avg + 3))
|
436
|
+
|
437
|
+
, 50
|
438
|
+
|
439
|
+
class Scaler
|
440
|
+
constructor: (@source) ->
|
441
|
+
@last = @sinceLastUpdate = 0
|
442
|
+
@rate = options.initialRate
|
443
|
+
@catchup = 0
|
444
|
+
@progress = @lastProgress = 0
|
445
|
+
|
446
|
+
if @source?
|
447
|
+
@progress = result(@source, 'progress')
|
448
|
+
|
449
|
+
tick: (frameTime, val) ->
|
450
|
+
val ?= result(@source, 'progress')
|
451
|
+
|
452
|
+
if val >= 100
|
453
|
+
@done = true
|
454
|
+
|
455
|
+
if val == @last
|
456
|
+
@sinceLastUpdate += frameTime
|
457
|
+
else
|
458
|
+
if @sinceLastUpdate
|
459
|
+
@rate = (val - @last) / @sinceLastUpdate
|
460
|
+
|
461
|
+
@catchup = (val - @progress) / options.catchupTime
|
462
|
+
|
463
|
+
@sinceLastUpdate = 0
|
464
|
+
@last = val
|
465
|
+
|
466
|
+
if val > @progress
|
467
|
+
# After we've got a datapoint, we have catchupTime to
|
468
|
+
# get the progress bar to reflect that new data
|
469
|
+
@progress += @catchup * frameTime
|
470
|
+
|
471
|
+
scaling = (1 - Math.pow(@progress / 100, options.easeFactor))
|
472
|
+
|
473
|
+
# Based on the rate of the last update, we preemptively update
|
474
|
+
# the progress bar, scaling it so it can never hit 100% until we
|
475
|
+
# know it's done.
|
476
|
+
@progress += scaling * @rate * frameTime
|
477
|
+
|
478
|
+
@progress = Math.min(@lastProgress + options.maxProgressPerFrame, @progress)
|
479
|
+
|
480
|
+
@progress = Math.max(0, @progress)
|
481
|
+
@progress = Math.min(100, @progress)
|
482
|
+
|
483
|
+
@lastProgress = @progress
|
484
|
+
|
485
|
+
@progress
|
486
|
+
|
487
|
+
sources = null
|
488
|
+
scalers = null
|
489
|
+
bar = null
|
490
|
+
uniScaler = null
|
491
|
+
animation = null
|
492
|
+
cancelAnimation = null
|
493
|
+
Pace.running = false
|
494
|
+
|
495
|
+
handlePushState = ->
|
496
|
+
if options.restartOnPushState
|
497
|
+
Pace.restart()
|
498
|
+
|
499
|
+
# We reset the bar whenever it looks like an ajax navigation has occured.
|
500
|
+
if window.history.pushState?
|
501
|
+
_pushState = window.history.pushState
|
502
|
+
window.history.pushState = ->
|
503
|
+
handlePushState()
|
504
|
+
|
505
|
+
_pushState.apply window.history, arguments
|
506
|
+
|
507
|
+
if window.history.replaceState?
|
508
|
+
_replaceState = window.history.replaceState
|
509
|
+
window.history.replaceState = ->
|
510
|
+
handlePushState()
|
511
|
+
|
512
|
+
_replaceState.apply window.history, arguments
|
513
|
+
|
514
|
+
SOURCE_KEYS =
|
515
|
+
ajax: AjaxMonitor
|
516
|
+
elements: ElementMonitor
|
517
|
+
document: DocumentMonitor
|
518
|
+
eventLag: EventLagMonitor
|
519
|
+
|
520
|
+
do init = ->
|
521
|
+
Pace.sources = sources = []
|
522
|
+
|
523
|
+
for type in ['ajax', 'elements', 'document', 'eventLag']
|
524
|
+
if options[type] isnt false
|
525
|
+
sources.push new SOURCE_KEYS[type](options[type])
|
526
|
+
|
527
|
+
for source in options.extraSources ? []
|
528
|
+
sources.push new source(options)
|
529
|
+
|
530
|
+
Pace.bar = bar = new Bar
|
531
|
+
|
532
|
+
# Each source of progress data has it's own scaler to smooth its output
|
533
|
+
scalers = []
|
534
|
+
|
535
|
+
# We have an extra scaler for the final output to keep things looking nice as we add and
|
536
|
+
# remove sources
|
537
|
+
uniScaler = new Scaler
|
538
|
+
|
539
|
+
Pace.stop = ->
|
540
|
+
Pace.running = false
|
541
|
+
|
542
|
+
bar.destroy()
|
543
|
+
|
544
|
+
# Not all browsers support cancelAnimationFrame
|
545
|
+
cancelAnimation = true
|
546
|
+
|
547
|
+
if animation?
|
548
|
+
cancelAnimationFrame? animation
|
549
|
+
animation = null
|
550
|
+
|
551
|
+
init()
|
552
|
+
|
553
|
+
Pace.restart = ->
|
554
|
+
Pace.stop()
|
555
|
+
Pace.start()
|
556
|
+
|
557
|
+
Pace.go = ->
|
558
|
+
Pace.running = true
|
559
|
+
|
560
|
+
bar.render()
|
561
|
+
|
562
|
+
cancelAnimation = false
|
563
|
+
animation = runAnimation (frameTime, enqueueNextFrame) ->
|
564
|
+
# Every source gives us a progress number from 0 - 100
|
565
|
+
# It's up to us to figure out how to turn that into a smoothly moving bar
|
566
|
+
#
|
567
|
+
# Their progress numbers can only increment. We try to interpolate
|
568
|
+
# between the numbers.
|
569
|
+
|
570
|
+
remaining = 100 - bar.progress
|
571
|
+
|
572
|
+
count = sum = 0
|
573
|
+
done = true
|
574
|
+
# A source is composed of a bunch of elements, each with a raw, unscaled progress
|
575
|
+
for source, i in sources
|
576
|
+
scalerList = scalers[i] ?= []
|
577
|
+
|
578
|
+
elements = source.elements ? [source]
|
579
|
+
|
580
|
+
# Each element is given it's own scaler, which turns its value into something
|
581
|
+
# smoothed for display
|
582
|
+
for element, j in elements
|
583
|
+
scaler = scalerList[j] ?= new Scaler element
|
584
|
+
|
585
|
+
done &= scaler.done
|
586
|
+
|
587
|
+
continue if scaler.done
|
588
|
+
|
589
|
+
count++
|
590
|
+
sum += scaler.tick(frameTime)
|
591
|
+
|
592
|
+
avg = sum / count
|
593
|
+
|
594
|
+
bar.update uniScaler.tick(frameTime, avg)
|
595
|
+
|
596
|
+
start = now()
|
597
|
+
if bar.done() or done or cancelAnimation
|
598
|
+
bar.update 100
|
599
|
+
|
600
|
+
setTimeout ->
|
601
|
+
bar.finish()
|
602
|
+
|
603
|
+
Pace.running = false
|
604
|
+
, Math.max(options.ghostTime, Math.min(options.minTime, now() - start))
|
605
|
+
else
|
606
|
+
enqueueNextFrame()
|
607
|
+
|
608
|
+
Pace.start = (_options) ->
|
609
|
+
extend options, _options
|
610
|
+
|
611
|
+
Pace.running = true
|
612
|
+
|
613
|
+
try
|
614
|
+
bar.render()
|
615
|
+
catch NoTargetError
|
616
|
+
|
617
|
+
# It's usually possible to render a bit before the document declares itself ready
|
618
|
+
if not document.querySelector('.pace')
|
619
|
+
setTimeout Pace.start, 50
|
620
|
+
else
|
621
|
+
Pace.go()
|
622
|
+
|
623
|
+
if typeof define is 'function' and define.amd
|
624
|
+
# AMD
|
625
|
+
define -> Pace
|
626
|
+
else if typeof exports is 'object'
|
627
|
+
# CommonJS
|
628
|
+
module.exports = Pace
|
629
|
+
else
|
630
|
+
# Global
|
631
|
+
if options.startOnPageLoad
|
632
|
+
Pace.start()
|