//TODO
// add good comments to all the code


/**
* Creates some always-util features.
* 
* <p>Revision history:
* <ul>
* 	<li>15/06/2011 v0.0.1. - first version, feel free to improve it with other features.</li>
* </ul></p>
*/
function addBasicUtils() {
	
	// creates the ECMAScript 5 method Array.isArray() if the environment hasn't it
	if (typeof Array.isArray === "undefined") {
		Array.isArray = function (arg) {
			return Object.prototype.toString.call(arg) === "[object Array]";
		};
	}
}


/**
* Stores the jQuery instances for future use.
* 
* @param key, the jQuery selector.
* @param value, if you want to force a value (eg. for resetting the jQuery instance stored).
* @return the jQuery instance.
* 
* <p>Revision history:
* <ul>
* 	<li>26/04/2011 v0.0.1. - first beta version, feel free to improve it with scope funcionalities.</li>
* </ul></p>
* 
* @see http://code.google.com/p/jshashtable/
*/
function storedjQuery(selector, jQueryInstance) {
	var jQueryObject = null,
		associativeArray = null;
		
	// TODO
	// only simple selectors are managed now
	if (typeof(selector) !== 'string') {
		return jQuery(selector);
	}
	
	associativeArray = global.jQueryObjects;
	
	if (!jQueryInstance) {
		jQueryObject = associativeArray[selector];
		if (!jQueryObject) {
			jQueryObject = associativeArray[selector] = jQuery(selector);
		}
	} else {		
		associativeArray.put(selector, jQueryInstance);
		jQueryObject = jQueryInstance;
	}	
	
	return jQueryObject;
}



/**
* Returns a translated string.
* 
* @param key, the string key in the <code>global.strings</code> object.
* @return the localized string.
*/
function getLocalized(key) {
	return global.strings[key];
}


/**
* Transforms the querystring into an object.
* 
* @param queryString, an ampersand separated variables.
* @return the objected querystring.
*/
function splitQueryString(queryString) {
	var e,
		a = null,
		r = null,
		d = null,
		q = null,
		queryStringObj = {};
	
	a = /\+/g;  // Regex for replacing addition symbol with a space
	r = /([^&=]+)=?([^&]*)/g;
	d = function (s) { return decodeURIComponent(s.replace(a, " ")); };
	
	if (!queryString) {
		queryString = window.location.search;
	} else if (queryString.indexOf('?') === -1) {
		queryString = '?' + queryString;
	}
	
	q = queryString.substring(1);

	while (e = r.exec(q)) {
		queryStringObj[d(e[1])] = d(e[2]);
	}
	
	return queryStringObj;
}



/**
* Checks if a point is in some boundaries.
* 
* @param x, the x coordinate.
* @param y, the y coordinate.
* @param bounds, an object with these properties <code>{top, left, height, width}</code>.
* @return if the point is in boundaries or not.
*/
function pointIsInBounds(x, y, bounds) {
	return y >= bounds.top && y <= bounds.top + bounds.height && x >= bounds.left && x <= bounds.left + bounds.width;
}

/**
* Checks if the mouse coordinates are in a jQuery object boundaries.
* 
* @param mouseX, the mouse-x coordinate.
* @param mouseY, the mouse-y coordinate.
* @param jQueryObject, the jQuery object.
* @return if the mouse coordinates are in the jQuery object boundaries.
*/
function mouseIsOn(mouseX, mouseY, jQueryObject) {
	var bounds = null;		
	
	bounds = jQuery.extend(jQueryObject.offset(), {width: jQueryObject.width(), height: jQueryObject.height()});
	return pointIsInBounds(mouseX, mouseY, bounds);
}



/**
* Changes the image src if this is different from the current one.
* 
* @param jQueryImg, the jQuery image instance.
* @param src, the image url.
* 
* <p>Revision history:
* <ul>
* 	<li>06/05/2011 v1.0.0. - first version.</li>
* </ul></p>
*/
function changeImgSrc(jQueryImg, src) {
	if (jQueryImg.attr('src') === src) {
		return;
	}
	jQueryImg.attr("src", src);
}



/**
* Change the image src because of a <code>mouseover</code> event.
* 
* @param jQueryImg, the jQuery image instance.
* @param src, the image url.
* @param lock, if future src changes need to be locked.
* 
* <p>Revision history:
* <ul>
* 	<li>06/06/2011 v1.0.0. - first version created for the Asnaghi project.</li>
* </ul></p>
*/
function imgRollOver(jQueryImg, src, lock) {
	var data = null,
		i = 0,
		n = 0,
		supportedFormats = {},
		overPattern = '',
		format = '';

	// retrieves the jQuery object data
	data = jQueryImg.data();
	
	// creates the data
	if (!data) {
		data = {};
	}

	// saves the first src (the default one that is in page)
	if (!data.pageSrc) {
		data.pageSrc = jQueryImg.attr('src');
	}
	
	// if the new src is not passed maybe you want the auto-swap system retrieve it for you
	if (!src) {
		src = getRolloverSwapSrc(jQueryImg, data.pageSrc);	
	}
	
	// the changes on this img could be locked
	if (lock || !data.preventSrcChanges) {		
		changeImgSrc(jQueryImg, src);
	}

	// locks future changes
	if (lock) {
		data.preventSrcChanges = true;
	}
}

