1/** 2 * stepsForm.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 2014, Codrops 9 * http://www.codrops.com 10 */ 11;( function( window ) { 12 13 'use strict'; 14 15 var transEndEventNames = { 16 'WebkitTransition': 'webkitTransitionEnd', 17 'MozTransition': 'transitionend', 18 'OTransition': 'oTransitionEnd', 19 'msTransition': 'MSTransitionEnd', 20 'transition': 'transitionend' 21 }, 22 transEndEventName = transEndEventNames[ Modernizr.prefixed( 'transition' ) ], 23 support = { transitions : Modernizr.csstransitions }; 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 34 function stepsForm( el, options ) { 35 this.el = el; 36 this.options = extend( {}, this.options ); 37 extend( this.options, options ); 38 this._init(); 39 } 40 41 stepsForm.prototype.options = { 42 onSubmit : function() { return false; } 43 }; 44 45 stepsForm.prototype._init = function() { 46 // current question 47 this.current = 0; 48 49 // questions 50 this.questions = [].slice.call( this.el.querySelectorAll( 'ol.questions > li' ) ); 51 // total questions 52 this.questionsCount = this.questions.length; 53 // show first question 54 classie.addClass( this.questions[0], 'current' ); 55 56 // next question control 57 this.ctrlNext = this.el.querySelector( 'button.next' ); 58 59 // progress bar 60 this.progress = this.el.querySelector( 'div.progress' ); 61 62 // question number status 63 this.questionStatus = this.el.querySelector( 'span.number' ); 64 // current question placeholder 65 this.currentNum = this.questionStatus.querySelector( 'span.number-current' ); 66 this.currentNum.innerHTML = Number( this.current + 1 ); 67 // total questions placeholder 68 this.totalQuestionNum = this.questionStatus.querySelector( 'span.number-total' ); 69 this.totalQuestionNum.innerHTML = this.questionsCount; 70 71 // error message 72 this.error = this.el.querySelector( 'span.error-message' ); 73 74 // init events 75 this._initEvents(); 76 }; 77 78 stepsForm.prototype._initEvents = function() { 79 var self = this, 80 // first input 81 firstElInput = this.questions[ this.current ].querySelector( 'input' ), 82 // focus 83 onFocusStartFn = function() { 84 firstElInput.removeEventListener( 'focus', onFocusStartFn ); 85 classie.addClass( self.ctrlNext, 'show' ); 86 }; 87 88 // show the next question control first time the input gets focu
88sed 89 firstElInput.addEventListener( 'focus', onFocusStartFn ); 90 91 // show next question 92 this.ctrlNext.addEventListener( 'click', function( ev ) { 93 ev.preventDefault(); 94 self._nextQuestion(); 95 } ); 96 97 // pressing enter will jump to next question 98 document.addEventListener( 'keydown', function( ev ) { 99 var keyCode = ev.keyCode || ev.which; 100 // enter 101 if( keyCode === 13 ) { 102 ev.preventDefault(); 103 self._nextQuestion(); 104 } 105 } ); 106 107 // disable tab 108 this.el.addEventListener( 'keydown', function( ev ) { 109 var keyCode = ev.keyCode || ev.which; 110 // tab 111 if( keyCode === 9 ) { 112 ev.preventDefault(); 113 } 114 } ); 115 }; 116 117 stepsForm.prototype._nextQuestion = function() { 118 if( !this._validade() ) { 119 return false; 120 } 121 122 // check if form is filled 123 if( this.current === this.questionsCount - 1 ) { 124 this.isFilled = true; 125 } 126 127 // clear any previous error messages 128 this._clearError(); 129 130 // current question 131 var currentQuestion = this.questions[ this.current ]; 132 133 // increment current question iterator 134 ++this.current; 135 136 // update progress bar 137 this._progress(); 138 139 if( !this.isFilled ) { 140 // change the current question number/status 141 this._updateQuestionNumber(); 142 143 // add class "show-next" to form element (start animations) 144 classie.addClass( this.el, 'show-next' ); 145 146 // remove class "current" from current question and add it to the next one 147 // current question 148 var nextQuestion = this.questions[ this.current ]; 149 classie.removeClass( currentQuestion, 'current' ); 150 classie.addClass( nextQuestion, 'current' ); 151 } 152 153 // after animation ends, remove class "show-next" from form element and change current question placeholder 154 var self = this, 155 onEndTransitionFn = function( ev ) { 156 if( support.transitions ) { 157 this.removeEventListener( transEndEventName, onEndTransitionFn ); 158 } 159 if( self.isFilled ) { 160 self._submit(); 161 } 162 else { 163 classie.removeClass( self.el, 'show-next' ); 164 self.currentNum.innerHTML = self.nextQuestionNum.innerHTML; 165 self.questionStatus.removeChild( self.nextQuestionNum ); 166 // force the focus on the next input 167 nextQuestion.querySelector( 'input' ).focus(); 168 } 169 }; 170 171 if( support.transitions ) { 172 this.progress.addEventListener( transEndEventName, onEndTransitionFn ); 173 } 174 else { 175 onEndTransitionFn(); 176 } 177 } 178 179 // updates the progress bar by setting its width 180 stepsForm.prototype._progress = function() { 181 this.progress.style.width = this.current * ( 100 / this.questionsCount ) + '%'; 182 } 183 184 // changes the current question number 185 stepsForm.prototype._updateQuestionNumber = function() { 186 // first, create next question number placeholder 187 this.nextQuestionNum = document.createElement( 'span' ); 188 this.nextQuestionNum.className = 'number-next'; 189 this.nextQuestionNum.innerHTML = Number( this.current + 1 ); 190 // insert it in the DOM 191 this.questionStatus.appendChild( this.nextQuestionNum ); 192 } 193 194 // submits the form 195 stepsForm.prototype._submit = function() { 196 this.options.onSubmit( this.el ); 197 } 198 199 // TODO (next version..) 200 // the validation function 201 stepsForm.prototype._validade = function() { 202 // current question´s input 203 var input = this.questions[ this.current ].querySelector( 'input' ).value; 204 if( input === '' ) { 205 this._showError( 'EMPTYSTR' ); 206 return false; 207 } 208 209 return true; 210 } 211 212 // TODO (next version..) 213 stepsForm.prototype._showError = function( err ) { 214 var message = ''; 215 switch( err ) { 216 case 'EMPTYSTR' : 217 message = 'Please fill the field before continuing'; 218 break; 219 case 'INVALIDEMAIL' : 220 message = 'Please fill a valid email address'; 221 break; 222 // ... 223 }; 224 this.error.innerHTML = message; 225 classie.addClass( this.error, 'show' ); 226 } 227 228 // clears/hides the current error message 229 stepsForm.prototype._clearError = function() { 230 classie.removeClass( this.error, 'show' ); 231 } 232 233 // add to global namespace 234 window.stepsForm = stepsForm; 235 236})( window );
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.