PageSourceSearch

https://www.madisonexteriorsandremodeling.com/wp-content/themes/in…/datecounter/timecircles.js?ver=7.1.2

js madisonexteriorsandremodeling.com collected 2026-09-30 03:25:22 UTC 35,198 bytes, 961 lines download raw bytes

1/**
2 * Basic structure: TC_Class is the public class that is returned upon being called
3 * 
4 * So, if you do
5 *      var tc = $(".timer").TimeCircles();
6 *      
7 * tc will contain an instance of the public TimeCircles class. It is important to
8 * note that TimeCircles is not chained in the conventional way, check the
9 * documentation for more info on how TimeCircles can be chained.
10 * 
11 * After being called/created, the public TimerCircles class will then- for each element
12 * within it's collection, either fetch or create an instance of the private class.
13 * Each function called upon the public class will be forwarded to each instance
14 * of the private classes within the relevant element collection
15 **/
16(function($) {
17
18    var useWindow = window;
19    
20    // From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
21    if (!Object.keys) {
22        Object.keys = (function() {
23            'use strict';
24            var hasOwnProperty = Object.prototype.hasOwnProperty,
25                    hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'),
26                    dontEnums = [
27                        'toString',
28                        'toLocaleString',
29                        'valueOf',
30                        'hasOwnProperty',
31                        'isPrototypeOf',
32                        'propertyIsEnumerable',
33                        'constructor'
34                    ],
35                    dontEnumsLength = dontEnums.length;
36
37            return function(obj) {
38                if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) {
39                    throw new TypeError('Object.keys called on non-object');
40                }
41
42                var result = [], prop, i;
43
44                for (prop in obj) {
45                    if (hasOwnProperty.call(obj, prop)) {
46                        result.push(prop);
47                    }
48                }
49
50                if (hasDontEnumBug) {
51                    for (i = 0; i < dontEnumsLength; i++) {
52                        if (hasOwnProperty.call(obj, dontEnums[i])) {
53                            result.push(dontEnums[i]);
54                        }
55                    }
56                }
57                return result;
58            };
59        }());
60    }
61    
62    // Used to disable some features on IE8
63    var limited_mode = false;
64    var tick_duration = 200; // in ms
65    
66    var debug = (location.hash === "#debug");
67    function debug_log(msg) {
68        if (debug) {
69            console.log(msg);
70        }
71    }
72
73    var allUnits = ["Days", "Hours", "Minutes", "Seconds"];
74    var nextUnits = {
75        Seconds: "Minutes",
76        Minutes: "Hours",
77        Hours: "Days",
78        Days: "Years"
79    };
80    var secondsIn = {
81        Seconds: 1,
82        Minutes: 60,
83        Hours: 3600,
84        Days: 86400,
85        Months: 2678400,
86        Years: 31536000
87    };
88
89    /**
90     * Converts hex color code into object containing integer values for the r,g,b use
91     * This function (hexToRgb) originates from:
92     * http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb
93     * @param {string} hex color code
94     */
95    function hexToRgb(hex) {
96
97        // Verify already RGB (e.g. "rgb(0,0,0)") or RGBA (e.g. "rgba(0,0,0,0.5)")
98        var rgba = /^rgba?\(([\d]+),([\d]+),([\d]+)(,([\d\.]+))?\)$/;
99        if(rgba.test(hex)) {
100            var result = rgba.exec(hex);
101            return {
102                r: parseInt(result[1]),
103                g: parseInt(result[2]),
104                b: parseInt(result[3]),
105                a: parseInt(result[5] ? result[5] : 1)
106            };
107        }
108
109        // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
110        var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
111        hex = hex.replace(shorthandRegex, function(m, r, g, b) {
112            return r + r + g + g + b + b;
113        });
114
115        var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
116        return result ? {
117            r: parseInt(result[1], 16),
118            g: parseInt(result[2], 16),
119            b: parseInt(result[3], 16)
120        } : null;
121    }
122    
123    function isCanvasSupported() {
124        var elem = document.createElement('canvas');
125        return !!(elem.getContext && elem.getContext('2d'));
126    }
127
128    /**
129     * Function s4() and guid() originate from:
130     * http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
131     */
132    function s4() {
133        return Math.floor((1 + Math.random()) * 0x10000)
134                .toString(16)
135                .substring(1);
136    }
137
138    /**
139     * Creates a unique id
140     * @returns {String}
141     */
142    function guid() {
143        return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
144                s4() + '-' + s4() + s4() + s4();
145    }
146
147    /**
148     * Array.prototype.indexOf fallback for IE8
149     * @param {Mixed} mixed
150     * @returns {Number}
151     */
152    if (!Array.prototype.indexOf) {
153        Array.prototype.indexOf = function(elt /*, from*/)
154        {
155            var len = this.length >>> 0;
156
157            var from = Number(arguments[1]) || 0;
158            from = (from < 0)
159                    ? Math.ceil(from)
160                    : Math.floor(from);
161            if (from < 0)
162                from += len;
163
164            for (; from < len; from++)
165            {
166                if (from in this &&
167                        this[from] === elt)
168                    return from;
169            }
170            return -1;
171        };
172    }
173
174    function parse_date(str) {
175        var match = str.match(/^[0-9]{4}-[0-9]{2}-[0-9]{2}\s[0-9]{1,2}:[0-9]{2}:[0-9]{2}$/);
176        if (match !== null && match.length > 0) {
177            var parts = str.split(" ");
178            var date = parts[0].split("-");
179            var time = parts[1].split(":");
180            return new Date(date[0], date[1] - 1, date[2], time[0], time[1], time[2]);
181        }
182        // Fallback for different date formats
183        var d = Date.parse(str);
184        if (!isNaN(d))
185            return d;
186        d = Date.parse(str.replace(/-/g, '/').replace('T', ' '));
187        if (!isNaN(d))
188            return d;
189        // Cant find anything
190        return new Date();
191    }
192
193    function parse_times(diff, old_diff, total_duration, units, floor) {
194        var raw_time = {};
195        var raw_old_time = {};
196        var time = {};
197        var pct = {};
198        var old_pct = {};
199        var old_time = {};
200
201        var greater_unit = null;
202        for(var i = 0; i < units.length; i++) {
203            var unit = units[i];
204            var maxUnits;
205
206            if (greater_unit === null) {
207                maxUnits = total_duration / secondsIn[unit];
208            }
209            else {
210                maxUnits = secondsIn[greater_unit] / secondsIn[unit];
211            }
212
213            var curUnits = (diff / secondsIn[unit]);
214            var oldUnits = (old_diff / secondsIn[unit]);
215            
216            if(floor) {
217                if(curUnits > 0) curUnits = Math.floor(curUnits);
218                else curUnits = Math.ceil(curUnits);
219                if(oldUnits > 0) oldUnits = Math.floor(oldUnits);
220                else oldUnits = Math.ceil(oldUnits);
221            }
222            
223            if (unit !== "Days") {
224                curUnits = curUnits % maxUnits;
225                oldUnits = oldUnits % maxUnits;
226            }
227
228            raw_time[unit] = curUnits;
229            time[unit] = Math.abs(curUnits);
230            raw_old_time[unit] = oldUnits;
231            old_time[unit] = Math.abs(oldUnits);
232            pct[unit] = Math.abs(curUnits) / maxUnits;
233            old_pct[unit] = Math.abs(oldUnits) / maxUnits;
234
235            greater_unit = unit;
236        }
237
238        return {
239            raw_time: raw_time,
240            raw_old_time: raw_old_time,
241            time: time,
242            old_time: old_time,
243            pct: pct,
244            old_pct: old_pct
245        };
246    }
247
248    var TC_Instance_List = {};
249    function updateUsedWindow() {
250        if(typeof useWindow.TC_Instance_List !== "undefined") {
251            TC_Instance_List = useWindow.TC_Instance_List;
252        }
253        else {
254            useWindow.TC_Instance_List = TC_Instance_List;
255        }
256        initializeAnimationFrameHandler(useWindow);
257    };
258    
259    function initializeAnimationFrameHandler(w) {
260        var vendors = ['webkit', 'moz'];
261        for (var x = 0; x < vendors.length && !w.requestAnimationFrame; ++x) {
262            w.requestAnimationFrame = w[vendors[x] + 'RequestAnimationFrame'];
263            w.cancelAnimationFrame = w[vendors[x] + 'CancelAnimationFrame'];
264        }
265
266        if (!w.requestAnimationFrame || !w.cancelAnimationFrame) {
267            w.requestAnimationFrame = function(callback, element, instance) {
268                if (typeof instance === "undefined")
269                    instance = {data: {last_frame: 0}};
270                var currTime = new Date().getTime();
271                var timeToCall = Math.max(0, 16 - (currTime - instance.data.last_frame));
272                var id = w.setTimeout(function() {
273                    callback(currTime + timeToCall);
274                }, timeToCall);
275                instance.data.last_frame = currTime + timeToCall;
276                return id;
277            };
278            w.cancelAnimationFrame = function(id) {
279                clearTimeout(id);
280            };
281        }
282    };
283    
284
285    var TC_Instance = function(element, options) {
286        this.element = element;
287        this.container;
288        this.listeners = null;
289        this.data = {
290            paused: false,
291            last_frame: 0,
292            animation_frame: null,
293            interval_fallback: null,
294            timer: false,
295            total_duration: null,
296            prev_time: null,
297            drawn_units: [],
298            text_elements: {
299                Days: null,
300                Hours: null,
301                Minutes: null,
302                Seconds: null
303            },
304            attributes: {
305                canvas: null,
306                context: null,
307                item_size: null,
308                line_width: null,
309                radius: null,
310                outer_radius: null
311            },
312            state: {
313                fading: {
314                    Days: false,
315                    Hours: false,
316                    Minutes: false,
317                    Seconds: false
318                }
319            }
320        };
321
322        this.config = null;
323        this.setOptions(options);
324        this.initialize();
325    };
326
327    TC_Instance.prototype.clearListeners = function() {
328        this.listeners = { all: [], visible: [] };
329    };
330    
331    TC_Instance.prototype.addTime = function(seconds_to_add) {
332        if(this.data.attributes.ref_date instanceof Date) {
333            var d = this.data.attributes.ref_date;
334            d.setSeconds(d.getSeconds() + seconds_to_add);
335        }
336        else if(!isNaN(this.data.attributes.ref_date)) {
337            this.data.attributes.ref_date += (seconds_to_add * 1000);
338        }
339    };
340    
341    TC_Instance.prototype.initialize = function(clear_listeners) {
342        // Initialize drawn units
343        this.data.drawn_units = [];
344        for(var i = 0; i < Object.keys(this.config.time).length; i++) {
345            var unit = Object.keys(this.config.time)[i];
346            if (this.config.time[unit].show) {
347                this.data.drawn_units.push(unit);
348            }
349        }
350
351        // Avoid stacking
352        $(this.element).children('div.time_circles').remove();
353
354        if (typeof clear_listeners === "undefined")
355            clear_listeners = true;
356        if (clear_listeners || this.listeners === null) {
357            this.clearListeners();
358        }
359        this.container = $("<div>");
360        this.container.addClass('time_circles');
361        this.container.appendTo(this.element);
362        
363        // Determine the needed width and height of TimeCircles
364        var height = this.element.offsetHeight;
365        var width = this.element.offsetWidth;
366        if (height === 0)
367            height = $(this.element).height();
368        if (width === 0)
369            width = $(this.element).width();
370
371        if (height === 0 && width > 0)
372            height = width / this.data.drawn_units.length;
373        else if (width === 0 && height > 0)
374            width = height * this.data.drawn_units.length;
375        
376        // Create our canvas and set it to the appropriate size
377        var canvasElement = document.createElement('canvas');
378        canvasElement.width = width;
379        canvasElement.height = height;
380        
381        // Add canvas elements
382        this.data.attributes.canvas = $(canvasElement);
383        this.data.attributes.canvas.appendTo(this.container);
384        
385        // Check if the browser has browser support
386        var canvasSupported = isCanvasSupported();
387        // If the browser doesn't have browser support, check if explorer canvas is loaded
388        // (A javascript library that adds canvas support to browsers that don't have it)
389        if(!canvasSupported && typeof G_vmlCanvasManager !== "undefined") {
390            G_vmlCanvasManager.initElement(canvasElement);
391            limited_mode = true;
392            canvasSupported = true;
393        }
394        if(canvasSupported) {
395            this.data.attributes.context = canvasElement.getContext('2d');
396        }
397
398        this.data.attributes.item_size = Math.min(width / this.data.drawn_units.length, height);
399        this.data.attributes.line_width = this.data.attributes.item_size * this.config.fg_width;
400        this.data.attributes.radius = ((this.data.attributes.item_size * 0.8) - this.data.attributes.line_width) / 2;
401        this.data.attributes.outer_radius = this.data.attributes.radius + 0.5 * Math.max(this.data.attributes.line_width, this.data.attributes.line_width * this.config.bg_width);
402
403        // Prepare Time Elements
404        var i = 0;
405        for (var key in this.data.text_elements) {
406            if (!this.config.time[key].show)
407                continue;
408
409            var textElement = $("<div>");
410            textElement.addClass('textDiv_' + key);
411            textElement.css("top", Math.round(0.35 * this.data.attributes.item_size));
412            textElement.css("left", Math.round(i++ * this.data.attributes.item_size));
413            textElement.css("width", this.data.attributes.item_size);
414            textElement.appendTo(this.container);
415
416            var headerElement = $("<h4>");
417            headerElement.text(this.config.time[key].text); // Options
418            headerElement.css("font-size", Math.round(this.config.text_size * this.data.attributes.item_size));
419            headerElement.css("line-height", Math.round(this.config.text_size * this.data.attributes.item_size) + "px");
420            headerElement.appendTo(textElement);
421
422            var numberElement = $("<span>");
423            numberElement.css("font-size", Math.round(3 * this.config.text_size * this.data.attributes.item_size));
424            numberElement.css("line-height", Math.round(this.config.text_size * this.data.attributes.item_size) + "px");
425            numberElement.appendTo(textElement);
426
427            this.data.text_elements[key] = numberElement;
428        }
429
430        this.start();
431        if (!this.config.start) {
432            this.data.paused = true;
433        }
434        
435        // Set up interval fallback
436        var _this = this;
437        this.data.interval_fallback = useWindow.setInterval(function(){
438            _this.update.call(_this, true);
439        }, 100);
440    };
441
442    TC_Instance.prototype.update = function(nodraw) {
443        if(typeof nodraw === "undefined") {
444            nodraw = false;
445        }
446        else if(nodraw && this.data.paused) {
447            return;
448        }
449        
450        if(limited_mode) {
451            //Per unit clearing doesn't work in IE8 using explorer canvas, so do it in one time. The downside is that radial fade cant be used
452            this.data.attributes.context.clearRect(0, 0, this.data.attributes.canvas[0].width, this.data.attributes.canvas[0].hright);
453        }
454        var diff, old_diff;
455
456        var prevDate = this.data.prev_time;
457        var curDate = new Date();
458        this.data.prev_time = curDate;
459
460        if (prevDate === null)
461            prevDate = curDate;
462
463        // If not counting past zero, and time < 0, then simply draw the zero point once, and call stop
464        if (!this.config.count_past_zero) {
465            if (curDate > this.data.attributes.ref_date) {
466                for(var i = 0; i < this.data.drawn_units.length; i++) {
467                    var key = this.data.drawn_units[i];
468
469                    // Set the text value
470                    this.data.text_elements[key].text("0");
471                    var x = (i * this.data.attributes.item_size) + (this.data.attributes.item_size / 2);
472                    var y = this.data.attributes.item_size / 2;
473                    var color = this.config.time[key].color;
474                    this.drawArc(x, y, color, 0);
475                }
476                this.stop();
477                return;
478            }
479        }
480
481        // Compare current time with reference
482        diff = (this.data.attributes.ref_date - curDate) / 1000;
483        old_diff = (this.data.attributes.ref_date - prevDate) / 1000;
484
485        var floor = this.config.animation !== "smooth";
486
487        var visible_times = parse_times(diff, old_diff, this.data.total_duration, this.data.drawn_units, floor);
488        var all_times = parse_times(diff, old_diff, secondsIn["Years"], allUnits, floor);
489
490        var i = 0;
491        var j = 0;
492        var lastKey = null;
493
494        var cur_shown = this.data.drawn_units.slice();
495        for (var i in allUnits) {
496            var key = allUnits[i];
497
498            // Notify (all) listeners
499            if (Math.floor(all_times.raw_time[key]) !== Math.floor(all_times.raw_old_time[key])) {
500                this.notifyListeners(key, Math.floor(all_times.time[key]), Math.floor(diff), "all");
501            }
502
503            if (cur_shown.indexOf(key) < 0)
504                continue;
505
506            // Notify (visible) listeners
507            if (Math.floor(visible_times.raw_time[key]) !== Math.floor(visible_times.raw_old_time[key])) {
508                this.notifyListeners(key, Math.floor(visible_times.time[key]), Math.floor(diff), "visible");
509            }
510            
511            if(!nodraw) {
512                // Set the text value
513                this.data.text_elements[key].text(Math.floor(Math.abs(visible_times.time[key])));
514
515                var x = (j * this.data.attributes.item_size) + (this.data.attributes.item_size / 2);
516                var y = this.data.attributes.item_size / 2;
517                var color = this.config.time[key].color;
518
519                if (this.config.animation === "smooth") {
520                    if (lastKey !== null && !limited_mode) {
521                        if (Math.floor(visible_times.time[lastKey]) > Math.floor(visible_times.old_time[lastKey])) {
522                            this.radialFade(x, y, color, 1, key);
523                            this.data.state.fading[key] = true;
524                        }
525                        else if (Math.floor(visible_times.time[lastKey]) < Math.floor(visible_times.old_time[lastKey])) {
526                            this.radialFade(x, y, color, 0, key);
527                            this.data.state.fading[key] = true;
528                        }
529                    }
530                    if (!this.data.state.fading[key]) {
531                        this.drawArc(x, y, color, visible_times.pct[key]);
532                    }
533                }
534                else {
535                    this.animateArc(x, y, color, visible_times.pct[key], visible_times.old_pct[key], (new Date()).getTime() + tick_duration);
536                }
537            }
538            lastKey = key;
539            j++;
540        }
541
542        // Dont request another update if we should be paused
543        if(this.data.paused || nodraw) {
544            return;
545        }
546        
547        // We need this for our next frame either way
548        var _this = this;
549        var update = function() {
550            _this.update.call(_this);
551        };
552
553        // Either call next update immediately, or in a second
554        if (this.config.animation === "smooth") {
555            // Smooth animation, Queue up the next frame
556            this.data.animation_frame = useWindow.requestAnimationFrame(update, _this.element, _this);
557        }
558        else {
559            // Tick animation, Don't queue until very slightly after the next second happens
560            var delay = (diff % 1) * 1000;
561            if (delay < 0)
562                delay = 1000 + delay;
563            delay += 50;
564
565            _this.data.animation_frame = useWindow.setTimeout(function() {
566                _this.data.animation_frame = useWindow.requestAnimationFrame(update, _this.element, _this);
567            }, delay);
568        }
569    };
570
571    TC_Instance.prototype.animateArc = function(x, y, color, target_pct, cur_pct, animation_end) {
572        if (this.data.attributes.context === null)
573            return;
574
575        var diff = cur_pct - target_pct;
576        if (Math.abs(diff) > 0.5) {
577            if (target_pct === 0) {
578                this.radialFade(x, y, color, 1);
579            }
580            else {
581                this.radialFade(x, y, color, 0);
582            }
583        }
584        else {
585            var progress = (tick_duration - (animation_end - (new Date()).getTime())) / tick_duration;
586            if (progress > 1)
587                progress = 1;
588
589            var pct = (cur_pct * (1 - progress)) + (target_pct * progress);
590            this.drawArc(x, y, color, pct);
591
592            //var show_pct =
593            if (progress >= 1)
594                return;
595            var _this = this;
596            useWindow.requestAnimationFrame(function() {
597                _this.animateArc(x, y, color, target_pct, cur_pct, animation_end);
598            }, this.element);
599        }
600    };
601
602    TC_Instance.prototype.drawArc = function(x, y, color, pct) {
603        if (this.data.attributes.context === null)
604            return;
605
606        var clear_radius = Math.max(this.data.attributes.outer_radius, this.data.attributes.item_size / 2);
607        if(!limited_mode) {
608            this.data.attributes.context.clearRect(
609                    x - clear_radius,
610                    y - clear_radius,
611                    clear_radius * 2,
612                    clear_radius * 2
613                    );
614        }
615        
616        if (this.config.use_background) {
617            this.data.attributes.context.beginPath();
618            this.data.attributes.context.arc(x, y, this.data.attributes.radius, 0, 2 * Math.PI, false);
619            this.data.attributes.context.lineWidth = this.data.attributes.line_width * this.config.bg_width;
620
621            // line color
622            this.data.attributes.context.strokeStyle = this.config.circle_bg_color;
623            this.data.attributes.context.stroke();
624        }
625
626        // Direction
627        var startAngle, endAngle, counterClockwise;
628        var defaultOffset = (-0.5 * Math.PI);
629        var fullCircle = 2 * Math.PI;
630        startAngle = defaultOffset + (this.config.start_angle / 360 * fullCircle);
631        var offset = (2 * pct * Math.PI);
632
633        if (this.config.direction === "Both") {
634            counterClockwise = false;
635            startAngle -= (offset / 2);
636            endAngle = startAngle + offset;
637        }
638        else {
639            if (this.config.direction === "Clockwise") {
640                counterClockwise = false;
641                endAngle = startAngle + offset;
642            }
643            else {
644                counterClockwise = true;
645                endAngle = startAngle - offset;
646            }
647        }
648
649        this.data.attributes.context.beginPath();
650        this.data.attributes.context.arc(x, y, this.data.attributes.radius, startAngle, endAngle, counterClockwise);
651        this.data.attributes.context.lineWidth = this.data.attributes.line_width;
652
653        // line color
654        this.data.attributes.context.strokeStyle = color;
655        this.data.attributes.context.stroke();
656    };
657
658    TC_Instance.prototype.radialFade = function(x, y, color, from, key) {
659        // TODO: Make fade_time option
660        var rgb = hexToRgb(color);
661        var _this = this; // We have a few inner scopes here that will need access to our instance
662
663        var step = 0.2 * ((from === 1) ? -1 : 1);
664        var i;
665        for (i = 0; from <= 1 && from >= 0; i++) {
666            // Create inner scope so our variables are not changed by the time the Timeout triggers
667            (function() {
668                var delay = 50 * i;
669                var rgba = "rgba(" + rgb.r + ", " + rgb.g + ", " + rgb.b + ", " + (Math.round(from * 10) / 10) + ")";
670                useWindow.setTimeout(function() {
671                    _this.drawArc(x, y, rgba, 1);
672                }, delay);
673            }());
674            from += step;
675        }
676        if (typeof key !== undefined) {
677            useWindow.setTimeout(function() {
678                _this.data.state.fading[key] = false;
679            }, 50 * i);
680        }
681    };
682
683    TC_Instance.prototype.timeLeft = function() {
684        if (this.data.paused && typeof this.data.timer === "number") {
685            return this.data.timer;
686        }
687        var now = new Date();
688        return ((this.data.attributes.ref_date - now) / 1000);
689    };
690
691    TC_Instance.prototype.start = function() {
692        useWindow.cancelAnimationFrame(this.data.animation_frame);
693        useWindow.clearTimeout(this.data.animation_frame)
694
695        // Check if a date was passed in html attribute or jquery data
696        var attr_data_date = $(this.element).data('date');
697        if (typeof attr_data_date === "undefined") {
698            attr_data_date = $(this.element).attr('data-date');
699        }
700        if (typeof attr_data_date === "string") {
701            this.data.attributes.ref_date = parse_date(attr_data_date);
702        }
703        // Check if this is an unpause of a timer
704        else if (typeof this.data.timer === "number") {
705            if (this.data.paused) {
706                this.data.attributes.ref_date = (new Date()).getTime() + (this.data.timer * 1000);
707            }
708        }
709        else {
710            // Try to get data-timer
711            var attr_data_timer = $(this.element).data('timer');
712            if (typeof attr_data_timer === "undefined") {
713                attr_data_timer = $(this.element).attr('data-timer');
714            }
715            if (typeof attr_data_timer === "string") {
716                attr_data_timer = parseFloat(attr_data_timer);
717            }
718            if (typeof attr_data_timer === "number") {
719                this.data.timer = attr_data_timer;
720                this.data.attributes.ref_date = (new Date()).getTime() + (attr_data_timer * 1000);
721            }
722            else {
723                // data-timer and data-date were both not set
724                // use config date
725                this.data.attributes.ref_date = this.config.ref_date;
726            }
727        }
728
729        // Start running
730        this.data.paused = false;
731        this.update.call(this);
732    };
733
734    TC_Instance.prototype.restart = function() {
735        this.data.timer = false;
736        this.start();
737    };
738
739    TC_Instance.prototype.stop = function() {
740        if (typeof this.data.timer === "number") {
741            this.data.timer = this.timeLeft(this);
742        }
743        // Stop running
744        this.data.paused = true;
745        useWindow.cancelAnimationFrame(this.data.animation_frame);
746    };
747
748    TC_Instance.prototype.destroy = function() {
749        this.clearListeners();
750        this.stop();
751        useWindow.clearInterval(this.data.interval_fallback);
752        this.data.interval_fallback = null;
753        
754        this.container.remove();
755        $(this.element).removeAttr('data-tc-id');
756        $(this.element).removeData('tc-id');
757    };
758
759    TC_Instance.prototype.setOptions = function(options) {
760        if (this.config === null) {
761            this.default_options.ref_date = new Date();
762            this.config = $.extend(true, {}, this.default_options);
763        }
764        $.extend(true, this.config, options);
765
766        // Use window.top if use_top_frame is true
767        if(this.config.use_top_frame) {
768            useWindow = window.top;
769        }
770        else {
771            useWindow = window;
772        }
773        updateUsedWindow();
774        
775        this.data.total_duration = this.config.total_duration;
776        if (typeof this.data.total_duration === "string") {
777            if (typeof secondsIn[this.data.total_duration] !== "undefined") {
778                // If set to Years, Months, Days, Hours or Minutes, fetch the secondsIn value for that
779                this.data.total_duration = secondsIn[this.data.total_duration];
780            }
781            else if (this.data.total_duration === "Auto") {
782                // If set to auto, total_duration is the size of 1 unit, of the unit type bigger than the largest shown
783                for(var i = 0; i < Object.keys(this.config.time).length; i++) {
784                    var unit = Object.keys(this.config.time)[i];
785                    if (this.config.time[unit].show) {
786                        this.data.total_duration = secondsIn[nextUnits[unit]];
787                        break;
788                    }
789                }
790            }
791            else {
792                // If it's a string, but neither of the above, user screwed up.
793                this.data.total_duration = secondsIn["Years"];
794                console.error("Valid values for TimeCircles config.total_duration are either numeric, or (string) Years, Months, Days, Hours, Minutes, Auto");
795            }
796        }
797    };
798
799    TC_Instance.prototype.addListener = function(f, context, type) {
800        if (typeof f !== "function")
801            return;
802        if (typeof type === "undefined")
803            type = "visible";
804        this.listeners[type].push({func: f, scope: context});
805    };
806
807    TC_Instance.prototype.notifyListeners = function(unit, value, total, type) {
808        for (var i = 0; i < this.listeners[type].length; i++) {
809            var listener = this.listeners[type][i];
810            listener.func.apply(listener.scope, [unit, value, total]);
811        }
812    };
813
814    TC_Instance.prototype.default_options = {
815        ref_date: new Date(),
816        start: true,
817        animation: "smooth",
818        count_past_zero: true,
819        circle_bg_color: "#60686F",
820        use_background: true,
821        fg_width: 0.1,
822        bg_width: 1.2,
823        text_size: 0.07,
824        total_duration: "Auto",
825        direction: "Clockwise",
826        use_top_frame: false,
827        start_angle: 0,
828        time: {
829            Days: {
830                show: true,
831                text: "Days",
832                color: "#FC6"
833            },
834            Hours: {
835                show: true,
836                text: "Hours",
837                color: "#9CF"
838            },
839            Minutes: {
840                show: true,
841                text: "Minutes",
842                color: "#BFB"
843            },
844            Seconds: {
845                show: true,
846                text: "Seconds",
847                color: "#F99"
848            }
849        }
850    };
851
852    // Time circle class
853    var TC_Class = function(elements, options) {
854        this.elements = elements;
855        this.options = options;
856        this.foreach();
857    };
858
859    TC_Class.prototype.getInstance = function(element) {
860        var instance;
861
862        var cur_id = $(element).data("tc-id");
863        if (typeof cur_id === "undefined") {
864            cur_id = guid();
865            $(element).attr("data-tc-id", cur_id);
866        }
867        if (typeof TC_Instance_List[cur_id] === "undefined") {
868            var options = this.options;
869            var element_options = $(element).data('options');
870            if (typeof element_options === "string") {
871                element_options = JSON.parse(element_options);
872            }
873            if (typeof element_options === "object") {
874                options = $.extend(true, {}, this.options, element_options);
875            }
876            instance = new TC_Instance(element, options);
877            TC_Instance_List[cur_id] = instance;
878        }
879        else {
880            instance = TC_Instance_List[cur_id];
881            if (typeof this.options !== "undefined") {
882                instance.setOptions(this.options);
883            }
884        }
885        return instance;
886    };
887
888    TC_Class.prototype.addTime = function(seconds_to_add) {
889        this.foreach(function(instance) {
890            instance.addTime(seconds_to_add);
891        });
892    };
893    
894    TC_Class.prototype.foreach = function(callback) {
895        var _this = this;
896        this.elements.each(function() {
897            var instance = _this.getInstance(this);
898            if (typeof callback === "function") {
899                callback(instance);
900            }
901        });
902        return this;
903    };
904
905    TC_Class.prototype.start = function() {
906        this.foreach(function(instance) {
907            instance.start();
908        });
909        return this;
910    };
911
912    TC_Class.prototype.stop = function() {
913        this.foreach(function(instance) {
914            instance.stop();
915        });
916        return this;
917    };
918
919    TC_Class.prototype.restart = function() {
920        this.foreach(function(instance) {
921            instance.restart();
922        });
923        return this;
924    };
925
926    TC_Class.prototype.rebuild = function() {
927        this.foreach(function(instance) {
928            instance.initialize(false);
929        });
930        return this;
931    };
932
933    TC_Class.prototype.getTime = function() {
934        return this.getInstance(this.elements[0]).timeLeft();
935    };
936
937    TC_Class.prototype.addListener = function(f, type) {
938        if (typeof type === "undefined")
939            type = "visible";
940        var _this = this;
941        this.foreach(function(instance) {
942            instance.addListener(f, _this.elements, type);
943        });
944        return this;
945    };
946
947    TC_Class.prototype.destroy = function() {
948        this.foreach(function(instance) {
949            instance.destroy();
950        });
951        return this;
952    };
953
954    TC_Class.prototype.end = function() {
955        return this.elements;
956    };
957
958    $.fn.TimeCircles = function(options) {
959        return new TC_Class(this, options);
960    };
961}(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.