/**
* Change the image src because of a <code>mouseout</code> event.
* 
* @param jQueryImg, the jQuery image instance.
* @param src, the image url.
* @param force, if you want to break src changes locks.
* 
* <p>Revision history:
* <ul>
* 	<li>06/06/2011 v1.0.0. - first version created for the Asnaghi project.</li>
* </ul></p>
*/
function imgRollOut(jQueryImg, src, force) {
	var data = null;
	
	// retrieves the jQuery object data
	data = jQueryImg.data();
	
	// creates the data
	if (!data) {
		data = {};
	}
   
   // the changes on this img can be forced
	if (force) {
		data.preventSrcChanges = false;
	}
   
   // the changes on this img could be locked
	if (!data.preventSrcChanges) {
		changeImgSrc(jQueryImg, src || data.pageSrc);
	}
}



/**
* Get the mouseover image version of the given one according to the auto swap system.
* 
* @param jQueryImg, the jQuery image instance.
* @param pageSrc, the orginal src attribute of the image.
* @return the mouseover url.
* 
* <p>Revision history:
* <ul>
* 	<li>06/05/2011 v0.0.1. - first version.</li>
* </ul></p>
* 
* @see #global.mouseoverSwap
*/
function getRolloverSwapSrc(jQueryImg, pageSrc) {
	var data = null,
		i = 0,
		n = 0,
		supportedFormats = {},
		overPattern = '',
		format = '',
		src = '';
	
	if (!pageSrc) {
		pageSrc = jQueryImg.attr('src');
	}

	data = jQueryImg.data();
	src = pageSrc;
	supportedFormats = global.mouseoverSwap.supportedFormats;
	
	n = supportedFormats.length;
	for (i = 0; i < n; i++) {
		format = supportedFormats[i];
		if (src.indexOf(format) !== -1 ) {
			overPattern = jQueryImg.data(global.mouseoverSwap.nameId + '-pattern');
			if (!overPattern) {
				overPattern = global.mouseoverSwap.defaultSwapPattern;
			}
			src = src.replace(format, overPattern + format);
			break;
		}
	}		
	
	return src;
}


function setDefaultRolloverSwap() {
	var i = 0,
		n = 0,
		jQueryObjects = null,
		jQueryObject = null,
		jQueryObjectData = {},
		queryStringObj = {},
		nameId = '',
		swapSelectors = [],
		jQueryObjectToSwap = [],
		j = 0,
		m = 0,
		swapSelector = '';
	
	if (!('mouseoverSwap' in global)) {
		$.extend(global, {mouseoverSwap: {
			// every tag with a querystringified-name attribute will be managed
			nameId: 'mouseover-swap',
			// by default it attaches this pattern to the image name to find the mouseover image
			defaultSwapPattern: '-sel',
			supportedFormats: ['.jpg', '.jpeg', '.png', '.gif']
		}});
	}
	
	nameId = global.mouseoverSwap.nameId;
	
	
	jQueryObjects = $('*[name*="' + nameId + '"]');	
	n = jQueryObjects.length;
	for (i = 0; i < n; i++) {
		jQueryObject = jQuery(jQueryObjects[i]);
		
		if (jQueryObject.data(nameId)) {
			continue;
		}
		
		queryStringObj = splitQueryString(jQueryObject.attr('name'));
		
		if (!(nameId in queryStringObj) || !queryStringObj[nameId]) {
			queryStringObj[nameId] = 'true';
		}
		
		jQuery.extend(jQueryObject.data(), queryStringObj);
		
		jQueryObjectData = jQueryObject.data();
		jQueryObjectData.jQueryObjectToSwap = [];
		swapSelectors = queryStringObj[nameId].split(',');
		m = swapSelectors.length;
		
		for (j = 0; j < m; j++) {
			swapSelector = swapSelectors[j];
			if (swapSelector === 'true') {
				jQueryObjectToSwap = jQuery(jQueryObject.find('img')[0]);
			} else {
				jQueryObjectToSwap = storedjQuery(swapSelector);
			}
			
			
			jQueryObjectData.jQueryObjectToSwap.push(jQueryObjectToSwap);				
			
			jQueryObject.bind('mouseover', function (e) {
				var i = 0,
					n = 0,
					jQueryObject = null,
					jQueryObjectData = null,
					jQueryObjectsToSwap = null,
					jQueryObjectToSwap = null,
					patternKey = '';
				
				jQueryObject = storedjQuery(this);
				jQueryObjectData = jQueryObject.data();
				patternKey = nameId + '-pattern';
				
				jQueryObjectsToSwap = jQueryObjectData['jQueryObjectToSwap'];
				n = jQueryObjectsToSwap.length;
				for (i = 0; i < n; i++) {
					jQueryObjectToSwap = jQueryObjectsToSwap[i];
					jQueryObjectToSwap.data(patternKey, jQueryObjectData[patternKey])
					imgRollOver(jQueryObjectToSwap);
				}
				
			});
			jQueryObject.bind('mouseout', function (e) {
				var i = 0,
					n = 0,
					jQueryObjectsToSwap = null,
					jQueryObjectToSwap = null;
				
				jQueryObjectsToSwap = storedjQuery(this).data('jQueryObjectToSwap');
				n = jQueryObjectsToSwap.length;
				for (i = 0; i < n; i++) {
					jQueryObjectToSwap = jQueryObjectsToSwap[i];
					imgRollOut(jQueryObjectToSwap);
				}
				
			});
		}
	}	
}


