|
1
|
|
|
/* NProgress, (c) 2013, 2014 Rico Sta. Cruz - http://ricostacruz.com/nprogress |
|
2
|
|
|
* @license MIT */ |
|
3
|
|
|
|
|
4
|
|
|
;(function(root, factory) { |
|
5
|
|
|
|
|
6
|
|
|
if (typeof define === 'function' && define.amd) { |
|
7
|
|
|
define(factory); |
|
8
|
|
|
} else if (typeof exports === 'object') { |
|
9
|
|
|
module.exports = factory(); |
|
10
|
|
|
} else { |
|
11
|
|
|
root.NProgress = factory(); |
|
12
|
|
|
} |
|
13
|
|
|
|
|
14
|
|
|
})(this, function() { |
|
15
|
|
|
var NProgress = {}; |
|
16
|
|
|
|
|
17
|
|
|
NProgress.version = '0.1.6'; |
|
18
|
|
|
|
|
19
|
|
|
var Settings = NProgress.settings = { |
|
20
|
|
|
minimum: 0.08, |
|
21
|
|
|
easing: 'ease', |
|
22
|
|
|
positionUsing: '', |
|
23
|
|
|
speed: 200, |
|
24
|
|
|
trickle: true, |
|
25
|
|
|
trickleRate: 0.02, |
|
26
|
|
|
trickleSpeed: 800, |
|
27
|
|
|
showSpinner: true, |
|
28
|
|
|
barSelector: '[role="bar"]', |
|
29
|
|
|
spinnerSelector: '[role="spinner"]', |
|
30
|
|
|
parent: 'body', |
|
31
|
|
|
template: '<div class="bar" role="bar"><div class="peg"></div></div><div class="spinner" role="spinner"><div class="spinner-icon"></div></div>' |
|
32
|
|
|
}; |
|
33
|
|
|
|
|
34
|
|
|
/** |
|
35
|
|
|
* Updates configuration. |
|
36
|
|
|
* |
|
37
|
|
|
* NProgress.configure({ |
|
38
|
|
|
* minimum: 0.1 |
|
39
|
|
|
* }); |
|
40
|
|
|
*/ |
|
41
|
|
|
NProgress.configure = function(options) { |
|
42
|
|
|
var key, value; |
|
43
|
|
|
for (key in options) { |
|
44
|
|
|
value = options[key]; |
|
45
|
|
|
if (value !== undefined && options.hasOwnProperty(key)) Settings[key] = value; |
|
46
|
|
|
} |
|
47
|
|
|
|
|
48
|
|
|
return this; |
|
49
|
|
|
}; |
|
50
|
|
|
|
|
51
|
|
|
/** |
|
52
|
|
|
* Last number. |
|
53
|
|
|
*/ |
|
54
|
|
|
|
|
55
|
|
|
NProgress.status = null; |
|
56
|
|
|
|
|
57
|
|
|
/** |
|
58
|
|
|
* Sets the progress bar status, where `n` is a number from `0.0` to `1.0`. |
|
59
|
|
|
* |
|
60
|
|
|
* NProgress.set(0.4); |
|
61
|
|
|
* NProgress.set(1.0); |
|
62
|
|
|
*/ |
|
63
|
|
|
|
|
64
|
|
|
NProgress.set = function(n) { |
|
65
|
|
|
var started = NProgress.isStarted(); |
|
66
|
|
|
|
|
67
|
|
|
n = clamp(n, Settings.minimum, 1); |
|
68
|
|
|
NProgress.status = (n === 1 ? null : n); |
|
69
|
|
|
|
|
70
|
|
|
var progress = NProgress.render(!started), |
|
71
|
|
|
bar = progress.querySelector(Settings.barSelector), |
|
72
|
|
|
speed = Settings.speed, |
|
73
|
|
|
ease = Settings.easing; |
|
74
|
|
|
|
|
75
|
|
|
progress.offsetWidth; /* Repaint */ |
|
76
|
|
|
|
|
77
|
|
|
queue(function(next) { |
|
78
|
|
|
// Set positionUsing if it hasn't already been set |
|
79
|
|
|
if (Settings.positionUsing === '') Settings.positionUsing = NProgress.getPositioningCSS(); |
|
80
|
|
|
|
|
81
|
|
|
// Add transition |
|
82
|
|
|
css(bar, barPositionCSS(n, speed, ease)); |
|
83
|
|
|
|
|
84
|
|
|
if (n === 1) { |
|
85
|
|
|
// Fade out |
|
86
|
|
|
css(progress, { |
|
87
|
|
|
transition: 'none', |
|
88
|
|
|
opacity: 1 |
|
89
|
|
|
}); |
|
90
|
|
|
progress.offsetWidth; /* Repaint */ |
|
91
|
|
|
|
|
92
|
|
|
setTimeout(function() { |
|
93
|
|
|
css(progress, { |
|
94
|
|
|
transition: 'all ' + speed + 'ms linear', |
|
95
|
|
|
opacity: 0 |
|
96
|
|
|
}); |
|
97
|
|
|
setTimeout(function() { |
|
98
|
|
|
NProgress.remove(); |
|
99
|
|
|
next(); |
|
100
|
|
|
}, speed); |
|
101
|
|
|
}, speed); |
|
102
|
|
|
} else { |
|
103
|
|
|
setTimeout(next, speed); |
|
104
|
|
|
} |
|
105
|
|
|
}); |
|
106
|
|
|
|
|
107
|
|
|
return this; |
|
108
|
|
|
}; |
|
109
|
|
|
|
|
110
|
|
|
NProgress.isStarted = function() { |
|
111
|
|
|
return typeof NProgress.status === 'number'; |
|
112
|
|
|
}; |
|
113
|
|
|
|
|
114
|
|
|
/** |
|
115
|
|
|
* Shows the progress bar. |
|
116
|
|
|
* This is the same as setting the status to 0%, except that it doesn't go backwards. |
|
117
|
|
|
* |
|
118
|
|
|
* NProgress.start(); |
|
119
|
|
|
* |
|
120
|
|
|
*/ |
|
121
|
|
|
NProgress.start = function() { |
|
122
|
|
|
if (!NProgress.status) NProgress.set(0); |
|
123
|
|
|
|
|
124
|
|
|
var work = function() { |
|
125
|
|
|
setTimeout(function() { |
|
126
|
|
|
if (!NProgress.status) return; |
|
127
|
|
|
NProgress.trickle(); |
|
128
|
|
|
work(); |
|
129
|
|
|
}, Settings.trickleSpeed); |
|
130
|
|
|
}; |
|
131
|
|
|
|
|
132
|
|
|
if (Settings.trickle) work(); |
|
133
|
|
|
|
|
134
|
|
|
return this; |
|
135
|
|
|
}; |
|
136
|
|
|
|
|
137
|
|
|
/** |
|
138
|
|
|
* Hides the progress bar. |
|
139
|
|
|
* This is the *sort of* the same as setting the status to 100%, with the |
|
140
|
|
|
* difference being `done()` makes some placebo effect of some realistic motion. |
|
141
|
|
|
* |
|
142
|
|
|
* NProgress.done(); |
|
143
|
|
|
* |
|
144
|
|
|
* If `true` is passed, it will show the progress bar even if its hidden. |
|
145
|
|
|
* |
|
146
|
|
|
* NProgress.done(true); |
|
147
|
|
|
*/ |
|
148
|
|
|
|
|
149
|
|
|
NProgress.done = function(force) { |
|
150
|
|
|
if (!force && !NProgress.status) return this; |
|
151
|
|
|
|
|
152
|
|
|
return NProgress.inc(0.3 + 0.5 * Math.random()).set(1); |
|
153
|
|
|
}; |
|
154
|
|
|
|
|
155
|
|
|
/** |
|
156
|
|
|
* Increments by a random amount. |
|
157
|
|
|
*/ |
|
158
|
|
|
|
|
159
|
|
|
NProgress.inc = function(amount) { |
|
160
|
|
|
var n = NProgress.status; |
|
161
|
|
|
|
|
162
|
|
|
if (!n) { |
|
163
|
|
|
return NProgress.start(); |
|
164
|
|
|
} else { |
|
165
|
|
|
if (typeof amount !== 'number') { |
|
166
|
|
|
amount = (1 - n) * clamp(Math.random() * n, 0.1, 0.95); |
|
167
|
|
|
} |
|
168
|
|
|
|
|
169
|
|
|
n = clamp(n + amount, 0, 0.994); |
|
170
|
|
|
return NProgress.set(n); |
|
171
|
|
|
} |
|
172
|
|
|
}; |
|
173
|
|
|
|
|
174
|
|
|
NProgress.trickle = function() { |
|
175
|
|
|
return NProgress.inc(Math.random() * Settings.trickleRate); |
|
176
|
|
|
}; |
|
177
|
|
|
|
|
178
|
|
|
/** |
|
179
|
|
|
* Waits for all supplied jQuery promises and |
|
180
|
|
|
* increases the progress as the promises resolve. |
|
181
|
|
|
* |
|
182
|
|
|
* @param $promise jQUery Promise |
|
183
|
|
|
*/ |
|
184
|
|
|
(function() { |
|
185
|
|
|
var initial = 0, current = 0; |
|
186
|
|
|
|
|
187
|
|
|
NProgress.promise = function($promise) { |
|
188
|
|
|
if (!$promise || $promise.state() == "resolved") { |
|
189
|
|
|
return this; |
|
190
|
|
|
} |
|
191
|
|
|
|
|
192
|
|
|
if (current == 0) { |
|
193
|
|
|
NProgress.start(); |
|
194
|
|
|
} |
|
195
|
|
|
|
|
196
|
|
|
initial++; |
|
197
|
|
|
current++; |
|
198
|
|
|
|
|
199
|
|
|
$promise.always(function() { |
|
200
|
|
|
current--; |
|
201
|
|
|
if (current == 0) { |
|
202
|
|
|
initial = 0; |
|
203
|
|
|
NProgress.done(); |
|
204
|
|
|
} else { |
|
205
|
|
|
NProgress.set((initial - current) / initial); |
|
206
|
|
|
} |
|
207
|
|
|
}); |
|
208
|
|
|
|
|
209
|
|
|
return this; |
|
210
|
|
|
}; |
|
211
|
|
|
|
|
212
|
|
|
})(); |
|
213
|
|
|
|
|
214
|
|
|
/** |
|
215
|
|
|
* (Internal) renders the progress bar markup based on the `template` |
|
216
|
|
|
* setting. |
|
217
|
|
|
*/ |
|
218
|
|
|
|
|
219
|
|
|
NProgress.render = function(fromStart) { |
|
220
|
|
|
if (NProgress.isRendered()) return document.getElementById('nprogress'); |
|
221
|
|
|
|
|
222
|
|
|
addClass(document.documentElement, 'nprogress-busy'); |
|
223
|
|
|
|
|
224
|
|
|
var progress = document.createElement('div'); |
|
225
|
|
|
progress.id = 'nprogress'; |
|
226
|
|
|
progress.innerHTML = Settings.template; |
|
227
|
|
|
|
|
228
|
|
|
var bar = progress.querySelector(Settings.barSelector), |
|
229
|
|
|
perc = fromStart ? '-100' : toBarPerc(NProgress.status || 0), |
|
230
|
|
|
parent = document.querySelector(Settings.parent), |
|
231
|
|
|
spinner; |
|
232
|
|
|
|
|
233
|
|
|
css(bar, { |
|
234
|
|
|
transition: 'all 0 linear', |
|
235
|
|
|
transform: 'translate3d(' + perc + '%,0,0)' |
|
236
|
|
|
}); |
|
237
|
|
|
|
|
238
|
|
|
if (!Settings.showSpinner) { |
|
239
|
|
|
spinner = progress.querySelector(Settings.spinnerSelector); |
|
240
|
|
|
spinner && removeElement(spinner); |
|
241
|
|
|
} |
|
242
|
|
|
|
|
243
|
|
|
if (parent != document.body) { |
|
244
|
|
|
addClass(parent, 'nprogress-custom-parent'); |
|
245
|
|
|
} |
|
246
|
|
|
|
|
247
|
|
|
parent.appendChild(progress); |
|
248
|
|
|
return progress; |
|
249
|
|
|
}; |
|
250
|
|
|
|
|
251
|
|
|
/** |
|
252
|
|
|
* Removes the element. Opposite of render(). |
|
253
|
|
|
*/ |
|
254
|
|
|
|
|
255
|
|
|
NProgress.remove = function() { |
|
256
|
|
|
removeClass(document.documentElement, 'nprogress-busy'); |
|
257
|
|
|
removeClass(document.querySelector(Settings.parent), 'nprogress-custom-parent') |
|
258
|
|
|
var progress = document.getElementById('nprogress'); |
|
259
|
|
|
progress && removeElement(progress); |
|
260
|
|
|
}; |
|
261
|
|
|
|
|
262
|
|
|
/** |
|
263
|
|
|
* Checks if the progress bar is rendered. |
|
264
|
|
|
*/ |
|
265
|
|
|
|
|
266
|
|
|
NProgress.isRendered = function() { |
|
267
|
|
|
return !!document.getElementById('nprogress'); |
|
268
|
|
|
}; |
|
269
|
|
|
|
|
270
|
|
|
/** |
|
271
|
|
|
* Determine which positioning CSS rule to use. |
|
272
|
|
|
*/ |
|
273
|
|
|
|
|
274
|
|
|
NProgress.getPositioningCSS = function() { |
|
275
|
|
|
// Sniff on document.body.style |
|
276
|
|
|
var bodyStyle = document.body.style; |
|
277
|
|
|
|
|
278
|
|
|
// Sniff prefixes |
|
279
|
|
|
var vendorPrefix = ('WebkitTransform' in bodyStyle) ? 'Webkit' : |
|
280
|
|
|
('MozTransform' in bodyStyle) ? 'Moz' : |
|
281
|
|
|
('msTransform' in bodyStyle) ? 'ms' : |
|
282
|
|
|
('OTransform' in bodyStyle) ? 'O' : ''; |
|
283
|
|
|
|
|
284
|
|
|
if (vendorPrefix + 'Perspective' in bodyStyle) { |
|
285
|
|
|
// Modern browsers with 3D support, e.g. Webkit, IE10 |
|
286
|
|
|
return 'translate3d'; |
|
287
|
|
|
} else if (vendorPrefix + 'Transform' in bodyStyle) { |
|
288
|
|
|
// Browsers without 3D support, e.g. IE9 |
|
289
|
|
|
return 'translate'; |
|
290
|
|
|
} else { |
|
291
|
|
|
// Browsers without translate() support, e.g. IE7-8 |
|
292
|
|
|
return 'margin'; |
|
293
|
|
|
} |
|
294
|
|
|
}; |
|
295
|
|
|
|
|
296
|
|
|
/** |
|
297
|
|
|
* Helpers |
|
298
|
|
|
*/ |
|
299
|
|
|
|
|
300
|
|
|
function clamp(n, min, max) { |
|
301
|
|
|
if (n < min) return min; |
|
302
|
|
|
if (n > max) return max; |
|
303
|
|
|
return n; |
|
304
|
|
|
} |
|
305
|
|
|
|
|
306
|
|
|
/** |
|
307
|
|
|
* (Internal) converts a percentage (`0..1`) to a bar translateX |
|
308
|
|
|
* percentage (`-100%..0%`). |
|
309
|
|
|
*/ |
|
310
|
|
|
|
|
311
|
|
|
function toBarPerc(n) { |
|
312
|
|
|
return (-1 + n) * 100; |
|
313
|
|
|
} |
|
314
|
|
|
|
|
315
|
|
|
|
|
316
|
|
|
/** |
|
317
|
|
|
* (Internal) returns the correct CSS for changing the bar's |
|
318
|
|
|
* position given an n percentage, and speed and ease from Settings |
|
319
|
|
|
*/ |
|
320
|
|
|
|
|
321
|
|
|
function barPositionCSS(n, speed, ease) { |
|
322
|
|
|
var barCSS; |
|
323
|
|
|
|
|
324
|
|
|
if (Settings.positionUsing === 'translate3d') { |
|
325
|
|
|
barCSS = { transform: 'translate3d('+toBarPerc(n)+'%,0,0)' }; |
|
326
|
|
|
} else if (Settings.positionUsing === 'translate') { |
|
327
|
|
|
barCSS = { transform: 'translate('+toBarPerc(n)+'%,0)' }; |
|
328
|
|
|
} else { |
|
329
|
|
|
barCSS = { 'margin-left': toBarPerc(n)+'%' }; |
|
330
|
|
|
} |
|
331
|
|
|
|
|
332
|
|
|
barCSS.transition = 'all '+speed+'ms '+ease; |
|
333
|
|
|
|
|
334
|
|
|
return barCSS; |
|
335
|
|
|
} |
|
336
|
|
|
|
|
337
|
|
|
/** |
|
338
|
|
|
* (Internal) Queues a function to be executed. |
|
339
|
|
|
*/ |
|
340
|
|
|
|
|
341
|
|
|
var queue = (function() { |
|
342
|
|
|
var pending = []; |
|
343
|
|
|
|
|
344
|
|
|
function next() { |
|
345
|
|
|
var fn = pending.shift(); |
|
346
|
|
|
if (fn) { |
|
347
|
|
|
fn(next); |
|
348
|
|
|
} |
|
349
|
|
|
} |
|
350
|
|
|
|
|
351
|
|
|
return function(fn) { |
|
352
|
|
|
pending.push(fn); |
|
353
|
|
|
if (pending.length == 1) next(); |
|
354
|
|
|
}; |
|
355
|
|
|
})(); |
|
356
|
|
|
|
|
357
|
|
|
/** |
|
358
|
|
|
* (Internal) Applies css properties to an element, similar to the jQuery |
|
359
|
|
|
* css method. |
|
360
|
|
|
* |
|
361
|
|
|
* While this helper does assist with vendor prefixed property names, it |
|
362
|
|
|
* does not perform any manipulation of values prior to setting styles. |
|
363
|
|
|
*/ |
|
364
|
|
|
|
|
365
|
|
|
var css = (function() { |
|
366
|
|
|
var cssPrefixes = [ 'Webkit', 'O', 'Moz', 'ms' ], |
|
367
|
|
|
cssProps = {}; |
|
368
|
|
|
|
|
369
|
|
|
function camelCase(string) { |
|
370
|
|
|
return string.replace(/^-ms-/, 'ms-').replace(/-([\da-z])/gi, function(match, letter) { |
|
371
|
|
|
return letter.toUpperCase(); |
|
372
|
|
|
}); |
|
373
|
|
|
} |
|
374
|
|
|
|
|
375
|
|
|
function getVendorProp(name) { |
|
376
|
|
|
var style = document.body.style; |
|
377
|
|
|
if (name in style) return name; |
|
378
|
|
|
|
|
379
|
|
|
var i = cssPrefixes.length, |
|
380
|
|
|
capName = name.charAt(0).toUpperCase() + name.slice(1), |
|
381
|
|
|
vendorName; |
|
382
|
|
|
while (i--) { |
|
383
|
|
|
vendorName = cssPrefixes[i] + capName; |
|
384
|
|
|
if (vendorName in style) return vendorName; |
|
385
|
|
|
} |
|
386
|
|
|
|
|
387
|
|
|
return name; |
|
388
|
|
|
} |
|
389
|
|
|
|
|
390
|
|
|
function getStyleProp(name) { |
|
391
|
|
|
name = camelCase(name); |
|
392
|
|
|
return cssProps[name] || (cssProps[name] = getVendorProp(name)); |
|
393
|
|
|
} |
|
394
|
|
|
|
|
395
|
|
|
function applyCss(element, prop, value) { |
|
396
|
|
|
prop = getStyleProp(prop); |
|
397
|
|
|
element.style[prop] = value; |
|
398
|
|
|
} |
|
399
|
|
|
|
|
400
|
|
|
return function(element, properties) { |
|
401
|
|
|
var args = arguments, |
|
402
|
|
|
prop, |
|
403
|
|
|
value; |
|
404
|
|
|
|
|
405
|
|
|
if (args.length == 2) { |
|
406
|
|
|
for (prop in properties) { |
|
407
|
|
|
value = properties[prop]; |
|
408
|
|
|
if (value !== undefined && properties.hasOwnProperty(prop)) applyCss(element, prop, value); |
|
409
|
|
|
} |
|
410
|
|
|
} else { |
|
411
|
|
|
applyCss(element, args[1], args[2]); |
|
412
|
|
|
} |
|
413
|
|
|
} |
|
414
|
|
|
})(); |
|
415
|
|
|
|
|
416
|
|
|
/** |
|
417
|
|
|
* (Internal) Determines if an element or space separated list of class names contains a class name. |
|
418
|
|
|
*/ |
|
419
|
|
|
|
|
420
|
|
|
function hasClass(element, name) { |
|
421
|
|
|
var list = typeof element == 'string' ? element : classList(element); |
|
422
|
|
|
return list.indexOf(' ' + name + ' ') >= 0; |
|
423
|
|
|
} |
|
424
|
|
|
|
|
425
|
|
|
/** |
|
426
|
|
|
* (Internal) Adds a class to an element. |
|
427
|
|
|
*/ |
|
428
|
|
|
|
|
429
|
|
|
function addClass(element, name) { |
|
430
|
|
|
var oldList = classList(element), |
|
431
|
|
|
newList = oldList + name; |
|
432
|
|
|
|
|
433
|
|
|
if (hasClass(oldList, name)) return; |
|
434
|
|
|
|
|
435
|
|
|
// Trim the opening space. |
|
436
|
|
|
element.className = newList.substring(1); |
|
437
|
|
|
} |
|
438
|
|
|
|
|
439
|
|
|
/** |
|
440
|
|
|
* (Internal) Removes a class from an element. |
|
441
|
|
|
*/ |
|
442
|
|
|
|
|
443
|
|
|
function removeClass(element, name) { |
|
444
|
|
|
var oldList = classList(element), |
|
445
|
|
|
newList; |
|
446
|
|
|
|
|
447
|
|
|
if (!hasClass(element, name)) return; |
|
448
|
|
|
|
|
449
|
|
|
// Replace the class name. |
|
450
|
|
|
newList = oldList.replace(' ' + name + ' ', ' '); |
|
451
|
|
|
|
|
452
|
|
|
// Trim the opening and closing spaces. |
|
453
|
|
|
element.className = newList.substring(1, newList.length - 1); |
|
454
|
|
|
} |
|
455
|
|
|
|
|
456
|
|
|
/** |
|
457
|
|
|
* (Internal) Gets a space separated list of the class names on the element. |
|
458
|
|
|
* The list is wrapped with a single space on each end to facilitate finding |
|
459
|
|
|
* matches within the list. |
|
460
|
|
|
*/ |
|
461
|
|
|
|
|
462
|
|
|
function classList(element) { |
|
463
|
|
|
return (' ' + (element.className || '') + ' ').replace(/\s+/gi, ' '); |
|
464
|
|
|
} |
|
465
|
|
|
|
|
466
|
|
|
/** |
|
467
|
|
|
* (Internal) Removes an element from the DOM. |
|
468
|
|
|
*/ |
|
469
|
|
|
|
|
470
|
|
|
function removeElement(element) { |
|
471
|
|
|
element && element.parentNode && element.parentNode.removeChild(element); |
|
472
|
|
|
} |
|
473
|
|
|
|
|
474
|
|
|
return NProgress; |
|
475
|
|
|
}); |
|
476
|
|
|
|
|
477
|
|
|
|