synclounge/app/js/submitButton/uiProgressButton.js

170 lines
4.9 KiB
JavaScript

/**
* uiProgressButton.js v1.0.0
* http://www.codrops.com
*
* Licensed under the MIT license.
* http://www.opensource.org/licenses/mit-license.php
*
* Copyright 2014, Codrops
* http://www.codrops.com
*/
;( function( window ) {
'use strict';
var transEndEventNames = {
'WebkitTransition': 'webkitTransitionEnd',
'MozTransition': 'transitionend',
'OTransition': 'oTransitionEnd',
'msTransition': 'MSTransitionEnd',
'transition': 'transitionend'
},
transEndEventName = transEndEventNames[ Modernizr.prefixed( 'transition' ) ],
support = { transitions : Modernizr.csstransitions };
function extend( a, b ) {
for( var key in b ) {
if( b.hasOwnProperty( key ) ) {
a[key] = b[key];
}
}
return a;
}
function SVGEl( el ) {
this.el = el;
// the path elements
this.paths = [].slice.call( this.el.querySelectorAll( 'path' ) );
// we will save both paths and its lengths in arrays
this.pathsArr = new Array();
this.lengthsArr = new Array();
this._init();
}
SVGEl.prototype._init = function() {
var self = this;
this.paths.forEach( function( path, i ) {
self.pathsArr[i] = path;
path.style.strokeDasharray = self.lengthsArr[i] = path.getTotalLength();
} );
// undraw stroke
this.draw(0);
}
// val in [0,1] : 0 - no stroke is visible, 1 - stroke is visible
SVGEl.prototype.draw = function( val ) {
for( var i = 0, len = this.pathsArr.length; i < len; ++i ){
this.pathsArr[ i ].style.strokeDashoffset = this.lengthsArr[ i ] * ( 1 - val );
}
}
function UIProgressButton( el, options ) {
this.el = el;
this.options = extend( {}, this.options );
extend( this.options, options );
this._init();
}
UIProgressButton.prototype.options = {
// time in ms that the status (success or error will be displayed) - should be at least higher than the transition-duration value defined for the stroke-dashoffset transition of both checkmark and cross strokes
statusTime : 1500
}
UIProgressButton.prototype._init = function() {
// the button
this.button = this.el.querySelector( 'button' );
// progress el
this.progressEl = new SVGEl( this.el.querySelector( 'svg.progress-circle' ) );
// the success/error elems
this.successEl = new SVGEl( this.el.querySelector( 'svg.checkmark' ) );
this.errorEl = new SVGEl( this.el.querySelector( 'svg.cross' ) );
// init events
this._initEvents();
// enable button
this._enable();
}
UIProgressButton.prototype._initEvents = function() {
var self = this;
this.button.addEventListener( 'click', function() { self._submit(); } );
}
UIProgressButton.prototype._submit = function() {
// by adding the loading class the button will transition to a "circle"
classie.addClass( this.el, 'loading' );
var self = this,
onEndBtnTransitionFn = function( ev ) {
if( support.transitions ) {
if( ev.propertyName !== 'width' ) return false;
this.removeEventListener( transEndEventName, onEndBtnTransitionFn );
}
// disable the button - this should have been the first thing to do when clicking the button.
// however if we do so Firefox does not seem to fire the transitionend event.
this.setAttribute( 'disabled', '' );
if( typeof self.options.callback === 'function' ) {
self.options.callback( self );
}
else {
// fill it (time will be the one defined in the CSS transition-duration property)
self.setProgress(1);
self.stop();
}
};
if( support.transitions ) {
this.button.addEventListener( transEndEventName, onEndBtnTransitionFn );
}
else {
onEndBtnTransitionFn();
}
}
// runs after the progress reaches 100%
UIProgressButton.prototype.stop = function( status ) {
var self = this,
endLoading = function() {
// first undraw progress stroke.
self.progressEl.draw(0);
if( typeof status === 'number' ) {
var statusClass = status >= 0 ? 'success' : 'error',
statusEl = status >=0 ? self.successEl : self.errorEl;
// draw stroke of success (checkmark) or error (cross).
statusEl.draw( 1 );
// add respective class to the element
classie.addClass( self.el, statusClass );
// after options.statusTime remove status and undraw the respective stroke. Also enable the button.
setTimeout( function() {
classie.remove( self.el, statusClass );
statusEl.draw(0);
self._enable();
}, self.options.statusTime );
}
else {
self._enable();
}
// finally remove class loading.
classie.removeClass( self.el, 'loading' );
};
// give it a time (ideally the same like the transition time) so that the last progress increment animation is still visible.
setTimeout( endLoading, 300 );
}
UIProgressButton.prototype.setProgress = function( val ) {
this.progressEl.draw( val );
}
// enable button
UIProgressButton.prototype._enable = function() {
this.button.removeAttribute( 'disabled' );
}
// add to global namespace
window.UIProgressButton = UIProgressButton;
})( window );