PageSourceSearch

https://almerecity.nl/wp-content/themes/Impreza/common/js/elements/counter.js?ver=8.46.1

js almerecity.nl collected 2026-10-02 11:43:15 UTC 11,552 bytes, 377 lines download raw bytes

1/**
2 * UpSolution Element: Counter
3 */
4! function( $, _undefined ) {
5
6	/**
7	 * Counter Number part animations.
8	 *
9	 * @param container
10	 * @constructor
11	 */
12	function usCounterNumber( container ) {
13		const self = this;
14
15		self.$container = $( container );
16		self.initialString = $ush.toString( self.$container.html() );
17		self.finalString = $ush.toString( self.$container.data( 'final' ) );
18		self.format = self.getFormat( self.initialString, self.finalString );
19		self.isNoAnimation = self.$container.closest( '.w-counter.animation_none' ).length;
20		if ( self.format.decMark ) {
21			const pattern = new RegExp( '[^0-9\/' + self.format.decMark + ']+', 'g' );
22			self.initial = parseFloat( self.initialString.replace( pattern, '' ).replace( self.format.decMark, '.' ) );
23			self.final = parseFloat( self.finalString.replace( pattern, '' ).replace( self.format.decMark, '.' ) );
24		} else {
25			self.initial = parseInt( self.initialString.replace( /[^0-9]+/g, '' ) );
26			self.final = parseInt( self.finalString.replace( /[^0-9]+/g, '' ) );
27		}
28		if ( self.format.accounting ) {
29			if ( self.initialString.length > 0 && self.initialString[0] == '(' ) {
30				self.initial = -self.initial;
31			}
32			if ( self.finalString.length > 0 && self.finalString[0] == '(' ) {
33				self.final = -self.final;
34			}
35		}
36
37		if ( ! self.isNoAnimation ) {
38			self.initSlideAnimation();
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			const self = this;
53			var 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
80			// No Animation
81			if ( self.isNoAnimation ) {
82				self.$container.html( result );
83			} else { // Slide Animation
84				const digitsOnly = result.replace( /\D/g, '' );
85
86				// Start animate from the last digit
87				var digitIndex = digitsOnly.length - 1;
88
89				for ( var slotIndex = self.slots.length - 1; slotIndex >= 0; slotIndex-- ) {
90
91					const slot = self.slots[ slotIndex ];
92
93					if ( slot.type !== 'digit' ) {
94						continue;
95					}
96
97					const num = $ush.parseInt( digitsOnly[ digitIndex ] );
98					const offset = num * slot.el.find( 'li' ).innerHeight();
99
100					slot.el.find( 'ul' )
101						.css( 'transform', 'translateY(-' + offset + 'px)' );
102
103					// Hide non-active digits after animation (fonts with different num heights case)
104					$ush.timeout( () => {
105						slot.el.find( 'li:nth-child(' + num + ')' ).addClass( 'pre-active' );
106					}, ( self.$container.closest( '.w-counter' ).data( 'duration' ) || 2 ) * 1000 );
107
108					digitIndex--;
109				}
110			}
111		},
112
113		/**
114		 * Get number format based on initial and final number strings.
115		 * @param initial string
116		 * @param final string
117		 * @returns {{groupMark, decMark, accounting, zerofill, indian}}
118		 */
119		getFormat: function( initial, final ) {
120			const self = this;
121			var iFormat = self._getFormat( initial ),
122				fFormat = self._getFormat( final ),
123				// Final format has more priority
124				format = $.extend( {}, iFormat, fFormat );
125			// Group marks detector is more precise, so using it in controversial cases
126			if ( format.groupMark == format.decMark ) {
127				delete format.groupMark;
128			}
129			return format;
130		},
131
132		/**
133		 * Get number format based on a single number string.
134		 * @param str string
135		 * @returns {{groupMark, decMark, accounting, zerofill, indian}}
136		 */
137		_getFormat: function( str ) {
138			var marks = str.replace( /[0-9\(\)\-]+/g, '' ),
139				format = {};
140			if ( str.charAt(0) == '(' ) {
141				format.accounting = true;
142			}
143			if ( /^0[0-9]/.test( str ) ) {
144				format.zerofill = true;
145			}
146			str = str.replace( /[\(\)\-]/g, '' );
147			if ( marks.length != 0 ) {
148				if ( marks.length > 1 ) {
149					format.groupMark = marks.charAt(0);
150					if ( marks.charAt(0) != marks.charAt( marks.length - 1 ) ) {
151						format.decMark = marks.charAt( marks.length - 1 );
152					}
153					if ( str.split( format.groupMark ).length > 2 && str.split( format.groupMark )[1].length == 2 ) {
154						format.indian = true;
155					}
156				} else/*if (marks.length == 1)*/ {
157					format[ ( ( ( str.length - 1 ) - str.indexOf( marks ) ) == 3 && marks !== '.' ) ? 'groupMark' : 'decMark' ] = marks;
158				}
159				if ( format.decMark ) {
160					format.decDigits = str.length - str.indexOf( format.decMark ) - 1;
161				}
162			}
163			if ( format.zerofill ) {
164				format.intDigits = str.replace( /[^\d]+/g, '' ).length - ( format.decDigits || 0 );
165			}
166
167			return format;
168		},
169
170		/**
171		 * Create slots(digits and chars) for each character in final number string
172		 * Fill DOM with slots
173		 * Needed for slide animation
174		 */
175		initSlideAnimation: function() {
176			const self = this;
177
178			self.$container.empty();
179			self.slots = []; // Numbers and chars
180
181			const chars = self.finalString.split( '' );
182
183			chars.forEach( char => {
184				if ( /\d/.test( char ) ) { // Digits
185					const $mainDigit = $( '<span class="w-counter-value-digit"></span>' );
186					const $digits = $( '<div class="w-counter-digits"></div>' );
187
188					$digits.append( '<ul></ul>' );
189
190					for ( let i = 0; i < 10; i++ ) {
191						$( 'ul', $digits ).append( '<li>' + i + '</li>' );
192					}
193
194					$mainDigit
195						.append( '<span>' + char + '</span>' )
196						.append( $digits );
197
198					self.$container.append( $mainDigit );
199
200					self.slots.push( { type: 'digit', el: $digits } );
201
202				} else { // Chars like '.' or ','
203					const $char = $( '<span class="w-counter-value-delimeter">' + char + '</span>' );
204					self.$container.append( $char );
205
206					self.slots.push( { type: 'char', el: $char } );
207				}
208			} );
209		}
210	};
211
212	/**
213	 * Counter Number part animations.
214	 *
215	 * @param container
216	 * @constructor
217	 */
218	function usCounterText( container ) {
219		const self = this;
220		self.$container = $( container );
221		self.initial = $ush.toString( self.$container.text() );
222		self.final = $ush.toString( self.$container.data( 'final' ) );
223		self.partsStates = self.getStates( self.initial, self.final );
224		self.len = 1 / ( self.partsStates.length - 1 );
225		// The text value won’t change on every frame, so we’ll update it only when needed.
226		self.curState = 0;
227	}
228	/**
229	 * Export API
230	 */
231	usCounterText.prototype = {
232
233		/**
234		 * Function to be called at each animation frame.
235		 *
236		 * @param now float Relative state between 0 and 1
237		 */
238		step: function( now ) {
239			const self = this;
240			const state = Math.round( Math.max( 0, now / self.len ) );
241			if ( state == self.curState ) {
242				return;
243			}
244			self.$container.html( self.partsStates[ state ] );
245			self.curState = state;
246		},
247
248		/**
249		 * Slightly modified Wagner-Fischer algorithm to obtain the shortest edit distance with intermediate states.
250		 *
251		 * @param initial string The initial string
252		 * @param final string The final string
253		 * @returns {[]}
254		 */
255		getStates: function( initial, final ) {
256			var min = Math.min,
257				dist = [],
258				i, j;
259			for ( i = 0; i <= initial.length; i ++ ) {
260				dist[ i ] = [ i ];
261			}
262			for ( j = 1; j <= final.length; j ++ ) {
263				dist[ 0 ][ j ] = j;
264				for ( i = 1; i <= initial.length; i ++ ) {
265					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 );
266				}
267			}
268			// Obtaining the intermediate states
269			var states = [ final ];
270			for ( i = initial.length, j = final.length; i > 0 || j > 0; i --, j -- ) {
271				var min = dist[ i ][ j ];
272				if ( i > 0 ) {
273					min = Math.min( min, dist[ i - 1 ][ j ], ( j > 0 ) ? dist[ i - 1 ][ j - 1 ] : min );
274				}
275				if ( j > 0 ) {
276					min = Math.min( min, dist[ i ][ j - 1 ] );
277				}
278				if ( min >= dist[ i ][ j ] ) {
279					continue;
280				}
281				if ( min == dist[ i ][ j - 1 ] ) {
282					// Remove
283					states.unshift( states[0].substring( 0, j - 1 ) + states[0].substring( j ) );
284					i ++;
285				} else if ( min == dist[ i - 1 ][ j - 1 ] ) {
286					// Modify
287					states.unshift( states[0].substring( 0, j - 1 ) + initial[ i - 1 ] + states[0].substring( j ) );
288				} else if ( min == dist[ i - 1 ][ j ] ) {
289					// Insert
290					states.unshift( states[0].substring( 0, j ) + initial[ i - 1 ] + states[0].substring( j ) );
291					j ++;
292				}
293			}
294			return states;
295		}
296	};
297
298	/**
299	 * @param container
300	 * @constructor
301	 */
302	function usCounter( container ) {
303		const self = this;
304
305		// Commonly used DOM elements
306		self.$container = $( container );
307		self.parts = [];
308		self.duration = parseFloat( self.$container.data( 'duration' ) || 2 ) * 1000;
309
310		// Hide final values on init
311		$( '.w-counter-value-part', self.$container ).each( ( _, part ) => {
312			const $part = $( part );
313			if ( $part.hasClass( 'final' ) ) {
314				$part.remove();
315			} else {
316				$part.removeClass( 'hidden' );
317			}
318		} );
319
320		$( '.w-counter-value-part', self.$container ).each( ( _, part ) => {
321			const $part = $( part );
322			// Skip the ones that won't be changed
323			if ( $ush.toString( $part.html() ) === $ush.toString( $part.data( 'final' ) ) ) {
324				return;
325			}
326			if ( $part.usMod( 'type' ) === 'number' ) {
327				self.parts.push( new usCounterNumber( $part ) );
328			} else {
329				self.parts.push( new usCounterText( $part ) );
330			}
331		} );
332		if ( window.$us !== _undefined && window.$us.scroll !== _undefined ) {
333			// Animate element when it becomes visible
334			$us.waypoints.add( self.$container, /* offset */'15%', self.animate.bind( self ) );
335		} else {
336			// No waypoints available: animate right from the start
337			self.animate();
338		}
339	}
340	/**
341	 * Export API
342	 */
343	usCounter.prototype = {
344		animate: function() {
345			const self = this;
346			if ( self.$container.hasClass( 'animation_none' ) ) {
347				self.$container.css( 'w-counter', 0 ).animate( { 'w-counter': 1 }, {
348					duration: self.duration,
349					step: self.step.bind( self )
350				} );
351			} else {
352				self.step( 1 );
353			}
354		},
355
356		/**
357		 * Function to be called at each animation frame.
358		 *
359		 * @param now float Relative state between 0 and 1
360		 */
361		step: function( now ) {
362			const self = this;
363			for ( var i = 0; i < self.parts.length; i++ ) {
364				self.parts[ i ].step( now );
365			}
366		}
367	};
368
369	$.fn.usCounter = function( options ) {
370		return this.each( function() {
371			$( this ).data( 'usCounter', new usCounter( this, options ) );
372		} );
373	};
374
375	$( () => $( '.w-counter' ).usCounter() );
376
377}( 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.