/**
 * Digital Fruition Javascript Tools
 *
 * A collection of common Digital Fruition functions, including the DFTools
 * library, and some assorted common code for
 * dealing with dates, arrays, events, numbers, etc.
 * 
 * All copyright Digital Fruition, LLC except where otherwise noted
 * 
 * A licence must be granted to you by Digital Fruition, LLC in order to make
 * use of this file. USING THIS FILE WITHOUT AGREEMENT TO DIGITAL FRUITION, LLC
 * POLICIES AND TERMS AND CONDITIONS IS EXPRESSLY FORBIDDEN
 * http://www.digitalfruition.com/tos
 *
 * @see http://www.digitalfruition.com/tos
 * @author Joshua Gitlin
 * @copyright 2001 - 2009 Digital Fruition, LLC. All Rights Reserved.
 * @license Propritary Digital Fruition License.
 */


/**
 * Mouse Wheel Event
 *
 * Extend the prototype event class to handle mouse wheel events
 *
 * @author unknown
 * @copyright unknown
 * @license unknown
 */
Object.extend(Event, {
	wheel:function (event){
		var delta = 0;
		if (!event) event = window.event;
		if (event.wheelDelta) {
			delta = event.wheelDelta/120; 
			if (window.opera) delta = -delta;
		} else if (event.detail) {delta = -event.detail/3;}
		return Math.round(delta); //Safari Round
	}
});


/**
 * Digital Frution Standard Tools Library
 *
 * A collection of standard digital fruition tools, including a console, an
 * error reporting system, math and string functions, and more.
 *
 * @version 1.0
 * @author Joshua Gitlin
 * @copyright 2001 - 2009 Digital Fruition, LLC. All Rights Reserved.
 * @license Propritary Digital Fruition License.
 */
