PageSourceSearch

https://www.waniyanpi.es/wp-content/themes/petio/js/jquery.circlestime.js

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