PageSourceSearch

https://manasamruddhi.in/assets/libs/timer/TimeCircles.js

js manasamruddhi.in collected 2026-10-05 04:33:01 UTC 33,589 bytes, 944 lines download raw bytes

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