var DFTools =
{
	version: '1.0',
	
	urls: {
		self: '',
		base: '/',
		reportError: 'problemReport.php'
	},
	
	setupUrlBase: function()
	{
		$A(document.getElementsByTagName("script")).findAll( function(s) {
			return (s.src && s.src.match(/DFTools\.js(\?.*)?$/))
		}).each( function(s) {
			DFTools.urls.self = s.src;
			DFTools.urls.base = DFTools.urls.self.replace(/[^\/]+\/DFTools\.js(\?.*)?$/,'');
		});
	},
	
	/**
	 * Create (if necessary) a given namespace
	 *
	 * Taken from http://weblogs.asp.net/mschwarz/archive/2005/08/26/423699.aspx
	 */
	namespace: function(ns)
	{
		var nsParts = ns.split(".");
		var root = window;
		
		for(var i=0; i<nsParts.length; i++)
		{
			if(typeof(root[nsParts[i]]) == "undefined"){
				root[nsParts[i]] = new Object();
			}
			
			root = root[nsParts[i]];
		}
	},
	
	
	
	/**
	 * Digital Fruition console
	 *
	 */
	console: (function()
	{
		var log = $A(), debug = $A();
		
		var consoleLog = function()
		{
			try
			{
				try
				{
					var entry = new Date;
					entry = entry.toString() + ": " + $A(arguments).join(' ');
				}
				catch(e)
				{
					entry = "DFTools.console: Error while building a log entry!"
				}
				log.push(entry);
				
				if(log.length >= DFTools.console.maxLogSize)
					log.shift();
				
				if(console && typeof(console.log) == 'function') {console.log.apply(console,arguments);}
			}
			catch(e) {}
		};

		var consoleError = function()
		{
			try
			{
				try
				{
					var entry = new Date;
					entry = entry.toString() + ": ERROR: " + $A(arguments).join(' ');
				}
				catch(e)
				{
					entry = "DFTools.console: Error while building a log error entry!"
				}
				log.push(entry);

				if(log.length >= DFTools.console.maxLogSize)
					log.shift();

				if(console && typeof(console.error) == 'function') {console.error.apply(console,arguments);}
				else if(console && typeof(console.log) == 'function') {console.log.apply(console,arguments);}
			}
			catch(e) {}
		};

		var consoleException = function(exceptionObject,functionName)
		{
			var msg = '';

			if(exceptionObject instanceof Error)
			{
				if(functionName)
					msg = functionName+': ';

				msg += "Caught a "+exceptionObject.name+" Exception: ";
				msg += exceptionObject.message;
			}
			else if((typeof Exception != "undefined") && (exceptionObject instanceof Exception))
			{
				msg += "Caught an Exception: ";

				if(typeof exceptionObject.message == "string")
					msg += exceptionObject.message;
			}
			else
			{
				msg += "Caught a "+(typeof exceptionObject);
			}

			var trace = DFTools.getStackTrace();
			trace.shift();

			consoleError(msg,exceptionObject,trace.join("  <-  "));
		}
		
		var debugLog = function()
		{
			if(DFTools.console.debugLogEnabled)
			{
				try
				{
					try
					{
						var entry = $A(arguments).join(' ');
					}
					catch(e)
					{
						entry = "DFTools.console: Error while building a debug log entry!"
					}
					debug.push(entry);
					
					if(debug.length >= DFTools.console.maxLogSize)
						debug.shift();
					
					if(console && typeof(console.debug) == 'function') {console.debug.apply(console,arguments);}
				}
				catch(e) {}
			}
		};
		
		var getConsoleLog = function()
		{
			return log.join("\n");
		};
		
		var getDebugLog = function()
		{
			return debug.join("\n");
		};
		
		var getAllLogs = function()
		{
			return {'log':getConsoleLog(), 'debug': getDebugLog()};
		};
				
		return {
			log: consoleLog,
			debug: debugLog,
			error: consoleError,
			logException: consoleException,

			getLog: getConsoleLog,
			getDebugLog: getDebugLog,
			getAll: getAllLogs,
			
			maxLogSize: 1000,
			debugLogEnabled: false
		};
	})(),
	
	cancelEvent: function(e)
	{
		e.stop();
	},
	
	writeEmail: function(u,d,tld)
	{
		document.write('<a href="');
		document.write('mailto:');
		document.write(u);
		document.write('@');
		document.write(d + '.' + tld);
		document.write('">');
		document.write(u);
		document.write('@');
		document.write(d + '.' + tld);
		document.write('</a>');
	},
	
	isValidEmail: function(string)
	{
		return /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(string);
	},

	/**
	 * Returns a string that is less than or equal to length.
	 *
	 * If $text is less than or equal to length then text is returned as is. If
	 * text is longer than length, then as many words as will fit in length chars
	 * are returned folled by an elipsis (...). The return value will always be less
	 * than or equal to length.
	 *
	 * @return string
	 * @param text
	 * @param length
	 * @author Joshua Gitlin <josh@digitalfruition.com>
	 * @access public
	*/
	summarize: function(text,length)
	{
		var append = '...', digest = '';

		if (text.length < length)
			digest = text;
		else
		{
			var endp = length - append.length;
			while (endp>0 && text.substr(endp-1,1).trim().length == 0)
			{
				endp--;
			}
			if(endp <= 0)
				endp = length - append.length;
			digest = text.substr(0,endp).trim() + append;
		}

		return digest;
	},
	
	fireEvent: function(element,event)
	{
		var evt;

		if (document.createEventObject)
		{
			// dispatch for IE
			
			evt = document.createEventObject();
			return element.fireEvent('on'+event,evt);
		}
		else
		{
			// dispatch for firefox + others
			
			evt = document.createEvent("HTMLEvents");
			evt.initEvent(event, true, true ); // event type,bubbling,cancelable
			return !element.dispatchEvent(evt);
		}
	},
	
	submitForm: function(form)
	{
		//var submit = document.createEvent('HTMLEvents');
		//submit.initEvent('submit',true,true);
		//myAddToCartForm.dispatchEvent(submit);
		
		return DFTools.fireEvent(form,'submit');
	},
	
	loadFlash: function(options,container)
	{
		options = $H(options);
		
		var flashObj = {embedAttrs: {}, params: {}, objAttrs: {}};
		
		$A(["pluginspage"]).each(function(a){flashObj.embedAttrs[a] = options.get(a);});
		
		$A(["src","movie"]).each(function(a){flashObj.embedAttrs["src"] = flashObj.params["movie"] = options.get(a);});
		
		$A(["onafterupdate",
			"onbeforeupdate",
			"onblur",
			"oncellchange",
			"onclick",
			"ondblClick",
			"ondrag",
			"ondragend",
			"ondragenter",
			"ondragleave",
			"ondragover",
			"ondrop",
			"onfinish",
			"onfocus",
			"onhelp",
			"onmousedown",
			"onmouseup",
			"onmouseover",
			"onmousemove",
			"onmouseout",
			"onkeypress",
			"onkeydown",
			"onkeyup",
			"onload",
			"onlosecapture",
			"onpropertychange",
			"onreadystatechange",
			"onrowsdelete",
			"onrowenter",
			"onrowexit",
			"onrowsinserted",
			"onstart",
			"onscroll",
			"onbeforeeditfocus",
			"onactivate",
			"onbeforedeactivate",
			"ondeactivate",
			"type",
			"codebase"]).each(function(a){if(typeof(options.get(a)) != 'undefined') {flashObj.objAttrs[a] = options.get(a);options.unset(a);}});

		$A(["width",
			"height",
			"align",
			"vspace", 
			"hspace",
			"class",
			"title",
			"accesskey",
			"name",
			"id",
			"tabindex"]).each(function(a){if(typeof(options.get(a)) != 'undefined') {flashObj.objAttrs[a] = flashObj.embedAttrs[a] = options.get(a);options.unset(a);}});
		
		options.each(function(pair){flashObj.params[pair.key] = flashObj.embedAttrs[pair.key] = pair.value;});
		
		flashObj.objAttrs["classid"] = "clsid:d27cdb6e-ae6d-11cf-96b8-444553540000";
		flashObj.embedAttrs["type"] = "application/x-shockwave-flash";
		
		
		var tag = '';
		
		tag = $H(flashObj.objAttrs).inject('<object ',function(str,pair){
			return str+' '+pair[0]+'="'+pair[1]+'"';
		});
		
		tag += $H(flashObj.params).inject('>',function(str,pair){
			return str+'<param name="'+pair[0]+'" value="'+pair[1]+'" /> ';
		});
		
		tag += $H(flashObj.embedAttrs).inject('<embed ',function(str,pair){
			return str+' '+pair[0]+'="'+pair[1]+'"';
		});
		
		tag += ' ></embed></object>';
		
		if((container = $(container)))
		{
			container.insert(tag);
		}
		
		return tag;
	},
	
	imagesLoaded: function(container)
	{
		container = $(container);
		
		if(!container)
			return false;
		
		var images = container.getElementsBySelector('img');
		
		return Try.these(
			function() {return images.pluck('complete').all();},
			function() {alert("Can't be sure if imagews are loaded, assuming yes...");return true;}
		);
	},
	
	/**
	 * Set the default text for fields, which will appear when the fields are empty and dissapear when
	 * they gain focus.
	 *
	 * This function takes a field or an array of fields and applies default text to them. This means
	 * that when the field is empty, the default text will appear and the field will get a class of
	 * .defaultText, allowing the text to change color or style. When the field gains focus, the default
	 * text and class are removed.
	 *
	 * You may pass either a single element, and element ID, or an array of elements / element IDs. If
	 * the text field is omitted, the text for each field will be pulled from the field's title attribute
	 *
	 * @param Array/Element fields
	 * @param string [text]
	 * @return value
	 */
	defaultFieldText: function()
	{
		var defaultFieldTextHash = $H({});
		
		var defaultText = '';
		
		var defaultFieldTextMethods = $H(
		{
			getDefaultText: function()
			{
				return defaultFieldTextHash.get(this.identify());
			},
			
			setDefaultText: function(text)
			{
				defaultFieldTextHash.set(this.identify(),text);
			},
			
			defaultTextUpdate: function()
			{
				val = $F(this);
				var defaultText = this.getDefaultText();
				
				if(!val.length || val == defaultText)
				{
					this.value = defaultText;
					this.addClassName('defaultText');
				}
				else
				{
					this.removeClassName('defaultText');
				}
			},
			
			defaultTextClear: function()
			{
				val = $F(this);
				var defaultText = this.getDefaultText();
				
				if(val == defaultText)
				{
					this.value = '';
				}
				this.removeClassName('defaultText');
			}
		});
		
		var applyDefaultFieldText = function(field)
		{
			field = $(field);
			
			if(field)
			{
				defaultFieldTextMethods.each(function (pair) {field[pair.key] = pair.value.bind(field);});
				
				field.setDefaultText(defaultText ? defaultText : field.title);
				
				field.observe('blur',field.defaultTextUpdate);
				field.observe('focus',field.defaultTextClear);
				
				field.defaultTextUpdate();
			}
		};
		
		var defaultFieldText = function(fields,text)
		{
			defaultText = text;
			
			if(!(fields instanceof Array)) {fields = [fields];}
			
			fields = $A(fields);
		
			fields.each(applyDefaultFieldText);
		};
		
		return defaultFieldText;
	}(),
	
	var_dump: function(v,seen)
	{
		var dump = {type:'unknown',value:''};
		
		try
		{
			if(!seen)
				seen = $A();
			
			dump.type = typeof v;
			
			switch (dump.type)
			{
				case "function":
					return false;
				
				case "undefined":
				case "unknown":
					dump.value = 'unknown';
				
				case "boolean":
				case "number":
				case "string":
					dump.value = v;
			}
			
			if (v === null)
			{
				dump.type = 'null';
				dump.value = null;
			}
			else if (Object.isElement(v))
			{
				dump.type = 'Element';
				dump.value = 'N/A';
			}
			else if (dump.type == 'object')
			{
				var index = seen.indexOf(v);
				
				if(index != -1)
				{
					++index;
					dump.value = "(*recusion*)";
				}
				else
				{
					index = seen.push(v);
					
					dump.value = {};
					for (var property in v)
					{
						if (!Object.isUndefined(v[property]))
						{
							dump.value[property] = DFTools.var_dump(v[property],seen);
							
							if(!dump.value[property])
								delete dump.value[property];
						}
					}
				}
				
				dump.type += " (#" + index + ")";
			}
		}
		catch(ex)
		{
			dump = {type:'unknown',value:'Error!'};
		}
		
		return dump;
	},
	
	reportError: function(error,module,data,alert_support)
	{
		var msg;
		
		try
		{
			msg = "Unknown Error";
			
			if(error.message) {msg = error.message;}
			
			DFTools.console.log("DFTools Error Report:",msg,error,module,data);
			
			var post = {
				errorMsg: msg,
				module: module ? module : 'Unknown',
				report: {
					browserName: BrowserDetect.name,
					browserOS: BrowserDetect.OS,
					browserVersion: BrowserDetect.version,
					location: window.location.toString()
				}
			};
			
			
			if(error.name) {post.errorName = error.name;}
			
			if(error.fileName) {post.fileName = error.fileName;}
			if(error.lineNumber) {post.lineNumber = error.lineNumber;}
			if(error.number) {post.errorNumber = error.number;}
			
			if(error.error_function) {post.report.errorFunction = error.error_function;}
			if(error.error_handler) {post.report.errorHandler = error.error_handler;}
			
			if(error.description) {post.report.errorDescription = error.description;}
			if(error.stack) {post.report.errorStack = error.Stack;}
			
			if(window.innerHeight) {
				post.report.windowHeight = window.innerHeight;
				post.report.windowWidth = window.innerWidth;
			}
			
			if(typeof(window.pageXOffset) != 'undefinied') {
				post.report.pageXOffset = window.pageXOffset;
				post.report.pageYOffset = window.pageYOffset;
			}
			
			if(document.referrer) {
				post.report.referrer = document.referrer;
			}
			
			if(data)
				post.report.moduleData = DFTools.var_dump(data);
			
			post.report.logs = DFTools.console.getAll();
			
			post.report.stack = $A(DFTools.getStackTrace()).join("\n");
			
			post.report = Object.toJSON(post.report);

			post.alert_support = alert_support?'1':'0';
			
			new Ajax.Request(DFTools.urls.base+DFTools.urls.reportError,{method:'post', parameters: post});
		}
		catch (error_error)
		{
			// Damn, we just can't win. Error when reporting an error!
			// Just give up and ignore this error.
			
			msg = "Unknown Error";
			
			if(error_error.message) {msg = error_error.message;}
			
			DFTools.console.error("Error while reporting en error!!",msg,error_error);
		}
	},
	
	/**
	 * Set a cookie.
	 *
	 * @url http://www.quirksmode.org/js/cookies.html
	 */
	setCookie: function(name,value,days)
	{
		var expires;

		if (days)
		{
			var date = new Date();
			date.setTime(date.getTime()+(days*24*60*60*1000));
			expires = "; expires="+date.toGMTString();
		}
		else
			expires = "";
		document.cookie = name+"="+value+expires+"; path=/";
	},
	
	/**
	 * Get the value of a cookie.
	 *
	 * @url http://www.quirksmode.org/js/cookies.html
	 * @return string
	 */
	getCookie: function(name)
	{
		var nameEQ = name + "=";
		var ca = document.cookie.split(';');
		for(var i=0;i < ca.length;i++) {
			var c = ca[i];
			while (c.charAt(0)==' ') c = c.substring(1,c.length);
			if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
		}
		return null;
	},
	
	/**
	 * Remove a cookie.
	 *
	 * @url http://www.quirksmode.org/js/cookies.html
	 */
	deleteCookie: function(name)
	{
		DFTools.setCookie(name,"",-1);
	},
	
	
	format: 
	{
		bytes: function(n)
		{
			var units = $A(['KB','MB','GB','TB']);
			var increment = 1024;
			
			var precision = 2;
			
			var unit = 'b';
			
			for(var i=0; i<units.length && n >= increment; ++i)
			{
				n /= increment;
				unit = units[i];
			}
			
			var mult = Math.pow(10,precision);
			
			n = Math.ceil(n*mult)/mult;
			
			return n+unit;
		}
	}
}

