PageSourceSearch

https://www.bletterbach.info/wp-content/themes/bletterbach/js/counter.js

js bletterbach.info collected 2026-10-02 11:37:19 UTC 15,805 bytes, 607 lines download raw bytes

1(function ( $, window ) {
2  
3  var pluginName = 'countimator';
4  
5  var defaults = {
6    duration: 1000, 
7    value: null, 
8    max: 0, 
9    lineWidth: 8, 
10    valueColor: "#aaeeff", 
11    maxColor: "lightgray",  
12    countSelector: '.counter-count', 
13    maxSelector: '.counter-max', 
14    style: null, 
15    template: null, 
16    engine: "auto", 
17    animateOnInit: true, 
18    animateOnAppear: true, 
19    bodyClass: 'counter-body',
20    // format options 
21    decimals: 0, 
22    decimalDelimiter: '.', 
23    thousandDelimiter: null, 
24    pad: false, 
25    // appearance
26    textAlign: '', 
27    verticalAlign: 'middle'
28  };
29  
30  var dataOpts = [
31    "value", 
32    "count", 
33    "min", 
34    "max", 
35    "template", 
36    "decimals", 
37    "decimalDelimiter", 
38    "thousandDelimiter", 
39    "pad", 
40    "style"
41  ];
42  
43  function formatNumber(n, decimals, decimalDelimiter, thousandDelimiter){
44    decimals = isNaN(decimals = Math.abs(decimals)) ? 2 : decimals, 
45    decimalDelimiter = typeof decimalDelimiter == 'undefined' ? "." : decimalDelimiter, 
46    thousandDelimiter = typeof thousandDelimiter == 'undefined' ? "," : thousandDelimiter, 
47    thousandDelimiter = typeof thousandDelimiter == 'string' ? thousandDelimiter : "", 
48    s = n < 0 ? "-" : "", 
49    i = parseInt(n = Math.abs(+n || 0).toFixed(decimals)) + "", 
50    j = (j = i.length) > 3 ? j % 3 : 0;
51    return s + (j ? i.substr(0, j) + thousandDelimiter : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + thousandDelimiter) + (decimals ? decimalDelimiter + Math.abs(n - i).toFixed(decimals).slice(2) : "");
52  };
53  
54  function pad(number, length) {
55    var str = '' + number;
56    while (str.length < length) {
57        str = '0' + str;
58    }
59    return str;
60  }
61  
62  function textNodes(parent) {
63    return $(parent).contents().filter(function () {
64      return this.nodeType === Node.TEXT_NODE;
65    });
66  }
67  
68  function inView(el){
69    var top = el.getBoundingClientRect().top, left = el.getBoundingClientRect().left, rect, el = el.parentNode;
70    do {
71        rect = el.getBoundingClientRect();
72        if (top <= rect.bottom === false && left <= rect.right === false)
73            return false;
74        el = el.parentNode;
75    } while (el != document.body);
76    // Check its within the document viewport
77    return top <= document.documentElement.clientHeight && left <= document.documentElement.clientWidth;
78  };
79  
80  function isElementInViewport (el) {
81
82    //special bonus for those using jQuery
83    if (el instanceof jQuery) {
84        el = el[0];
85    }
86
87    var rect = el.getBoundingClientRect();
88
89    return (
90        rect.top >= 0 &&
91        rect.left >= 0 &&
92        rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
93        rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
94    );
95  }
96
97  // request animation frame
98  // TODO: polyfill
99  var requestAnimationFrame = 
100    window.mozRequestAnimationFrame ||
101    window.webkitRequestAnimationFrame ||
102    window.msRequestAnimationFrame ||
103    window.oRequestAnimationFrame || 
104    function( callback ){
105      window.setTimeout(callback, 1000 / 60);
106    };
107
108
109  function detectEngine() {
110    var engines = ['Handlebars', 'Mugine'];
111    for (var i = 0, engine; engine = engines[i]; i++) {
112      if (window[engine] && window[engine].compile) return engine;
113    }
114    return null;
115  }
116
117  var pluginClass = function Countimator(element, options) {
118    
119    var instance = this;
120    var $element = $(element);
121    
122    options = $.extend({}, defaults, options, $element.data());
123    
124    // $(dataOpts).each(function(index, value) {
125      // if (typeof $element.data(value) != 'undefined') {
126        // options[value] = $element.data(value);
127      // }
128    // });
129    
130    var canvas, ctx, container, body;
131    var startTime, startCount;
132    var animating = false;
133    
134    
135    // private methods
136    function init() {
137    
138      var value = getOption('value');
139      var count = getOption('count');
140      var max = getOption('max');
141    
142      // init values
143      if (!count) {
144        var countNode = getCountNode();
145        if (countNode) {
146          var value = getOption('value');
147          if (typeof value != 'number') {
148            options.value = countNode.nodeValue;
149          } else {
150            options.count = countNode.nodeValue;
151          }
152        }
153      }
154      
155      if (!max) {
156        var maxNode = getMaxNode();
157        if (maxNode) {
158          options.max = maxNode.nodeValue;
159        }
160      }
161      
162      // init template
163      
164      var script = $element.find("script[type*='text/x-']");
165      if (script.length) {
166        options.template = script.html();
167        script.remove();
168      }
169
170      // init style
171      var style = getOption('style');
172      
173      if (typeof style == "object") {
174        if (typeof style.draw == "function") {
175          
176          canvas = document.createElement('canvas');
177          if (canvas.getContext) {
178            // is supported
179            var contentNode = element.firstChild;
180            body = $('<span class="' + getOption('bodyClass') + '"></span>');
181            $element.append(body);
182            body.append(contentNode);
183            $(canvas).insertBefore(body);
184          }
185        }
186      }
187      
188      // init listeners
189      $(window).on('resize', function() {
190        resize.call(instance);
191      });
192      
193      
194      $(window).on('scroll touchmove', function() {
195        if (getOption('animateOnInit') && getOption('animateOnAppear') && isElementInViewport(element)) {
196          $(window).off('scroll touchmove', arguments.callee);
197          start.call(instance);
198        }
199      });
200      
201      // render.call(this);
202      
203      if (getOption('animateOnInit')) {
204        if (getOption('animateOnAppear') && isElementInViewport(element)) {
205          options.count = typeof count == 'number' ? count : 0; 
206          start.call(instance);
207        } else {
208          render.call(this);
209        }
210      } else {
211        options.count = getOption('value');
212        render.call(this);
213      }
214      
215      resize.call(this);
216      
217    };
218    
219    function setOption(name, value) {
220      if (name == "style") return; // style cannot be set at runtime
221      var old = options[name];
222      options[name] = value;
223      switch (name) {
224        case 'value': 
225          if (old == value) {
226            return;
227          }
228          if (typeof old != 'number') {
229            options['count'] = value;
230            render.call(this);
231          } else {
232            options['count'] = old;
233            start();
234          }
235          break;
236       }
237    }
238    
239    function getOption(name) {
240      var value = options[name];
241      
242      switch (name) {          
243      
244        case 'value': 
245          var max = getOption('max');
246          var min = getOption('min');
247          var count = getOption('count');
248          var floatValue = parseFloat(value);
249          if (!isNaN(floatValue)) {
250            floatValue = Math.max(floatValue, min);
251            value = Math.min(floatValue, max);
252          }
253          break;
254        case 'count': 
255          if (!value) {
256            var max = getOption('max');
257            var min = getOption('min');
258            value = Math.max(value, min);
259            value = Math.min(value, max);
260            value = parseFloat(value);
261            if (isNaN(value)) {
262              value = min;
263            }
264          }
265          break;
266        case 'max': 
267          value = parseFloat(value ? value : options.value);
268          break;
269        case 'min': 
270          if (!value) {
271            value = 0;
272          }
273          break;
274        case 'style':
275          value = $.fn.countimator.getStyle(value);
276          break;
277        case 'template':
278          // try to find by selector
279          try {
280            value = $(value).html();
281          } catch(e) {
282            // not a valid selector
283          }
284          break;
285      }
286      return value;
287    }
288    
289    function resize() {
290      
291      
292      
293      var $body = $element.find("." + this.getOption('bodyClass'));
294
295      // reset element
296      $element.css({
297        textAlign: ''
298      });
299      
300      var textAlign = this.getOption('textAlign') || $element.css('text-align');
301      var verticalAlign = this.getOption('verticalAlign');
302      
303      $element.css({
304        textAlign: textAlign
305      });
306      
307      // TODO: replace canvas with background
308      
309      if (canvas) {
310        
311        $element.css({
312          position: position != "static" ? position : 'relative' 
313        });
314        
315        $(canvas).css({
316          position: 'absolute',
317          top: 0, 
318          left: 0
319        });
320        
321        // reset body
322        $body.css({
323          position: 'absolute', 
324          top: "", 
325          bottom: "",  
326          left: "", 
327          right: "", 
328          textAlign: "", 
329          display: 'none'
330        });
331        
332        
333        
334      }
335      
336      
337      var position = $element.css('position');
338      var display = $element.css('display');
339
340      var width = $element.width();
341      var height = $element.height();
342      
343      var outerWidth = $element.outerWidth(false);
344      var outerHeight = $element.outerHeight(false);
345      
346      
347      if (canvas) {
348        // vertical-align
349        var paddingLeft = parseFloat($element.css('padding-left'));
350        var paddingRight = parseFloat($element.css('padding-right'));
351        var paddingTop = parseFloat($element.css('padding-top'));
352        var paddingBottom = parseFloat($element.css('padding-top'));
353        var top, bottom;
354        if (verticalAlign == 'justify') {
355          top = paddingTop; 
356          bottom = paddingBottom;
357        } else {
358          var va = verticalAlign == 'top' ? 0 : verticalAlign == 'bottom' ? 1 : 0.5;
359          top = paddingTop + ( height - $body.outerHeight(false) ) * va;
360          bottom = 'auto';
361        }
362        
363        $body.css({
364          position: 'absolute', 
365          top: top, 
366          bottom: bottom,  
367          left: paddingLeft, 
368          right: paddingRight, 
369          textAlign: textAlign, 
370          display: ''
371        });
372        
373      }
374      
375      var style = getOption('style');
376      if (style && style.resize) {
377        style.resize.call(instance, element, width, height);
378      }
379      
380      if (canvas) {
381        // resize canvas
382        canvas.width = $element.innerWidth();
383        canvas.height = $element.innerHeight();
384      }
385      
386      draw.call(this);
387      
388    };
389    
390    function animate(value) {
391      options.value = value;
392      if (!animating) {
393        start();
394      } else {
395        // TODO: stretch duration
396      }
397    }
398    
399    function start() {
400      startTime = new Date().getTime();
401      startCount = getOption('count');
402      animating = true;
403      requestAnimationFrame(step);
404    }
405    
406    function getCountNode(count) {
407      var countElement = $element.find(options.countSelector)[0];
408      var textNode = textNodes(countElement || element)[0];
409      return textNode;
410    }
411    
412    function getMaxNode(count) {
413      var maxElement = $element.find(options.maxSelector)[0];
414      if (maxElement) {
415        return textNodes(maxElement)[0];
416      }
417      return null;
418    }
419    
420    function getFormattedValue(value) {
421      
422      // format number
423      var decimals = getOption('decimals');
424      var decimalDelimiter = getOption('decimalDelimiter');
425      var thousandDelimiter = getOption('thousandDelimiter');
426     
427      string = formatNumber(value, decimals, decimalDelimiter, thousandDelimiter);
428      
429      // pad
430      string = pad(string, getOption('pad'));
431      
432      return string;
433    }
434    
435    
436    function render() {
437    
438      var max = getOption('max');
439      var min = getOption('min');
440      var value = getOption('value');
441      var count = getOption('count');
442      
443      var formattedCount = getFormattedValue(count);
444      var formattedValue = getFormattedValue(value);
445      var formattedMax = getFormattedValue(max);
446      var formattedMin = getFormattedValue(min);
447      
448      var engine = getOption('engine');
449      engine = engine && engine != 'auto' ? engine : detectEngine(); 
450      engine = typeof engine == "string" ? window[engine] : engine;
451      
452      var template = getOption('template');
453      var string;
454      
455      if (engine && template) {
456        
457        // template engine
458        var tmpl = engine.compile(template);
459        
460        if (tmpl) {
461          var tmplData = $.extend({}, options, {count: formattedCount, value: formattedValue, max: formattedMax, min: formattedMin});
462          string = tmpl(tmplData);
463        }
464        var div = document.createElement('div');
465        div.innerHTML = string;
466        var nodeList = div.childNodes; 
467        
468        if (body) {
469          $(body).empty();
470          $(body).append(nodeList);
471        } else {
472          $(element).contents().not(container).remove();
473          $(element).append(nodeList);
474        }
475        
476      } else {
477      
478        // classic approach without template engine
479        
480        string = formattedCount;
481        
482        // set count
483        var countNode = getCountNode();
484        if (countNode) {
485          countNode.nodeValue = formattedCount;
486        }
487        // set max (there will be no max-node when using templates)
488        var maxNode = getMaxNode();
489        if (maxNode) {
490          maxNode.nodeValue = formattedMax;
491        }
492        
493        if (!countNode && !maxNode) {
494          element.innerHTML = formattedCount;
495        }
496      
497      }
498      
499      var style = getOption('style');
500      if (style && style.render) {
501        style.render.call(instance, count);
502      }
503      
504      // draw
505      draw.call(this);
506        
507    }
508
509    function draw() {
510      var count = getOption('count');
511      var style = getOption('style');
512      if (style && style.draw && canvas && canvas.getContext) {
513        var ctx = canvas.getContext("2d");
514        ctx.clearRect(0, 0, canvas.width, canvas.height);
515        style.draw.call(instance, canvas, count);
516      }
517    }
518
519    function step() {
520        
521        var duration = getOption('duration');
522        var max = getOption('max');
523        var value = getOption('value');
524        
525        var currentTime = new Date().getTime();
526        var endTime = startTime + duration;
527        
528        var currentStep = Math.min((duration - (endTime - currentTime)) / duration, 1);
529        
530        var count = startCount + currentStep * (value - startCount);
531        
532        options.count = count;
533        
534        render.call(this);
535        
536        if (currentStep < 1 && animating) {
537        
538          requestAnimationFrame(step);
539          
540        } else {
541        
542          end.call(this);
543        }
544        
545    }
546    
547    
548    function end() {
549      animating = false;
550    }
551        
552    
553    
554    // public methods
555    
556    this.resize = function() {
557      resize.call(this);
558    };
559    
560    this.getOption = function(name) {
561      return getOption.call(this, name);
562    };
563    
564    this.setOption = function(name, value) {
565      setOption.call(this, name, value);
566    };
567    
568    this.animate = function(value) {
569      animate.call(this, value);
570    };
571    
572    // call init
573    init.call(this);
574
575  };
576  
577
578  // bootstrap plugin
579  
580  $.fn[pluginName] = function(options) {
581      
582    return this.each(function() {
583
584      if (!$(this).data(pluginName)) {
585        $(this).data(pluginName, new pluginClass(this, options));
586      }
587      
588      return $(this);
589  
590    });
591  };
592  
593  
594  // style api
595  
596  var styles = {};
597  
598  $.fn[pluginName].registerStyle = function(name, def) {
599    styles[name] = def; 
600  };
601  
602  $.fn[pluginName].getStyle = function(name) {
603    return styles[name];
604  };
605  
606
607})( jQuery, 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.