//TODO to comment
function alphaObjectsTo(objects, alpha, duration, callback) {
	var i = 0,
		n = 0,
		object = null;
	
	if (!Array.isArray(objects)) {
		objects = [objects];
	}
	
	n = objects.length;
	for (i = 0; i < n; i += 1) {
		object = objects[i];
		if(!object) {
			continue;
		}
		
		if (alpha > 0) {
			if (object.is(':hidden')) {
				object.css('opacity', 0);
			}
			
			object.show();
		}

		if (parseInt(object.css('opacity'), 10) === alpha) {
			if (callback) {
				callback.apply();
			}
			return;
		}

		object.stop();	
		object.fadeTo(duration * 1000, alpha, function () {
			if (alpha === 0) {
				object.hide();
			}

			if (callback) {
				callback.apply();
			}
		});
	}
}


/*********************************
// WINDOW HASH MANAGEMENT
*********************************/
/**
* Set the <code>location.hash</code> property.
* 
* @param hash, the new hash value.
* @param applicant, for tracking and debugging purposes.
*/
function setHash (hash, applicant) {
	if(getHash() !== hash) {
		location.hash = hash;
	}
}

/**
* Get a cleared-hash value.
*
* @param hash, the hash value to use. If it's empty the localtion.hash will be used.
* 
* @return the <code>hash</code> value cleared of the <code>#</code> character.
*/
function getHash (hash) {
	if(!hash) {
		hash = location.hash;
	}
	hash = hash.replace('#', '');	
	return hash;
}



/*
 * jQuery hashchange event - v1.3 - 7/21/2010
 * http://benalman.com/projects/jquery-hashchange-plugin/
 * 
 * Copyright (c) 2010 "Cowboy" Ben Alman
 * Dual licensed under the MIT and GPL licenses.
 * http://benalman.com/about/license/
 */