DFTools.cancelEvent = DFTools.cancelEvent.bindAsEventListener(DFTools);





/**
 * Page Unload System
 *
 * The DFTools Page Unload System is a way to detect is the page is being
 * unloaded (due to the user closing the window/tab, clicking back/forward,
 * or taking any other action to navigate away from the page). 
 *
 * DFTools.pageUnload can be queried to find out if the page is being unloaded
 * (I.E. to determine if that is the cause of a failure for an AJAX request to
 * complete) but it can also be used to observe the unload event and cancel it
 * by prompting the user.
 *
 * @author Joshua Gitlin
 * @copyright 2001 - 2009 Digital Fruition, LLC. All Rights Reserved.
 * @license Propritary Digital Fruition License.
 */
DFTools.pageUnload = new function()
{
	var isHappening = false;
	
	var okToUnload = true;
	
	var unloadWarningFunctions = $A();
	
	var unloadWarnings = $A();
	
	var unloadWarningPopin = false;
	
	var resetHappeningFunction = function()
	{
		isHappening = false;
	}
	
	var mySetupUnloadWarningPopin = function()
	{
		var css = 'PageUnload';
		var html = '<h1>Are you sure you want to leave this page?</h1><div class="msgs"></div>';
		
		unloadWarningPopin = new com.digitalfruition.ThemeObject.Popin({
			mask: true,
			cssClass: css,
			innerHTML: html,
			closeLink: false,
			showEffect: false,
			hideEffect: false,
			maskShowEffect: false,
			maskHideEffect: false
		});
	}
	
	var resetHappening = new PeriodicalExecuter(resetHappeningFunction,1);

	var beforeunloadHandler = function()
	{
		isHappening = true;

		if(!this.okToUnload())
		{
			if(unloadWarningPopin)
			{
				unloadWarningPopin.element().down('div.msgs').update(unloadWarnings.join("<br>"));
				unloadWarningPopin.show();
				unloadWarningPopin.hide.bind(unloadWarningPopin).defer();
			}
			
			return unloadWarnings.join("\n\n");
		}

		return undefined;
	}.bind(this);

	//Event.observe(window,'beforeunload',beforeunloadHandler);

	window.onbeforeunload = beforeunloadHandler;

	this.isHappening = function()
	{
		return isHappening;
	}

	this.okToUnload = function()
	{
		unloadWarnings = unloadWarningFunctions.collect(function(f){return f();}).without(false);

		okToUnload = !unloadWarnings.any();

		return okToUnload;
	}

	this.addUnloadWarning = function(f)
	{
		if(typeof(f) == 'function')
		{
			unloadWarningFunctions.push(f);
		}
	}

	this.removeUnloadWarning = function(f)
	{
		if(typeof(f) == 'function')
		{
			unloadWarningFunctions = unloadWarningFunctions.without(f);
		}
	}
	
	document.observe('dom:loaded',mySetupUnloadWarningPopin);
}



