/* nprogress, (c) 2013, 2014 rico sta. cruz - http://ricostacruz.com/nprogress * @license mit */ ;(function(root, factory) { if (typeof define === 'function' && define.amd) { define(factory); } else if (typeof exports === 'object') { module.exports = factory(); } else { root.nprogress = factory(); } })(this, function() { var nprogress = {}; nprogress.version = '0.2.0'; var settings = nprogress.settings = { minimum: 0.08, easing: 'ease', positionusing: '', speed: 200, trickle: true, tricklerate: 0.02, tricklespeed: 800, showspinner: true, barselector: '[role="bar"]', spinnerselector: '[role="spinner"]', parent: 'body', template: '
' }; /** * updates configuration. * * nprogress.configure({ * minimum: 0.1 * }); */ nprogress.configure = function(options) { var key, value; for (key in options) { value = options[key]; if (value !== undefined && options.hasownproperty(key)) settings[key] = value; } return this; }; /** * last number. */ nprogress.status = null; /** * sets the progress bar status, where `n` is a number from `0.0` to `1.0`. * * nprogress.set(0.4); * nprogress.set(1.0); */ nprogress.set = function(n) { var started = nprogress.isstarted(); n = clamp(n, settings.minimum, 1); nprogress.status = (n === 1 ? null : n); var progress = nprogress.render(!started), bar = progress.queryselector(settings.barselector), speed = settings.speed, ease = settings.easing; progress.offsetwidth; /* repaint */ queue(function(next) { // set positionusing if it hasn't already been set if (settings.positionusing === '') settings.positionusing = nprogress.getpositioningcss(); // add transition css(bar, barpositioncss(n, speed, ease)); if (n === 1) { // fade out css(progress, { transition: 'none', opacity: 1 }); progress.offsetwidth; /* repaint */ settimeout(function() { css(progress, { transition: 'all ' + speed + 'ms linear', opacity: 0 }); settimeout(function() { nprogress.remove(); next(); }, speed); }, speed); } else { settimeout(next, speed); } }); return this; }; nprogress.isstarted = function() { return typeof nprogress.status === 'number'; }; /** * shows the progress bar. * this is the same as setting the status to 0%, except that it doesn't go backwards. * * nprogress.start(); * */ nprogress.start = function() { if (!nprogress.status) nprogress.set(0); var work = function() { settimeout(function() { if (!nprogress.status) return; nprogress.trickle(); work(); }, settings.tricklespeed); }; if (settings.trickle) work(); return this; }; /** * hides the progress bar. * this is the *sort of* the same as setting the status to 100%, with the * difference being `done()` makes some placebo effect of some realistic motion. * * nprogress.done(); * * if `true` is passed, it will show the progress bar even if its hidden. * * nprogress.done(true); */ nprogress.done = function(force) { if (!force && !nprogress.status) return this; return nprogress.inc(0.3 + 0.5 * math.random()).set(1); }; /** * increments by a random amount. */ nprogress.inc = function(amount) { var n = nprogress.status; if (!n) { return nprogress.start(); } else { if (typeof amount !== 'number') { amount = (1 - n) * clamp(math.random() * n, 0.1, 0.95); } n = clamp(n + amount, 0, 0.994); return nprogress.set(n); } }; nprogress.trickle = function() { return nprogress.inc(math.random() * settings.tricklerate); }; /** * waits for all supplied jquery promises and * increases the progress as the promises resolve. * * @param $promise jquery promise */ (function() { var initial = 0, current = 0; nprogress.promise = function($promise) { if (!$promise || $promise.state() === "resolved") { return this; } if (current === 0) { nprogress.start(); } initial++; current++; $promise.always(function() { current--; if (current === 0) { initial = 0; nprogress.done(); } else { nprogress.set((initial - current) / initial); } }); return this; }; })(); /** * (internal) renders the progress bar markup based on the `template` * setting. */ nprogress.render = function(fromstart) { if (nprogress.isrendered()) return document.getelementbyid('nprogress'); addclass(document.documentelement, 'nprogress-busy'); var progress = document.createelement('div'); progress.id = 'nprogress'; progress.innerhtml = settings.template; var bar = progress.queryselector(settings.barselector), perc = fromstart ? '-100' : tobarperc(nprogress.status || 0), parent = document.queryselector(settings.parent), spinner; css(bar, { transition: 'all 0 linear', transform: 'translate3d(' + perc + '%,0,0)' }); if (!settings.showspinner) { spinner = progress.queryselector(settings.spinnerselector); spinner && removeelement(spinner); } if (parent != document.body) { addclass(parent, 'nprogress-custom-parent'); } parent.appendchild(progress); return progress; }; /** * removes the element. opposite of render(). */ nprogress.remove = function() { removeclass(document.documentelement, 'nprogress-busy'); removeclass(document.queryselector(settings.parent), 'nprogress-custom-parent'); var progress = document.getelementbyid('nprogress'); progress && removeelement(progress); }; /** * checks if the progress bar is rendered. */ nprogress.isrendered = function() { return !!document.getelementbyid('nprogress'); }; /** * determine which positioning css rule to use. */ nprogress.getpositioningcss = function() { // sniff on document.body.style var bodystyle = document.body.style; // sniff prefixes var vendorprefix = ('webkittransform' in bodystyle) ? 'webkit' : ('moztransform' in bodystyle) ? 'moz' : ('mstransform' in bodystyle) ? 'ms' : ('otransform' in bodystyle) ? 'o' : ''; if (vendorprefix + 'perspective' in bodystyle) { // modern browsers with 3d support, e.g. webkit, ie10 return 'translate3d'; } else if (vendorprefix + 'transform' in bodystyle) { // browsers without 3d support, e.g. ie9 return 'translate'; } else { // browsers without translate() support, e.g. ie7-8 return 'margin'; } }; /** * helpers */ function clamp(n, min, max) { if (n < min) return min; if (n > max) return max; return n; } /** * (internal) converts a percentage (`0..1`) to a bar translatex * percentage (`-100%..0%`). */ function tobarperc(n) { return (-1 + n) * 100; } /** * (internal) returns the correct css for changing the bar's * position given an n percentage, and speed and ease from settings */ function barpositioncss(n, speed, ease) { var barcss; if (settings.positionusing === 'translate3d') { barcss = { transform: 'translate3d('+tobarperc(n)+'%,0,0)' }; } else if (settings.positionusing === 'translate') { barcss = { transform: 'translate('+tobarperc(n)+'%,0)' }; } else { barcss = { 'margin-left': tobarperc(n)+'%' }; } barcss.transition = 'all '+speed+'ms '+ease; return barcss; } /** * (internal) queues a function to be executed. */ var queue = (function() { var pending = []; function next() { var fn = pending.shift(); if (fn) { fn(next); } } return function(fn) { pending.push(fn); if (pending.length == 1) next(); }; })(); /** * (internal) applies css properties to an element, similar to the jquery * css method. * * while this helper does assist with vendor prefixed property names, it * does not perform any manipulation of values prior to setting styles. */ var css = (function() { var cssprefixes = [ 'webkit', 'o', 'moz', 'ms' ], cssprops = {}; function camelcase(string) { return string.replace(/^-ms-/, 'ms-').replace(/-([\da-z])/gi, function(match, letter) { return letter.touppercase(); }); } function getvendorprop(name) { var style = document.body.style; if (name in style) return name; var i = cssprefixes.length, capname = name.charat(0).touppercase() + name.slice(1), vendorname; while (i--) { vendorname = cssprefixes[i] + capname; if (vendorname in style) return vendorname; } return name; } function getstyleprop(name) { name = camelcase(name); return cssprops[name] || (cssprops[name] = getvendorprop(name)); } function applycss(element, prop, value) { prop = getstyleprop(prop); element.style[prop] = value; } return function(element, properties) { var args = arguments, prop, value; if (args.length == 2) { for (prop in properties) { value = properties[prop]; if (value !== undefined && properties.hasownproperty(prop)) applycss(element, prop, value); } } else { applycss(element, args[1], args[2]); } } })(); /** * (internal) determines if an element or space separated list of class names contains a class name. */ function hasclass(element, name) { var list = typeof element == 'string' ? element : classlist(element); return list.indexof(' ' + name + ' ') >= 0; } /** * (internal) adds a class to an element. */ function addclass(element, name) { var oldlist = classlist(element), newlist = oldlist + name; if (hasclass(oldlist, name)) return; // trim the opening space. element.classname = newlist.substring(1); } /** * (internal) removes a class from an element. */ function removeclass(element, name) { var oldlist = classlist(element), newlist; if (!hasclass(element, name)) return; // replace the class name. newlist = oldlist.replace(' ' + name + ' ', ' '); // trim the opening and closing spaces. element.classname = newlist.substring(1, newlist.length - 1); } /** * (internal) gets a space separated list of the class names on the element. * the list is wrapped with a single space on each end to facilitate finding * matches within the list. */ function classlist(element) { return (' ' + (element.classname || '') + ' ').replace(/\s+/gi, ' '); } /** * (internal) removes an element from the dom. */ function removeelement(element) { element && element.parentnode && element.parentnode.removechild(element); } return nprogress; });