Return to Snippet

Revision: 24838
at March 12, 2010 06:14 by arantxaortega


Initial Code
/*!
 * epiClock 2.2 - Create Epic Clocks Easily
 *
 * Copyright (c) 2008 Eric Garside (http://eric.garside.name)
 * Dual licensed under:
 * 	MIT: http://www.opensource.org/licenses/mit-license.php
 *	GPLv3: http://www.opensource.org/licenses/gpl-3.0.html
 */

// Manager States
var EC_HALT = 'disable', EC_RUN = 'enable', EC_KILL = 'destroy',
// Clock Types
EC_CLOCK = 0, EC_COUNTDOWN = 1, EC_COUNTUP = 2, EC_ROLLOVER = 3, 
EC_EXPIRE = 4, EC_LOOP = 5, EC_STOPWATCH = 6, EC_HOLDUP = 7;
	
(function($){
	
	/**
	 * Setup a placeholder for clock styles
	 */
	$.epiclocks = {};
	
	var defaults = {
		epiClock: {
			offset: {
				hours: 0,
				minutes: 0,
				seconds: 0,
				days: 0,
				years: 0
			},
			arbitrary: {
				days: 0,
				years: 0
			},
			gmt: false,
			target: null,
			onTimer: null,
			onKill: null,
			onRender: function(v,val){v.html(val)},
			format: null,
			frame: {},
			dead: false,
			displace: 0,
			modifier: 0,
			variance: 0,
			daysadded: 0,
			paused: 0,
			tolerance: 0,
			selfLoc: -1,
			mode: EC_CLOCK,
			onSetup: null,
			stylesheet: null,
			containerClass: null,
			tpl: '<span></span>'
		},
		formats: [
			'F j, Y, g:i:s a',			// EC_CLOCK
			'V{d} x{h} i{m} s{s}',		// EC_COUNTDOWN
			'Q{y} K{d} x{h} i{m} s{s}',	// EC_COUNTUP
			'V{d} x{h} i{m} s{s}',		// EC_ROLLOVER
			'x{h} i{m} s{s}',			// EC_EXPIRE
			'i{m} s{s}',				// EC_LOOP
			'x{h} C{m} s{s}',			// EC_STOPWATCH
			'Q{y} K{d} x{h} i{m} s{s}'	// EC_HOLDUP
		]
	},
		// The current mode the clock manager is in
	current = null,
		// The interval timer for the clock
	loop = null,
		// The clocks we're managing
	clocks = [];
	
	/**
	 * jQuery Entry Point - CSS Loader
	 * 
	 * Provides an interface to include stylesheets dynamically
	 */
	$.cssIncludes = {};
	$.cssInclude = function(href, media){
		if ($.cssIncludes[href]) return false;
		
		$.cssIncludes[href] = true;
		media = media || 'screen';
		
		$('<link type="text/css" rel="stylesheet" href="' + href + '" media="' + media + '"/>')
			.appendTo('head');
	}
	
	/** 
	 * jQuery Entry Point - Clock Manager
	 * 
	 * Provides an interface for the user to pause, destroy, or resume/start all clocks.
	 */
	$.epiclock = $.fn.clocks = function(mode, precision, path){
		mode = mode || EC_RUN;
		precision = precision || 5e2;
		if (mode == current) return;
		
		switch (mode){
			case EC_KILL:
				$.each(clocks, function(){
					this.epiclock('kill')
				})
				clocks = [];
			case EC_HALT:
				if (loop){
					clearInterval(loop);
					loop = null;
				} 
				
				$.each(clocks, function(){
					this.epiclock('disable')
				})
				
				current = mode;
				break;
			case EC_RUN:
				if (!loop){
					cycleClocks(true);
					loop = setInterval(cycleClocks, precision);
				}
				current = mode;
				break;
		}
		
		return this;
	}
	
	function cycleClocks(enabled){
		process = enabled === true;
		$.each(clocks, function(i){
			if (process)
				this.epiclock('enable');
				
			this.data('epiClock').render();
		})
	}
	
	/** 
	 * jQuery Entry Point
	 * 
	 * Creates the clock displays
	 */
	$.fn.epiclock = function(options, predefined){
		var action = null;
		
		if (typeof options == 'string' && $.epiclocks && $.epiclocks[options])
			options = $.epiclocks[options];
		else if (predefined && $.epiclocks && $.epiclocks[predefined])
			options = $.extend(true, {}, $.epiclocks[predefined], options);
		 
		switch (options){
			case 'destroy':
				action = 'kill';
			case 'disable':
				action = action||'pause';
			case 'enable':
				action = action||'resume';
				return this.each(function(){
					var ec = $(this).data('epiClock');
					if (ec instanceof epiClock) ec[ action ]();
				})
			default:
				options = $.extend(true, {}, defaults.epiClock, options);
				break;
		}
		
		this.each(function(){
			var object = $(this),
				format = (options.format || defaults.formats[options.mode]).split(''),
				isBuffering = false,
				tpl = options.tpl || defaults.tpl, 
				buffer = '',
				clock = new epiClock(options, object);
			
			object.data('epiClock', clock);

			$.each(format, function(){
				x = this+'';
				switch (x){
					case ' ':
						if (!isBuffering)
							$(tpl).addClass('epiclock epiclock-spacer').appendTo(object);
						else buffer += x;
						break;
					case '{':
						isBuffering = true;
						break;
					case '}':
						isBuffering = false;
						$(tpl).addClass('epiclock').html(buffer).appendTo(object);
						buffer = '';
						break;
					default:
							// If we're buffering, this is label text
						if (isBuffering) buffer += x;
							// If it's a special character, it will be span updated
						else if (Date.prototype[x] || clock[x]) {
							clock.frame[x] = $(tpl)
								.addClass('epiclock epiclock-digit')
								.data('ec-encoding', x)
								.appendTo(object);
						}
						// If it's anything else, it's a single char label seperator
						else 
							$(tpl).addClass('epiclock epiclock-separator').html(x).appendTo(object);
						break;
				}
			});
			
			clock.selfLoc = clocks.push(object) - 1;
			if ($.isFunction(clock.onSetup)) clock.onSetup.call(clock, []);
			if (clock.stylesheet) $.cssInclude(clock.stylesheet);
			if (clock.containerClass) object.addClass(clock.containerClass);
		})
		
		return this;
	}
	
	/*
	 * Export the current time.
	 */
	$.fn.epiclockQuery = function(format){
		var ec = $(this).data('epiClock');
		
		if (!ec)
			return "";
	
		var format = format.split(''),
			buffer = '',
			isBuffering = false,
			x = '';
    
		$.each(format, function(){
			x = this+'';
			switch (x){
				case ' ':
					buffer += x;
					break;
				case '{':
					isBuffering = true;
					break;
				case '}':
					isBuffering = false;
					break;
				default:
					// If we're buffering, this is label text
					if (isBuffering) buffer += x;
					// If it's a special character, it will be span updated
					else if (Date.prototype[x] || ec[x]) {
						buffer += ($.isFunction(ec.now[x]) ? ec.now[x]() : ec[x]()) + ''
					}
					// If it's anything else, it's a single char label seperator
					else 
						buffer += x;
					break;
			}
		});

		return buffer;
	}
	
	function epiClock(options, element){
		if (this instanceof epiClock)
			return this.init(options, element);
		else return new epiClock(options, element);
	}
	
	epiClock.prototype = {
		Q:	function() { return this.arbitrary.years },
		E:	function() { return this.arbitrary.days },
		e:  function() { return this.arbitrary.days.pad(0) },
		zero: new Date(0),
		pause:	function(){
			if (this.dead) return;
			this.paused = new Date().valueOf();
			this.dead = true;
		},
		resume:	function(){
			if (!this.dead) return;
			if (this.mode == EC_STOPWATCH)
				this.displace += (this.paused - new Date().valueOf());
			this.paused = 0;
			this.dead = false;
		},
		kill:	function(){
			// Remove and Renumber Clocks Array
			clocks.splice(this.selfLoc,1);
			$.each(clocks, function(i){this.data('epiClock').selfLoc = i});
			
			// Call on kill, set dead
			if ($.isFunction(this.onKill)) this.onKill();
			this.dead = true;
		},
		init:	function(options, element){
			if (options.mode < EC_CLOCK || options.mode > EC_HOLDUP) 
				throw 'EPICLOCK_INVALID_MODE';
				
			var clock = this;
			$.each(options, function(k, v){
				clock[k] = v;
			});
			
			switch (this.mode){
				case EC_LOOP:
				case EC_EXPIRE:
					this.target = this.target || new Date();
				case EC_COUNTDOWN:
				case EC_ROLLOVER:
					this.modifier = -1;
					this.variance = 1;
					break;
				case EC_STOPWATCH:
					this.displace += this.calculateOffset() + (-1 * new Date().valueOf());
					this.dead = true;
					this.paused = new Date().valueOf();
					return;
				case EC_HOLDUP:
					this.variance = -1;
					this.modifier = 1;
					break;
				default:
					this.modifier = 1;
					this.variance = 0;
					break;
			}
			
			if (this.gmt)
				this.normalize();
			
			switch (true){
				case this.target instanceof Date:
					this.target = this.target.valueOf();
					break;
				case typeof this.target == 'string':
					this.target = new Date(this.target).valueOf();
					break;
			}
			
			this.displace += this.modifier * this.calculateOffset();
		},
		calculateOffset:	function(offset){
			offset = offset || this.offset;

			return (
				offset.years * 3157056e4 +
				offset.days * 864e5 +
				offset.hours * 36e5 +
				offset.minutes * 6e4 +
				(this.variance + offset.seconds) * 1e3
			);
		},
		normalize:	function(){
			this.displace += new Date().getTimezoneOffset()*6e4;
		},
		render:		function(){
			if (!this.tick()) return;
			var clock = this,
				time = (this.mode == EC_HOLDUP) ? this.zero : this.now;

			$.each(this.frame, function(k,v){
				var val = ($.isFunction(time[k]) ? time[k]() : clock[k]()) + '';
				if (v.data('last') != val) clock.onRender(v, val);
				v.data('last', val)
			})
		},
		tick:	function(){
			if (this.dead) return false;
			var now = new Date().valueOf() + this.displace;
			
			switch (this.mode){
				case EC_HOLDUP:
					if (this.target < now) this.mode = EC_COUNTUP;
				case EC_COUNTUP:
					now -= this.target;
					break;
				case EC_ROLLOVER:
					if (now > this.target) now = now - this.target;
					else now = this.target - now;
					break;
				case EC_COUNTDOWN:
				case EC_EXPIRE:
				case EC_LOOP:
					now = this.target - now;
					if (now < this.tolerance) return this.timerEnd();
					break;
			}
			
			this.now = new Date(now);
			
			var days = this.now.V();
			if (days <= this.daysadded) return true;
			
			this.daysadded = days;
			this.arbitrary.days += days;
			
			if (this.arbitrary.days < 365) return true;
			this.arbitrary.years += Math.floor(this.arbitrary.days/365.4 % 365.4);
			this.arbitrary.days = Math.floor(this.arbitrary.days % 365.4);
			
			return true;
		},
		timerEnd:	function(){
			if ($.isFunction(this.onTimer)) this.onTimer();
			
			switch (this.mode){
				case EC_COUNTDOWN:
				case EC_EXPIRE:
					this.kill();
					break;
				case EC_LOOP:
					this.displace += this.modifier * this.calculateOffset();
					return this.render();
				case EC_ROLLOVER:
					this.mode = EC_COUNTUP;
					return true;
			}
			
			this.now = new Date(0);
			return true;
		}
	};
	
	$.extend(String.prototype, {
		pad: function(s,l){ l=l||2; return this.length < l ? new Array(1+l-this.length).join(s) + this : this },
		rpad: function(s,l){ l=l||2; return this.length < l ? this + new Array(1+l-this.length).join(s) : this }
	})
	
	$.extend(Number.prototype, {
		pad: function(s,l){ return (this+'').pad(s,l) },
		rpad: function(s,l){ return (this+'').rpad(s,l) }
	})

	/** Prototype the Date function **/
	$.extend(Date.prototype, {
		// Assistance Definitions
		modCalc: function(mod1,mod2){return (Math.floor(Math.floor(this.valueOf()/1e3)/mod1)%mod2)},
		months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
		days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
		suffix: [null, 'st', 'nd', 'rd'],
		// Timer Functions
		V: function(){return this.modCalc(864e2,1e5)},		// Days
		v: function(){return this.V().pad(0)},				// Paded Days
		K: function(){return this.V()%365},					// Days Offset for Years
		k: function(){return this.K().pad(0)},				// Padded Offset Days
		X: function(){return this.modCalc(36e2,24)},		// Hours
		x: function(){return this.X().pad(0)},				// Padded Hours
		p: function(){return this.modCalc(60,60)},			// Minutes
		C: function(){return this.p().pad(0)},				// Padded Minutes
		// Day
		d: function() { return this.getDate().pad('0') },
		D: function() { return this.days[this.getDay()].substring(0,3) },
		j: function() { return this.getDate() },
		l: function() { return this.days[this.getDay()] },
		N: function() { return this.getDay() + 1 },
		S: function() { return this.suffix[this.getDate()] || 'th' },
		w: function() { return this.getDay() },
		z: function() { return Math.round((this-this.f())/864e5) },
		// Week
		W: function() { return Math.ceil(((((this-this.f())/864e5) + this.f().w())/7)) },
		// Month
		F: function() { return this.months[this.getMonth()]; },
		m: function() { return (this.getMonth()+1).pad(0) },
		M: function() { return this.months[this.getMonth()].substring(0,3) },
		n: function() { return this.getMonth() + 1 },
		// Year
		L: function() { var Y = this.Y(); return Y%4 ? false : Y%100 ? true : Y%400 ? false : true },
		f: function() { return new Date(this.getFullYear(),0,1) },
		Y: function() { return this.getFullYear() },
		y: function() { return ('' + this.getFullYear()).substr(2) },
		// Time
		a: function() { return this.getHours() < 12 ? 'am' : 'pm' },
		A: function() { return this.a().toUpperCase() },
		B: function() { return Math.floor((((this.getHours()) * 36e5) + (this.getMinutes() * 6e4) + (this.getSeconds() * 1e3))/864e2).pad(0,3) },
		g: function() { return this.getHours()%12 || 12 },
		G: function() { return this.getHours() },
		h: function() { return this.g().pad('0') },
		H: function() { return this.getHours().pad('0') },
		i: function() { return this.getMinutes().pad(0) },
		s: function() { return this.getSeconds().pad('0') },
		u: function() { return this.getTime()%1000 },
		// Timezone
		O: function() { var t = this.getTimezoneOffset() / 60; return (t >= 0 ? '+' : '-') + Math.abs(t).pad(0).rpad(0,4) },
		P: function() { var t = this.O(); return t.substr(0,3) + ':' + t.substr(3)},
		Z: function() { return this.getTimezoneOffset() * 60;},
		// Full Date/Time
		c: function() { return this.Y()+'-'+this.m()+'-'+this.d()+'T'+this.H()+':'+this.i()+':'+this.s()+this.P()},
		r: function() { return this.toString() },
		U: function() { return this.getTime() / 1000 }
	});
	
})(jQuery);

Initial URL


Initial Description
Documentación en: http://eric.garside.name/docs.html?p=epiclock

Initial Title
Epiclock in español (js)

Initial Tags
javascript

Initial Language
JavaScript