var writeEmail = DFTools.writeEmail;


/**
 * Quirksmode Browser detection Library
 *
 * Provides browser detection capabilities.
 *
 * @author Peter-Paul Koch
 * @copyright 2009 Quirksmode. I don't believe in copyrights for JavaScript or CSS solutions.
 * @license Open: http://www.quirksmode.org/about/copyright.html
 * @see http://www.quirksmode.org/js/detect.html
 */
var BrowserDetect = {
	init: function () {
		this.browser = this.searchString(this.dataBrowser) || "An unknown browser";
		this.version = this.searchVersion(navigator.userAgent)
			|| this.searchVersion(navigator.appVersion)
			|| "an unknown version";
		this.OS = this.searchString(this.dataOS) || "an unknown OS";
	},
	searchString: function (data) {
		for (var i=0;i<data.length;i++)	{
			var dataString = data[i].string;
			var dataProp = data[i].prop;
			this.versionSearchString = data[i].versionSearch || data[i].identity;
			if (dataString) {
				if (dataString.indexOf(data[i].subString) != -1)
					return data[i].identity;
			}
			else if (dataProp)
				return data[i].identity;
		}

		return false;
	},
	searchVersion: function (dataString) {
		var index = dataString.indexOf(this.versionSearchString);
		if (index == -1) return null;
		return parseFloat(dataString.substring(index+this.versionSearchString.length+1));
	},
	dataBrowser: [
		{string: navigator.userAgent,
			subString: "OmniWeb",
			versionSearch: "OmniWeb/",
			identity: "OmniWeb"
		},
		{
			string: navigator.vendor,
			subString: "Apple",
			identity: "Safari"
		},
		{
			prop: window.opera,
			identity: "Opera"
		},
		{
			string: navigator.vendor,
			subString: "iCab",
			identity: "iCab"
		},
		{
			string: navigator.vendor,
			subString: "KDE",
			identity: "Konqueror"
		},
		{
			string: navigator.userAgent,
			subString: "Firefox",
			identity: "Firefox"
		},
		{
			string: navigator.vendor,
			subString: "Camino",
			identity: "Camino"
		},
		{		// for newer Netscapes (6+)
			string: navigator.userAgent,
			subString: "Netscape",
			identity: "Netscape"
		},
		{
			string: navigator.userAgent,
			subString: "MSIE",
			identity: "Explorer",
			versionSearch: "MSIE"
		},
		{
			string: navigator.userAgent,
			subString: "Gecko",
			identity: "Mozilla",
			versionSearch: "rv"
		},
		{ 		// for older Netscapes (4-)
			string: navigator.userAgent,
			subString: "Mozilla",
			identity: "Netscape",
			versionSearch: "Mozilla"
		}
	],
	dataOS : [
		{
			string: navigator.platform,
			subString: "Win",
			identity: "Windows"
		},
		{
			string: navigator.platform,
			subString: "Mac",
			identity: "Mac"
		},
		{
			string: navigator.platform,
			subString: "Linux",
			identity: "Linux"
		}
	]

};
BrowserDetect.init();


