1/** 2 * UpSolution Element: Counter 3 */ 4! function( $, undefined ) { 5 6 var // Private variables that are used only in the context of this function, it is necessary to optimize the code 7 _parseFloat = parseFloat, 8 _parseInt = parseInt, 9 _undefined = undefined, 10 _window = window 11 12 /** 13 * Counter Number part animations 14 * 15 * @param container 16 * @constructor 17 */ 18 var USCounterNumber = function( container ) { 19 var self = this; 20 21 self.$container = $( container ); 22 self.initialString = self.$container.html() + ''; 23 self.finalString = self.$container.data( 'final' ) + ''; 24 self.format = self.getFormat( self.initialString, self.finalString ); 25 if ( self.format.decMark ) { 26 var pattern = new RegExp( '[^0-9\/' + self.format.decMark + ']+', 'g' ); 27 self.initial = _parseFloat( self.initialString.replace( pattern, '' ).replace( self.format.decMark, '.' ) ); 28 self.final = _parseFloat( self.finalString.replace( pattern, '' ).replace( self.format.decMark, '.' ) ); 29 } else { 30 self.initial = _parseInt( self.initialString.replace( /[^0-9]+/g, '' ) ); 31 self.final = _parseInt( self.finalString.replace( /[^0-9]+/g, '' ) ); 32 } 33 if ( self.format.accounting ) { 34 if ( self.initialString.length > 0 && self.initialString[ 0 ] == '(' ) { 35 self.initial = - self.initial; 36 } 37 if ( self.finalString.length > 0 && self.finalString[ 0 ] == '(' ) { 38 self.final = - self.final; 39 } 40 } 41 }; 42 /** 43 * Export API 44 */ 45 USCounterNumber.prototype = { 46 47 /** 48 * Function to be called at each animation frame 49 * @param now float Relative state between 0 and 1 50 */ 51 step: function( now ) { 52 var self = this, 53 value = ( 1 - now ) * self.initial + self.final * now, 54 intPart = Math[ self.format.decMark ? 'floor' : 'round' ]( value ).toString(), 55 result = ''; 56 if ( self.format.zerofill ) { 57 // Check how many zeros we need to add to this step value 58 var amountOfZeros = ( self.format.intDigits - intPart.length ); 59 if ( amountOfZeros > 0 ) { 60 intPart = '0'.repeat( amountOfZeros ) + intPart; 61 } 62 } 63 if ( self.format.groupMark ) { 64 if ( self.format.indian ) { 65 result += intPart.replace( /(\d)(?=(\d\d)+\d$)/g, '$1' + self.format.groupMark ); 66 } else { 67 result += intPart.replace( /\B(?=(\d{3})+(?!\d))/g, self.format.groupMark ); 68 } 69 } else { 70 result += intPart; 71 } 72 if ( self.format.decMark ) { 73 var decimalPart = ( value % 1 ).toFixed( self.format.decDigits ).substring( 2 ); 74 result += self.format.decMark + decimalPart; 75 } 76 if ( self.format.accounting && result.length > 0 && result[ 0 ] == '-' ) { 77 result = '(' + result.substring( 1 ) + ')'; 78 } 79 self.$container.html( result ); 80 }, 81 82 /** 83 * Get number format based on initial and final number strings 84 * @param initial string 85 * @param final string 86 * @returns {{groupMark, decMark, accounting, zerofill, indian}} 87 */ 88 getFormat: function( initial, final ) { 89 var self = this, 90 iFormat = self._getFormat( initial ), 91 fFormat = self._getFormat( final ), 92 // Final format has more priority 93 format = $.extend( {}, iFormat, fFormat ); 94 // Group marks detector is more precise, so using it in controversial cases 95 if ( format.groupMark == format.decMark ) { 96 delete format.groupMark; 97 } 98 return format; 99 }, 100 101 /** 102 * Get number format based on a single number string 103 * @param str string 104 * @returns {{groupMark, decMark, accounting, zerofill, indian}} 105 */ 106 _getFormat: function( str ) { 107 var marks = str.replace( /[0-9\(\)\-]+/g, '' ), 108 format = {}; 109 if ( str.charAt( 0 ) == '(' ) { 110 format.accounting = true; 111 } 112 if ( /^0[0-9]/.test( str ) ) { 113 format.zerofill = true; 114 } 115 str = str.replace( /[\(\)\-]/g, '' ); 116 if ( marks.length != 0 ) { 117 if ( marks.length > 1 ) { 118 format.groupMark = marks.charAt( 0 ); 119 if ( marks.charAt( 0 ) != marks.charAt( marks.length - 1 ) ) { 120 format.decMark = marks.charAt( marks.length - 1 ); 121 } 122 if ( str.split( format.groupMark ).length > 2 && str.split( format.groupMark )[ 1 ].length == 2 ) { 123 format.indian = true; 124 } 125 } else/*if (marks.length == 1)*/ { 126 format[ ( ( ( str.length - 1 ) - str.indexOf( marks ) ) == 3 && marks !== '.' ) ? 'groupMark' : 'decMark' ] = marks; 127 } 128 if ( format.decMark ) { 129 format.decDigits = str.length - str.indexOf( format.decMark ) - 1; 130 } 131 } 132 if ( format.zerofill ) { 133 format.intDigits = str.replace( /[^\d]+/g, '' ).length - ( format.decDigits || 0 ); 134 } 135 136 return format; 137 } 138 }; 139 140 /** 141 * Counter Number part animations 142 * 143 * @param container 144 * @constructor 145 */ 146 var USCounterText = function( container ) { 147 var self = this; 148 self.$container = $( container ); 149 self.initial = self.$container.text() + ''; 150 self.final = self.$container.data( 'final' ) + ''; 151 self.partsStates = self.getStates( self.initial, self.final ); 152 self.len = 1 / ( self.partsStates.length - 1 ); 153 // Text value won't be changed on each frame, so we'll update it only on demand 154 self.curState = 0; 155 }; 156 /** 157 * Export API 158 */ 159 USCounterText.prototype = { 160 161 /** 162 * Function to be called at each animation frame 163 * 164 * @param now float Relative state between 0 and 1 165 */ 166 step: function( now ) { 167 var self = this, 168 state = Math.round( Math.max( 0, now / self.len ) ); 169 if ( state == self.curState ) { 170 return; 171 } 172 self.$container.html( self.partsStates[ state ] ); 173 self.curState = state; 174 }, 175 176 /** 177 * Slightly modified Wagner-Fischer algorithm to obtain the shortest edit distance with intermediate states 178 * 179 * @param initial string The initial string 180 * @param final string The final string 181 * @returns {[]} 182 */ 183 getStates: function( initial, final ) { 184 var min = Math.min, 185 dist = [], 186 i, j; 187 for ( i = 0; i <= initial.length; i ++ ) { 188 dist[ i ] = [ i ]; 189 } 190 for ( j = 1; j <= final.length; j ++ ) { 191 dist[ 0 ][ j ] = j; 192 for ( i = 1; i <= initial.length; i ++ ) { 193 dist[ i ][ j ] = ( initial[ i - 1 ] === final[ j - 1 ] ) ? dist[ i - 1 ][ j - 1 ] : ( Math.min( dist[ i - 1 ][ j ], dist[ i ][ j - 1 ], dist[ i - 1 ][ j - 1 ] ) + 1 ); 194 } 195 } 196 // Obtaining the intermediate states 197 var states = [ final ]; 198 for ( i = initial.length, j = final.length; i > 0 || j > 0; i --, j -- ) { 199 var min = dist[ i ][ j ]; 200 if ( i > 0 ) { 201 min = Math.min( min, dist[ i - 1 ][ j ], ( j > 0 ) ? dist[ i - 1 ][ j - 1 ] : min ); 202 } 203 if ( j > 0 ) { 204 min = Math.min( min, dist[ i ][ j - 1 ] ); 205 } 206 if ( min >= dist[ i ][ j ] ) { 207 continue; 208 } 209 if ( min == dist[ i ][ j - 1 ] ) { 210 // Remove 211 states.unshift( states[ 0 ].substring( 0, j - 1 ) + states[ 0 ].substring( j ) ); 212 i ++; 213 } else if ( min == dist[ i - 1 ][ j - 1 ] ) { 214 // Modify 215 states.unshift( states[ 0 ].substring( 0, j - 1 ) + initial[ i - 1 ] + states[ 0 ].substring( j ) ); 216 } else if ( min == dist[ i - 1 ][ j ] ) { 217 // Insert 218 states.unshift( states[ 0 ].substring( 0, j ) + initial[ i - 1 ] + states[ 0 ].substring( j ) ); 219 j ++; 220 } 221 } 222 return states; 223 } 224 }; 225 226 /** 227 * @param container 228 * @constructor 229 */ 230 var USCounter = function( container ) { 231 var self = this; 232 233 // Commonly used DOM elements 234 self.$container = $( container ); 235 self.parts = []; 236 self.duration = _parseFloat( self.$container.data( 'duration' ) || 2 ) * 1000; 237 self.$container.find( '.w-counter-value-part' ).each( function( index, part ) { 238 var $part = $( part ); 239 // Skipping the ones that won't be changed 240 if ( $part.html() + '' == $part.data( 'final' ) + '' ) { 241 return; 242 } 243 var type = $part.usMod( 'type' ); 244 if ( type == 'number' ) { 245 self.parts.push( new USCounterNumber( $part ) ); 246 } else { 247 self.parts.push( new USCounterText( $part ) ); 248 } 249 } ); 250 if ( _window.$us !== _undefined && _window.$us.scroll !== _undefined ) { 251 // Animate element when it becomes visible
252 $us.waypoints.add( self.$container, /* offset */'15%', self.animate.bind( self ) ); 253 } else { 254 // No waypoints available: animate right from the start 255 self.animate(); 256 } 257 }; 258 /** 259 * Export API 260 */ 261 USCounter.prototype = { 262 animate: function( duration ) { 263 var self = this; 264 self.$container.css( 'w-counter', 0 ).animate( { 'w-counter': 1 }, { 265 duration: self.duration, 266 step: self.step.bind( self ) 267 } ); 268 }, 269 270 /** 271 * Function to be called at each animation frame 272 * 273 * @param now float Relative state between 0 and 1 274 */ 275 step: function( now ) { 276 var self = this; 277 for ( var i = 0; i < self.parts.length; i ++ ) { 278 self.parts[ i ].step( now ); 279 } 280 } 281 }; 282 283 $.fn.wCounter = function( options ) { 284 return this.each( function() { 285 var self = this; 286 $( self ).data( 'wCounter', new USCounter( self, options ) ); 287 } ); 288 }; 289 290 $( function() { 291 $( '.w-counter' ).wCounter(); 292 } ); 293}( jQuery );
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.