source: spip-zone/_plugins_/timecircles/branches/v1.5.3/lib/timecircles.js.html @ 112669

Last change on this file since 112669 was 112614, checked in by l.oiseau2nuit@…, 2 years ago

v1.5.3.21 : sur suggestion de Jack31 on francise les chaines de langues 'jours' 'heures' etc ... que j'avais un peu occulté. Et un *gros* merci pierrrertux pour le coup de main

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