/**
 * WebKit Version Detection Library
 *
 * provides version information about browsers using WebKit, including Apple's
 * Safari and Google's Chrome.
 *
 * @author Unknown
 * @copyright unknown
 * @license Open
 * @see http://trac.webkit.org/wiki/DetectingWebKit
 */
var WebKitDetect = {  };

// If the user agent is WebKit, returns true. Otherwise, returns false.
WebKitDetect.isWebKit = function isWebKit()
{
    return RegExp(" AppleWebKit/").test(navigator.userAgent);
}

// If the user agent is WebKit, returns an array of numbers matching the "." separated 
// fields in the WebKit version number, with an "isNightlyBuild" property specifying
// whether the user agent is a WebKit nightly build. Otherwise, returns null.
//
// Example: 418.10.1 => [ 418, 10, 1 ] isNightlyBuild: false
WebKitDetect.version = function version() 
{
    /* Some example strings: 
            Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en) AppleWebKit/418.9.1 (KHTML, like Gecko) Safari/419.3
            Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en) AppleWebKit/420+ (KHTML, like Gecko) Safari/521.32
     */
     
    // grab (AppleWebKit/)(xxx.x.x)
    var webKitFields = RegExp("( AppleWebKit/)([^ ]+)").exec(navigator.userAgent);
    if (!webKitFields || webKitFields.length < 3)
        return null;
    var versionString = webKitFields[2];

    var isNightlyBuild = versionString.indexOf("+") != -1;

    // Remove '+' or any other stray characters
    var invalidCharacter = RegExp("[^\\.0-9]").exec(versionString);
    if (invalidCharacter)
        versionString = versionString.slice(0, invalidCharacter.index);
    
    var version = versionString.split(".");
    version.isNightlyBuild = isNightlyBuild;
    return version;
}

