mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-07-09 06:28:28 +03:00
408 lines
12 KiB
JavaScript
408 lines
12 KiB
JavaScript
/**
|
|
* Progress.js v0.1.0
|
|
* https://github.com/usablica/progress.js
|
|
* MIT licensed
|
|
*
|
|
* Copyright (C) 2013 usabli.ca - Afshin Mehrabani (@afshinmeh)
|
|
*/
|
|
|
|
(exports => {
|
|
//Default config/variables
|
|
var VERSION = '0.1.0';
|
|
|
|
/**
|
|
* ProgressJs main class
|
|
*
|
|
* @class ProgressJs
|
|
*/
|
|
function ProgressJs(obj) {
|
|
|
|
if (typeof obj.length != 'undefined') {
|
|
this._targetElement = obj;
|
|
} else {
|
|
this._targetElement = [obj];
|
|
}
|
|
|
|
if (typeof window._progressjsId === 'undefined')
|
|
window._progressjsId = 1;
|
|
|
|
if (typeof window._progressjsIntervals === 'undefined')
|
|
window._progressjsIntervals = {};
|
|
|
|
this._options = {
|
|
//progress bar theme
|
|
theme: 'rainloop',
|
|
//overlay mode makes an overlay layer in the target element
|
|
overlayMode: false,
|
|
//to consider CSS3 transitions in events
|
|
considerTransition: true
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Start progress for specific element(s)
|
|
*
|
|
* @api private
|
|
* @method _createContainer
|
|
*/
|
|
function _startProgress() {
|
|
|
|
//call onBeforeStart callback
|
|
if (typeof this._onBeforeStartCallback != 'undefined') {
|
|
this._onBeforeStartCallback.call(this);
|
|
}
|
|
|
|
//create the container for progress bar
|
|
_createContainer.call(this);
|
|
|
|
for (var i = 0, elmsLength = this._targetElement.length; i < elmsLength; i++) {
|
|
_setProgress.call(this, this._targetElement[i]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set progress bar for specific element
|
|
*
|
|
* @api private
|
|
* @method _setProgress
|
|
* @param {Object} targetElement
|
|
*/
|
|
function _setProgress(targetElement) {
|
|
|
|
//if the target element already as `data-progressjs`, ignore the init
|
|
if (targetElement.hasAttribute("data-progressjs"))
|
|
return;
|
|
|
|
//get target element position
|
|
var targetElementOffset = _getOffset.call(this, targetElement);
|
|
|
|
targetElement.setAttribute("data-progressjs", window._progressjsId);
|
|
|
|
var progressElementContainer = document.createElement('div');
|
|
progressElementContainer.className = 'progressjs-progress progressjs-theme-' + this._options.theme;
|
|
|
|
|
|
//set the position percent elements, it depends on targetElement tag
|
|
if (targetElement.tagName.toLowerCase() === 'body') {
|
|
progressElementContainer.style.position = 'fixed';
|
|
} else {
|
|
progressElementContainer.style.position = 'absolute';
|
|
}
|
|
|
|
progressElementContainer.setAttribute("data-progressjs", window._progressjsId);
|
|
var progressElement = document.createElement("div");
|
|
progressElement.className = "progressjs-inner";
|
|
|
|
//create an element for current percent of progress bar
|
|
var progressPercentElement = document.createElement('div');
|
|
progressPercentElement.className = "progressjs-percent";
|
|
progressPercentElement.innerHTML = "1%";
|
|
|
|
progressElement.appendChild(progressPercentElement);
|
|
|
|
if (this._options.overlayMode && targetElement.tagName.toLowerCase() === 'body') {
|
|
//if we have `body` for target element and also overlay mode is enable, we should use a different
|
|
//position for progress bar container element
|
|
progressElementContainer.style.left = 0;
|
|
progressElementContainer.style.right = 0;
|
|
progressElementContainer.style.top = 0;
|
|
progressElementContainer.style.bottom = 0;
|
|
} else {
|
|
//set progress bar container size and offset
|
|
progressElementContainer.style.left = targetElementOffset.left + 'px';
|
|
progressElementContainer.style.top = targetElementOffset.top + 'px';
|
|
progressElementContainer.style.width = targetElementOffset.width + 'px';
|
|
|
|
if (this._options.overlayMode) {
|
|
progressElementContainer.style.height = targetElementOffset.height + 'px';
|
|
}
|
|
}
|
|
|
|
progressElementContainer.appendChild(progressElement);
|
|
|
|
//append the element to container
|
|
var container = document.querySelector('.progressjs-container');
|
|
container.appendChild(progressElementContainer);
|
|
|
|
_setPercentFor(targetElement, 1);
|
|
|
|
//and increase the progressId
|
|
++window._progressjsId;
|
|
}
|
|
|
|
/**
|
|
* Set percent for all elements
|
|
*
|
|
* @api private
|
|
* @method _setPercent
|
|
* @param {Number} percent
|
|
*/
|
|
function _setPercent(percent) {
|
|
for (var i = 0, elmsLength = this._targetElement.length; i < elmsLength; i++) {
|
|
_setPercentFor.call(this, this._targetElement[i], percent);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set percent for specific element
|
|
*
|
|
* @api private
|
|
* @method _setPercentFor
|
|
* @param {Object} targetElement
|
|
* @param {Number} percent
|
|
*/
|
|
function _setPercentFor(targetElement, percent) {
|
|
//prevent overflow!
|
|
if (percent >= 100)
|
|
percent = 100;
|
|
|
|
if (targetElement.hasAttribute("data-progressjs")) {
|
|
//setTimeout for better CSS3 animation applying in some cases
|
|
setTimeout(function() {
|
|
|
|
var percentElement = _getPercentElement(targetElement);
|
|
percentElement.style.width = parseInt(percent) + '%';
|
|
|
|
percentElement = percentElement.querySelector(".progressjs-percent");
|
|
var existingPercent = parseInt(percentElement.innerHTML.replace('%', ''));
|
|
|
|
//start increase/decrease the percent element with animation
|
|
(function(percentElement, existingPercent, currentPercent) {
|
|
|
|
var increasement = true;
|
|
if (existingPercent > currentPercent) {
|
|
increasement = false;
|
|
}
|
|
|
|
var intervalIn = 10;
|
|
function changePercentTimer(percentElement, existingPercent, currentPercent) {
|
|
//calculate the distance between two percents
|
|
var distance = Math.abs(existingPercent - currentPercent);
|
|
if (distance < 3) {
|
|
intervalIn = 30;
|
|
} else if (distance < 20) {
|
|
intervalIn = 20;
|
|
} else {
|
|
intervalIn = 1;
|
|
}
|
|
|
|
if ((existingPercent - currentPercent) != 0) {
|
|
//set the percent
|
|
percentElement.innerHTML = (increasement ? (++existingPercent) : (--existingPercent)) + '%';
|
|
setTimeout(function() { changePercentTimer(percentElement, existingPercent, currentPercent); }, intervalIn);
|
|
}
|
|
}
|
|
|
|
changePercentTimer(percentElement, existingPercent, currentPercent);
|
|
|
|
})(percentElement, existingPercent, parseInt(percent));
|
|
|
|
}, 50);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the progress bar element
|
|
*
|
|
* @api private
|
|
* @method _getPercentElement
|
|
* @param {Object} targetElement
|
|
*/
|
|
function _getPercentElement(targetElement) {
|
|
var progressjsId = parseInt(targetElement.getAttribute('data-progressjs'));
|
|
return document.querySelector('.progressjs-container > .progressjs-progress[data-progressjs="' + progressjsId + '"] > .progressjs-inner');
|
|
}
|
|
|
|
/**
|
|
* Close and remove progress bar
|
|
*
|
|
* @api private
|
|
* @method _end
|
|
*/
|
|
function _end() {
|
|
|
|
//call onBeforeEnd callback
|
|
if (typeof this._onBeforeEndCallback != 'undefined') {
|
|
if (this._options.considerTransition === true) {
|
|
//we can safety assume that all layers would be the same, so `this._targetElement[0]` is the same as `this._targetElement[1]`
|
|
_getPercentElement(this._targetElement[0]).addEventListener(whichTransitionEvent(), this._onBeforeEndCallback, false);
|
|
} else {
|
|
this._onBeforeEndCallback.call(this);
|
|
}
|
|
}
|
|
|
|
var progressjsId = parseInt(this._targetElement[0].getAttribute('data-progressjs'));
|
|
|
|
for (var i = 0, elmsLength = this._targetElement.length; i < elmsLength; i++) {
|
|
var currentElement = this._targetElement[i];
|
|
var percentElement = _getPercentElement(currentElement);
|
|
|
|
if (!percentElement)
|
|
return;
|
|
|
|
var existingPercent = parseInt(percentElement.style.width.replace('%', ''));
|
|
|
|
var timeoutSec = 1;
|
|
if (existingPercent < 100) {
|
|
_setPercentFor.call(this, currentElement, 100);
|
|
timeoutSec = 500;
|
|
}
|
|
|
|
//I believe I should handle this situation with eventListener and `transitionend` event but I'm not sure
|
|
//about compatibility with IEs. Should be fixed in further versions.
|
|
(function(percentElement, currentElement) {
|
|
setTimeout(function() {
|
|
percentElement.parentNode.className += " progressjs-end";
|
|
|
|
setTimeout(function() {
|
|
//remove the percent element from page
|
|
percentElement.parentNode.parentNode.removeChild(percentElement.parentNode);
|
|
//and remove the attribute
|
|
currentElement.removeAttribute("data-progressjs");
|
|
}, 1000);
|
|
}, timeoutSec);
|
|
})(percentElement, currentElement);
|
|
}
|
|
|
|
//clean the setInterval for autoIncrease function
|
|
if (window._progressjsIntervals[progressjsId]) {
|
|
//`delete` keyword has some problems in IE
|
|
try {
|
|
clearInterval(window._progressjsIntervals[progressjsId]);
|
|
window._progressjsIntervals[progressjsId] = null;
|
|
delete window._progressjsIntervals[progressjsId];
|
|
} catch(ex) { }
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create the progress bar container
|
|
*
|
|
* @api private
|
|
* @method _createContainer
|
|
*/
|
|
function _createContainer() {
|
|
//first check if we have an container already, we don't need to create it again
|
|
if (!document.querySelector(".progressjs-container")) {
|
|
var containerElement = document.createElement("div");
|
|
containerElement.className = "progressjs-container";
|
|
document.body.appendChild(containerElement);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get an element position on the page
|
|
* Thanks to `meouw`: http://stackoverflow.com/a/442474/375966
|
|
*
|
|
* @api private
|
|
* @method _getOffset
|
|
* @param {Object} element
|
|
* @returns Element's position info
|
|
*/
|
|
function _getOffset(element) {
|
|
var elementPosition = {};
|
|
|
|
if (element.tagName.toLowerCase() === 'body') {
|
|
//set width
|
|
elementPosition.width = element.clientWidth;
|
|
//set height
|
|
elementPosition.height = element.clientHeight;
|
|
} else {
|
|
//set width
|
|
elementPosition.width = element.offsetWidth;
|
|
//set height
|
|
elementPosition.height = element.offsetHeight;
|
|
}
|
|
|
|
//calculate element top and left
|
|
var _x = 0;
|
|
var _y = 0;
|
|
while (element && !isNaN(element.offsetLeft) && !isNaN(element.offsetTop)) {
|
|
_x += element.offsetLeft;
|
|
_y += element.offsetTop;
|
|
element = element.offsetParent;
|
|
}
|
|
//set top
|
|
elementPosition.top = _y;
|
|
//set left
|
|
elementPosition.left = _x;
|
|
|
|
return elementPosition;
|
|
}
|
|
|
|
var progressJs = function (targetElm) {
|
|
if (typeof (targetElm) === 'object') {
|
|
//Ok, create a new instance
|
|
return new ProgressJs(targetElm);
|
|
}
|
|
if (typeof (targetElm) === 'string') {
|
|
//select the target element with query selector
|
|
var targetElement = document.querySelectorAll(targetElm);
|
|
|
|
if (targetElement) {
|
|
return new ProgressJs(targetElement);
|
|
} else {
|
|
throw new Error('There is no element with given selector.');
|
|
}
|
|
}
|
|
return new ProgressJs(document.body);
|
|
};
|
|
|
|
/**
|
|
* Get correct transition callback
|
|
* Thanks @webinista: http://stackoverflow.com/a/9090128/375966
|
|
*
|
|
* @returns transition name
|
|
*/
|
|
function whichTransitionEvent() {
|
|
var t,
|
|
el = document.createElement('fakeelement'),
|
|
transitions = {
|
|
'transition': 'transitionend',
|
|
'OTransition': 'oTransitionEnd',
|
|
'MozTransition': 'transitionend',
|
|
'WebkitTransition': 'webkitTransitionEnd'
|
|
}
|
|
|
|
for (t in transitions) {
|
|
if (el.style[t] !== undefined) {
|
|
return transitions[t];
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Current ProgressJs version
|
|
*
|
|
* @property version
|
|
* @type String
|
|
*/
|
|
progressJs.version = VERSION;
|
|
|
|
//Prototype
|
|
progressJs.fn = ProgressJs.prototype = {
|
|
start: function() {
|
|
_startProgress.call(this);
|
|
return this;
|
|
},
|
|
set: function(percent) {
|
|
_setPercent.call(this, percent);
|
|
return this;
|
|
},
|
|
end: function() {
|
|
_end.call(this);
|
|
return this;
|
|
},
|
|
onbeforeend: function(providedCallback) {
|
|
if (typeof (providedCallback) === 'function') {
|
|
this._onBeforeEndCallback = providedCallback;
|
|
} else {
|
|
throw new Error('Provided callback for onbeforeend was not a function');
|
|
}
|
|
return this;
|
|
}
|
|
};
|
|
|
|
exports.progressJs = progressJs;
|
|
return progressJs;
|
|
})(this);
|