(function($,e,b){var c="hashchange",h=document,f,g=$.event.special,i=h.documentMode,d="on"+c in e&&(i===b||i>7);function a(j){j=j||location.href;return"#"+j.replace(/^[^#]*#?(.*)$/,"$1")}$.fn[c]=function(j){return j?this.bind(c,j):this.trigger(c)};$.fn[c].delay=50;g[c]=$.extend(g[c],{setup:function(){if(d){return false}$(f.start)},teardown:function(){if(d){return false}$(f.stop)}});f=(function(){var j={},p,m=a(),k=function(q){return q},l=k,o=k;j.start=function(){p||n()};j.stop=function(){p&&clearTimeout(p);p=b};function n(){var r=a(),q=o(m);if(r!==m){l(m=r,q);$(e).trigger(c)}else{if(q!==m){location.href=location.href.replace(/#.*/,"")+q}}p=setTimeout(n,$.fn[c].delay)}$.browser.msie&&!d&&(function(){var q,r;j.start=function(){if(!q){r=$.fn[c].src;r=r&&r+a();q=$('<iframe tabindex="-1" title="empty"/>').hide().one("load",function(){r||l(a());n()}).attr("src",r||"javascript:0").insertAfter("body")[0].contentWindow;h.onpropertychange=function(){try{if(event.propertyName==="title"){q.document.title=h.title}}catch(s){}}}};j.stop=k;o=function(){return a(q.location.href)};l=function(v,s){var u=q.document,t=$.fn[c].domain;if(v!==s){u.title=h.title;u.open();t&&u.write('<script>document.domain="'+t+'"<\/script>');u.close();q.location.hash=v}}})();return j})()})(jQuery,this);
/*********************************
// END WINDOW HASH MANAGEMENT
*********************************/





/**
 * Copyright 2010 Tim Down.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * @see http://code.google.com/p/jshashtable/
 */
var Hashtable=(function(){var p="function";var n=(typeof Array.prototype.splice==p)?function(s,r){s.splice(r,1)}:function(u,t){var s,v,r;if(t===u.length-1){u.length=t}else{s=u.slice(t+1);u.length=t;for(v=0,r=s.length;v<r;++v){u[t+v]=s[v]}}};function a(t){var r;if(typeof t=="string"){return t}else{if(typeof t.hashCode==p){r=t.hashCode();return(typeof r=="string")?r:a(r)}else{if(typeof t.toString==p){return t.toString()}else{try{return String(t)}catch(s){return Object.prototype.toString.call(t)}}}}}function g(r,s){return r.equals(s)}function e(r,s){return(typeof s.equals==p)?s.equals(r):(r===s)}function c(r){return function(s){if(s===null){throw new Error("null is not a valid "+r)}else{if(typeof s=="undefined"){throw new Error(r+" must not be undefined")}}}}var q=c("key"),l=c("value");function d(u,s,t,r){this[0]=u;this.entries=[];this.addEntry(s,t);if(r!==null){this.getEqualityFunction=function(){return r}}}var h=0,j=1,f=2;function o(r){return function(t){var s=this.entries.length,v,u=this.getEqualityFunction(t);while(s--){v=this.entries[s];if(u(t,v[0])){switch(r){case h:return true;case j:return v;case f:return[s,v[1]]}}}return false}}function k(r){return function(u){var v=u.length;for(var t=0,s=this.entries.length;t<s;++t){u[v+t]=this.entries[t][r]}}}d.prototype={getEqualityFunction:function(r){return(typeof r.equals==p)?g:e},getEntryForKey:o(j),getEntryAndIndexForKey:o(f),removeEntryForKey:function(s){var r=this.getEntryAndIndexForKey(s);if(r){n(this.entries,r[0]);return r[1]}return null},addEntry:function(r,s){this.entries[this.entries.length]=[r,s]},keys:k(0),values:k(1),getEntries:function(s){var u=s.length;for(var t=0,r=this.entries.length;t<r;++t){s[u+t]=this.entries[t].slice(0)}},containsKey:o(h),containsValue:function(s){var r=this.entries.length;while(r--){if(s===this.entries[r][1]){return true}}return false}};function m(s,t){var r=s.length,u;while(r--){u=s[r];if(t===u[0]){return r}}return null}function i(r,s){var t=r[s];return(t&&(t instanceof d))?t:null}function b(t,r){var w=this;var v=[];var u={};var x=(typeof t==p)?t:a;var s=(typeof r==p)?r:null;this.put=function(B,C){q(B);l(C);var D=x(B),E,A,z=null;E=i(u,D);if(E){A=E.getEntryForKey(B);if(A){z=A[1];A[1]=C}else{E.addEntry(B,C)}}else{E=new d(D,B,C,s);v[v.length]=E;u[D]=E}return z};this.get=function(A){q(A);var B=x(A);var C=i(u,B);if(C){var z=C.getEntryForKey(A);if(z){return z[1]}}return null};this.containsKey=function(A){q(A);var z=x(A);var B=i(u,z);return B?B.containsKey(A):false};this.containsValue=function(A){l(A);var z=v.length;while(z--){if(v[z].containsValue(A)){return true}}return false};this.clear=function(){v.length=0;u={}};this.isEmpty=function(){return !v.length};var y=function(z){return function(){var A=[],B=v.length;while(B--){v[B][z](A)}return A}};this.keys=y("keys");this.values=y("values");this.entries=y("getEntries");this.remove=function(B){q(B);var C=x(B),z,A=null;var D=i(u,C);if(D){A=D.removeEntryForKey(B);if(A!==null){if(!D.entries.length){z=m(v,C);n(v,z);delete u[C]}}}return A};this.size=function(){var A=0,z=v.length;while(z--){A+=v[z].entries.length}return A};this.each=function(C){var z=w.entries(),A=z.length,B;while(A--){B=z[A];C(B[0],B[1])}};this.putAll=function(H,C){var B=H.entries();var E,F,D,z,A=B.length;var G=(typeof C==p);while(A--){E=B[A];F=E[0];D=E[1];if(G&&(z=w.get(F))){D=C(F,z,D)}w.put(F,D)}};this.clone=function(){var z=new b(t,r);z.putAll(w);return z}}return b})();