// If the user agent is a WebKit version greater than or equal to the version specified
// in the string minimumString, returns true. Returns false otherwise. minimumString 
// defaults to "".
//
// Example usage: WebKitDetect.versionIsAtLeast("418.10.1")
WebKitDetect.versionIsAtLeast = function versionIsAtLeast(minimumString)
{
    function toIntOrZero(s) 
    {
        var toInt = parseInt(s);
        return isNaN(toInt) ? 0 : toInt;
    }

    if (minimumString === undefined)
        minimumString = "";
    
    var minimum = minimumString.split(".");
    var version = WebKitDetect.version();

    if (!version)
        return false;
        
    if (version.isNightlyBuild)
        return true;

    for (var i = 0; i < minimum.length; i++) {
        var versionField = toIntOrZero(version[i]);
        var minimumField = toIntOrZero(minimum[i]);
        
        if (versionField > minimumField)
            return true;
        if (versionField < minimumField)
            return false;
    }

    return true;
}

WebKitDetect.isMobile = function isMobile()
{
    return WebKitDetect.isWebKit() && RegExp(" Mobile/").test(navigator.userAgent);
}

WebKitDetect.mobileDevice = function mobileDevice()
{
    if (!WebKitDetect.isMobile())
        return null;
        
    var fields = RegExp("(Mozilla/5.0 \\()([^;]+)").exec(navigator.userAgent);
    if (!fields || fields.length < 3)
        return null;
    return fields[2];
}

