progressbutton.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. /**
  2. * progressbutton.js v1.0.0
  3. * http://www.codrops.com
  4. *
  5. * Licensed under the MIT license.
  6. * http://www.opensource.org/licenses/mit-license.php
  7. *
  8. * Copyright 2013, Codrops
  9. * http://www.codrops.com
  10. */
  11. ;( function( window ) {
  12. 'use strict';
  13. // https://gist.github.com/edankwan/4389601
  14. Modernizr.addTest('csstransformspreserve3d', function () {
  15. var prop = Modernizr.prefixed('transformStyle');
  16. var val = 'preserve-3d';
  17. var computedStyle;
  18. if(!prop) return false;
  19. prop = prop.replace(/([A-Z])/g, function(str,m1){ return '-' + m1.toLowerCase(); }).replace(/^ms-/,'-ms-');
  20. Modernizr.testStyles('#modernizr{' + prop + ':' + val + ';}', function (el, rule) {
  21. computedStyle = window.getComputedStyle ? getComputedStyle(el, null).getPropertyValue(prop) : '';
  22. });
  23. return (computedStyle === val);
  24. });
  25. function extend( a, b ) {
  26. for( var key in b ) {
  27. if( b.hasOwnProperty( key ) ) {
  28. a[key] = b[key];
  29. }
  30. }
  31. return a;
  32. }
  33. // support
  34. var support = { transitions : Modernizr.csstransitions, transforms3d : Modernizr.csstransforms3d && Modernizr.csstransformspreserve3d },
  35. // transition end event name
  36. transEndEventNames = {
  37. 'WebkitTransition': 'webkitTransitionEnd',
  38. 'MozTransition': 'transitionend',
  39. 'OTransition': 'oTransitionEnd',
  40. 'msTransition': 'MSTransitionEnd',
  41. 'transition': 'transitionend'
  42. },
  43. transEndEventName = transEndEventNames[ Modernizr.prefixed( 'transition' ) ];
  44. function ProgressButton( el, options ) {
  45. this.button = el;
  46. this.options = extend( {}, this.options );
  47. extend( this.options, options );
  48. this._init();
  49. }
  50. ProgressButton.prototype.options = {
  51. // time in ms that the status (success or error will be displayed)
  52. // during this time the button will be disabled
  53. statusTime : 1500
  54. };
  55. ProgressButton.prototype._init = function() {
  56. this._validate();
  57. // create structure
  58. this._create();
  59. // init events
  60. this._initEvents();
  61. };
  62. ProgressButton.prototype._validate = function() {
  63. // we will consider the fill/horizontal as default
  64. if( this.button.getAttribute( 'data-style' ) === null ) {
  65. this.button.setAttribute( 'data-style', 'fill' );
  66. }
  67. if( this.button.getAttribute( 'data-vertical' ) === null && this.button.getAttribute( 'data-horizontal' ) === null ) {
  68. this.button.setAttribute( 'data-horizontal', '' );
  69. }
  70. if( !support.transforms3d && this.button.getAttribute( 'data-perspective' ) !== null ) {
  71. this.button.removeAttribute( 'data-perspective' );
  72. this.button.setAttribute( 'data-style', 'fill' );
  73. this.button.removeAttribute( 'data-vertical' );
  74. this.button.setAttribute( 'data-horizontal', '' );
  75. }
  76. };
  77. ProgressButton.prototype._create = function() {
  78. var textEl = document.createElement( 'span' );
  79. textEl.className = 'content';
  80. textEl.id = 'button_content';
  81. textEl.innerHTML = this.button.innerHTML;
  82. var progressEl = document.createElement( 'span' );
  83. progressEl.className = 'progress';
  84. var progressInnerEl = document.createElement( 'span' );
  85. progressInnerEl.className = 'progress-inner';
  86. progressEl.appendChild( progressInnerEl );
  87. // clear content
  88. this.button.innerHTML = '';
  89. if( this.button.getAttribute( 'data-perspective' ) !== null ) {
  90. var progressWrapEl = document.createElement( 'span' );
  91. progressWrapEl.className = 'progress-wrap';
  92. progressWrapEl.appendChild( textEl );
  93. progressWrapEl.appendChild( progressEl );
  94. this.button.appendChild( progressWrapEl );
  95. }
  96. else {
  97. this.button.appendChild( textEl );
  98. this.button.appendChild( progressEl );
  99. }
  100. // the element that serves as the progress bar
  101. this.progress = progressInnerEl;
  102. // property to change on the progress element
  103. if( this.button.getAttribute( 'data-horizontal' ) !== null ) {
  104. this.progressProp = 'width';
  105. }
  106. else if( this.button.getAttribute( 'data-vertical' ) !== null ) {
  107. this.progressProp = 'height';
  108. }
  109. this._enable();
  110. };
  111. ProgressButton.prototype._setProgress = function( val ) {
  112. /* Speed controlled by CSS settings */
  113. // console.log('Val: ' +val);
  114. this.progress.style[ this.progressProp ] = 100 * val + '%';
  115. };
  116. ProgressButton.prototype._initEvents = function() {
  117. var self = this;
  118. this.button.addEventListener( 'click', function() {
  119. // disable the button
  120. self.button.setAttribute( 'disabled', '' );
  121. // add class state-loading to the button (applies a specific transform to the button depending which data-style is defined - defined in the stylesheets)
  122. classie.remove( self.progress, 'notransition' );
  123. classie.add( this, 'state-loading' );
  124. setTimeout( function() {
  125. if( typeof self.options.callback === 'function' ) {
  126. self.options.callback( self );
  127. }
  128. else {
  129. self._setProgress( 1 );
  130. var onEndTransFn = function( ev ) {
  131. if( support.transitions && ev.propertyName !== self.progressProp ) return;
  132. this.removeEventListener( transEndEventName, onEndTransFn );
  133. self._stop();
  134. };
  135. if( support.transitions ) {
  136. self.progress.addEventListener( transEndEventName, onEndTransFn );
  137. }
  138. else {
  139. onEndTransFn.call();
  140. }
  141. }
  142. },
  143. self.button.getAttribute( 'data-style' ) === 'fill' ||
  144. self.button.getAttribute( 'data-style' ) === 'top-line' ||
  145. self.button.getAttribute( 'data-style' ) === 'lateral-lines' ? 0 : 200 ); // TODO: change timeout to transitionend event callback
  146. } );
  147. };
  148. ProgressButton.prototype._stop = function( status ) {
  149. var self = this;
  150. setTimeout( function() {
  151. // fade out progress bar
  152. self.progress.style.opacity = 0;
  153. var onEndTransFn = function( ev ) {
  154. if( support.transitions && ev.propertyName !== 'opacity' ) return;
  155. this.removeEventListener( transEndEventName, onEndTransFn );
  156. classie.add( self.progress, 'notransition' );
  157. self.progress.style[ self.progressProp ] = '0%';
  158. self.progress.style.opacity = 1;
  159. };
  160. if( support.transitions ) {
  161. self.progress.addEventListener( transEndEventName, onEndTransFn );
  162. }
  163. else {
  164. onEndTransFn.call();
  165. }
  166. // add class state-success to the button
  167. if( typeof status === 'number' ) {
  168. var statusClass = status >= 0 ? 'state-success' : 'state-error';
  169. classie.add( self.button, statusClass );
  170. // after options.statusTime remove status
  171. setTimeout( function() {
  172. classie.remove( self.button, statusClass );
  173. // self._enable();
  174. }, self.options.statusTime );
  175. }
  176. else {
  177. self._enable();
  178. }
  179. // remove class state-loading from the button
  180. classie.remove( self.button, 'state-loading' );
  181. }, 500 );
  182. };
  183. // enable button
  184. ProgressButton.prototype._enable = function() {
  185. this.button.removeAttribute( 'disabled' );
  186. };
  187. // add to global namespace
  188. window.ProgressButton = ProgressButton;
  189. })( window );