// Example: 1C28 => [ 1, C, 28 ]
WebKitDetect._mobileVersion = function _mobileVersion(versionString)
{
    var fields = RegExp("([0-9]+)([A-Z]+)([0-9]+)").exec(versionString);
    if (!fields || fields.length != 4)
        return null;
    return [ fields[1], fields[2], fields[3] ];
}

WebKitDetect.mobileVersion = function mobileVersion()
{
    // grab (Mobile/)(nxnnn)
    var fields = RegExp("( Mobile/)([^ ]+)").exec(navigator.userAgent);
    if (!fields || fields.length < 3)
        return null;
    var versionString = fields[2];
    
    return WebKitDetect._mobileVersion(versionString);
}

WebKitDetect.mobileVersionIsAtLeast = function mobileVersionIsAtLeast(minimumString)
{
    function toIntOrZero(s) 
    {
        var toInt = parseInt(s);
        return isNaN(toInt) ? 0 : toInt;
    }

    if (minimumString === undefined)
        minimumString = "";

    var minimum = WebKitDetect._mobileVersion(minimumString);
    var version = WebKitDetect.mobileVersion();

    if (!version)
        return false;
        
    var majorVersInt = toIntOrZero(version[0]);
    var majorMinInt = toIntOrZero(minimum[0]);
    if (majorVersInt > majorMinInt)
        return true;
    if (majorVersInt < majorMinInt)
        return false;
    
    var majorVersAlpha = version[1];
    var majorMinAlpha = minimum[1];
    if (majorVersAlpha > majorMinAlpha)
        return true;
    if (majorVersAlpha < majorMinAlpha)
        return false;
    
    var minorVersInt = toIntOrZero(version[2]);
    var minorMinInt = toIntOrZero(minimum[2]);
    if (minorVersInt > minorMinInt)
        return true;
    if (minorVersInt < minorMinInt)
        return false;
    
    return true;
}


/**
 * Digital Frution Stack Trace
 *
 * Returns a Javascript function call stack trace. Taken from the web and
 * adapted for our needs. License unknown.
 *
 * @return array
 * @author Unknown
 * @copyright Unknown
 * @license Unknown
 */
DFTools.getStackTrace = (function ()
{
	var mode;
	/*
	try {(0)()} catch (e) {
		mode = e.stack ? 'Firefox' : window.opera ? 'Opera' : 'Other';
	}
	*/
	
	mode = BrowserDetect.browser;
	
	switch (mode)
	{
		case 'Firefox' :return function ()
			{
				try {(0)()} catch (e)
				{
					return e.stack.replace(/^.*?\n/,'').replace(/(?:\n@:0)?\s+$/m,'').replace(/^\(/gm,'{anonymous}(').split("\n");
				}
			};
		
		case 'Opera' :return function () 
			{
				try {(0)()} catch (e)
				{
					var lines = e.message.split("\n"),ANON = '{anonymous}',lineRE = /Line\s+(\d+).*?in\s+(http\S+)(?:.*?in\s+function\s+(\S+))?/i,i,j,len;
					
					for (i=4,j=0,len=lines.length; i<len; i+=2)
					{
						if (lineRE.test(lines[i]))
						{
							lines[j++] = (RegExp.$3 ? RegExp.$3 + '()@' + RegExp.$2 + RegExp.$1 : ANON + RegExp.$2 + ':' + RegExp.$1) + ' -- ' + lines[i+1].replace(/^\s+/,'');
						}
					}
					
					lines.splice(j,lines.length-j);
					return lines;
				}
			};
		
		default :return function ()
			{
				var curr = arguments.callee.caller, FUNC = 'function', ANON = "{anonymous}", fnRE = /function\s*([\w\-$]+)?\s*\(/i, stack = [],j=0,fn,args,i;
				
				while (curr)
				{
					fn    = fnRE.test(curr.toString()) ? RegExp.$1 || ANON : ANON;
					args  = stack.slice.call(curr.arguments);
					i     = args.length;
					
					while (i--)
					{
						switch (typeof args[i])
						{
							case 'string'  :args[i] = '"'+args[i].replace(new RegExp('"','g'),'\\"')+'"';break;
							case 'function':args[i] = FUNC;break;
						}
					}
					
					stack[j++] = fn + '(' + args.join() + ')';
					curr = curr.caller;
				}
				
				return stack;
			};
	}
})();









/*
* FastInit: system for observing when the DOM is ready (but images have not yet loaded)
*
* @copyright Copyright (c) 2007 Andrew Tetlaw
*/
var FastInit = {
	onload : function() {
		if (FastInit.done) {return;}
		FastInit.done = true;
		for(var x = 0, al = FastInit.f.length; x < al; x++) {
			FastInit.f[x]();
		}
	},
	addOnLoad : function() {
		var a = arguments;
		for(var x = 0, al = a.length; x < al; x++) {
			if(typeof a[x] === 'function') {
				if (FastInit.done ) {
					a[x]();
				} else {
					FastInit.f.push(a[x]);
				}
			}
		}
	},
	listen : function() {
		if (/WebKit|khtml/i.test(navigator.userAgent)) {
			FastInit.timer = setInterval(function() {
				if (/loaded|complete/.test(document.readyState)) {
					clearInterval(FastInit.timer);
					delete FastInit.timer;
					FastInit.onload();
				}}, 10);
		} else if (document.addEventListener) {
			document.addEventListener('DOMContentLoaded', FastInit.onload, false);
		} else if(!FastInit.iew32) {
			if(window.addEventListener) {
				window.addEventListener('load', FastInit.onload, false);
			} else if (window.attachEvent) {
				return window.attachEvent('onload', FastInit.onload);
			}
		}

		return false;
	},
	f:[],done:false,timer:null,iew32:false
};
/*@cc_on @*/
/*@if (@_win32)
FastInit.iew32 = true;
document.write('<script id="__ie_onload" defer src="' + ((location.protocol == 'https:') ? '//0' : 'javascript:void(0)') + '"><\/script>');
document.getElementById('__ie_onload').onreadystatechange = function(){if (this.readyState == 'complete') { FastInit.onload(); }};
/*@end @*/
FastInit.listen();





//+ Jonas Raoni Soares Silva
//@ http://jsfromhell.com/string/pad [v1.0]

String.prototype.pad = function(l, s, t){
	return s || (s = " "), (l -= this.length) > 0 ? (s = new Array(Math.ceil(l / s.length)
		+ 1).join(s)).substr(0, t = !t ? l : t == 1 ? 0 : Math.ceil(l / 2))
		+ this + s.substr(0, l - t) : this;
};





/* Date/Time Format v0.2; MIT-style license
By Steven Levithan <http://stevenlevithan.com> */

/**
 * Date/Time Format Library
 *
 * Excellent date/time formatting library by Steven Levithan
 *
 * @author Joshua Gitlin
 * @copyright 2009 Steven Levithan <http://stevenlevithan.com>
 * @see http://stevenlevithan.com
 * @version v0.2
 * @license MIT-style license.
 */
Date.prototype.format = function(mask) {
	var d = this; // Needed for the replace() closure
	
	// If preferred, zeroise() can be moved out of the format() method for performance and reuse purposes
	var zeroize = function (value, length) {
		if (!length) length = 2;
		value = String(value);
		for (var i = 0, zeros = ''; i < (length - value.length); i++) {
			zeros += '0';
		}
		return zeros + value;
	};
	
	return mask.replace(/"[^"]*"|'[^']*'|\b(?:d{1,4}|m{1,4}|yy(?:yy)?|([hHMs])\1?|TT|tt|[lL])\b/g, function($0) {
		switch($0) {
			case 'd':return d.getDate();
			case 'dd':return zeroize(d.getDate());
			case 'ddd':return ['Sun','Mon','Tue','Wed','Thr','Fri','Sat'][d.getDay()];
			case 'dddd':return ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'][d.getDay()];
			case 'm':return d.getMonth() + 1;
			case 'mm':return zeroize(d.getMonth() + 1);
			case 'mmm':return ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'][d.getMonth()];
			case 'mmmm':return ['January','February','March','April','May','June','July','August','September','October','November','December'][d.getMonth()];
			case 'yy':return String(d.getFullYear()).substr(2);
			case 'yyyy':return d.getFullYear();
			case 'h':return d.getHours() % 12 || 12;
			case 'hh':return zeroize(d.getHours() % 12 || 12);
			case 'H':return d.getHours();
			case 'HH':return zeroize(d.getHours());
			case 'M':return d.getMinutes();
			case 'MM':return zeroize(d.getMinutes());
			case 's':return d.getSeconds();
			case 'ss':return zeroize(d.getSeconds());
			case 'l':return zeroize(d.getMilliseconds(), 3);
			case 'L':var m = d.getMilliseconds();
					if (m > 99) m = Math.round(m / 10);
					return zeroize(m);
			case 'tt':return d.getHours() < 12 ? 'am' : 'pm';
			case 'TT':return d.getHours() < 12 ? 'AM' : 'PM';
			// Return quoted strings with the surrounding quotes removed
			default:return $0.substr(1, $0.length - 2);
		}
	});
};









FastInit.addOnLoad(DFTools.setupUrlBase);

