/*	Net-Createurs system library */


/* Détection du navigateur utilisé...
On utilise la chaine retournée par navigator.userAgent :
IE5.5 (IE tester)	: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.1; SV1; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; InfoPath.2; .NET CLR 1.1.4322)  
IE6 (vrai)			: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; InfoPath.2; .NET CLR 1.1.4322)
IE7 (IE tester)		: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; SV1; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; InfoPath.2; .NET CLR 1.1.4322)  
IE8					: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; slcc1; .net clr 2.0.50727; etc.)
FF 3.x				: Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3
Opera 10			: Opera/9.80 (Windows NT 5.1; U; fr) Presto/2.2.15 Version/10.10
Safari 3 PC			: Mozilla/5.0 (Windows; U; Windows NT 5.1; fr-FR) AppleWebKit/525.28 (KHTML, like Gecko) Version/3.2.2 Safari/525.28.1
Chrome				: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.38 Safari/532.0
iPhone				: mozilla/5.0 (iPhone; u; cpu iphone OS 3_1_2 like mac os x; fr-fr) applewebkit/528.18 (KHTML, like Gecko) version/4.0 mobile/7d11 safari/528.16
iPad (source Apple)	: Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B334b Safari/531.21.10
Positionne :	nc_nav -> array which='', IE, FF, OP, SA, CH
				majVersion = 0 si nav inconnu, ou nr de version majeur (1, 2, 3 etc.) du navigateur
				iPhone = true/false
*/

var	nc_nav={ which:'', majVersion:0, touchPad:'' },
	x=navigator.userAgent.toLowerCase(),
	isIE=x.indexOf('msie '),
	isFF=x.indexOf('firefox/'),
	isOP=x.substring(0,6)=='opera/',
	isCH=x.indexOf('chrome/'),
	isSA=isCH==-1 ? x.indexOf('safari/') : -1;		// Chrome signe Chrome ET Safari
if(x.indexOf('iphone;')!=-1) nc_nav['touchPad']='iPhone';
else if(x.indexOf('ipad;')!=-1) nc_nav['touchPad']='iPad';
if(isSA!=-1 || isOP) voffset=x.indexOf('version/');
if(isIE!=-1) { nc_nav['which']='IE'; nc_nav['majVersion']=parseInt(x.substring(isIE+5)) }
else if(isFF!=-1) { nc_nav['which']='FF'; nc_nav['majVersion']=parseInt(x.substring(isFF+8)) }
else if(isOP) { nc_nav['which']='OP'; nc_nav['majVersion']=parseInt(x.substring(voffset+8)) }
else if(isSA!=-1) { nc_nav['which']='SA'; nc_nav['majVersion']=parseInt(x.substring(voffset+8)) }
else if(isCH!=-1) { nc_nav['which']='CH'; nc_nav['majVersion']=parseInt(x.substring(isCH+7)) };

// L/R trim. Usage = xxxxx.trim()
String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g,"") };

if(typeof(NC)=='undefined') NC={};


// ******* NC main object
NC.tools= {

	// Library version
	version: function() { return { version:'1.2', revision:'110715', name:'Net-Createurs core library' }; },

	// =================================================
	// Extends 'source' object with object 'toadd'. If some toadd elements already exist in source, source elements are overwritten
	extend: function (source, toadd) { for(var i in toadd) source[i]=toadd[i]; },

	// =================================================
	// mini template translation
	getMiniTemplate:function(template, fields) {
		var i=0, j, k, ok=true, name;
		while(ok) {
			i=template.indexOf('-(',i);
			j= i==-1 ? -1 : template.indexOf(')-', i);
			k= j==-1 ? -1 : template.indexOf('-(', i+2);
			ok=i!=-1 && j!=-1 && (k==-1 || k>j);
			if(ok) {
				name=template.substring(i+2, j);
				template=
					  template.substring(0,i)
					+ ( typeof(fields[name])=='undefined' ? '' : fields[name] )
					+ template.substring(j+2);
				;
			}
		}
		return template;
	},

	// =================================================
	// Lecture dynamique des style CSS réels (calculés) d'un objet
	// obj = ID de l'objet, ou objet lui même
	getStyle:function(obj) {
		if(typeof(obj)=='string') obj=document.getElementById(obj);
		return typeof(obj.currentStyle)=='undefined' ?
	  	  document.defaultView.getComputedStyle(obj, null)		// tous les navigateurs sauf IE
	  	: obj.currentStyle;										// IE
	},

	/* =================================================
	 * "obj" centring on screen (container=false) or 'container' object.
	 * If obj is not attached to target container's root, it is moved in
	 * If the object is larger than the container, left and top values are forced to 0 and if CSS position is "fixed", it is temporarily turned to "absolute"
	 */
	center:function(obj, container) {
		if(typeof(obj)=='string') obj=document.getElementById(obj);
		if(obj) {
			if(typeof(container)=='undefined') var container=false;
			this.moveNode(container ? container : document.body, obj);	// moves object to target container root
			var style=this.getStyle(obj),
				pos=style.position,
				size=this.objSize(obj),
				wrapper=container ? this.objSize(container) : this.winInfo(),
				x= Math.round((wrapper.width-size.width)/2),		// Theorical x pos for centering the object
				y= Math.round((wrapper.height-size.height)/2);		// and y
			if(pos!='absolute' && pos!='fixed') pos='absolute';

			// 'fixed' to 'absolute' position management (and vice-versa)
			if(!obj.SFparams) obj.SFparams={};						// Sifacile temporary store area, in the object itself
			if(!obj.SFparams.position) obj.SFparams.position=pos;	// Keep trace of original CSS position
			pos= x<0 || y<0 ? 'absolute' : obj.SFparams.position;

			// object displacement
			obj.style.position=pos;
			obj.style.left= (Math.max(0,x) + (pos=='fixed' || container ? 0 : wrapper.left)) + 'px';
			obj.style.top= (Math.max(0,y) + (pos=='fixed' || container ? 0 : wrapper.top)) + 'px';
		}
		return false;
	},

	/* =================================================
	 * Retourne un tableau avec des infos sur la fenêtre : [width] / [height] = largeur / hauteur visible du document, [left) / [top] = offset left et top,
	 * [totalWidth] / [totalHeight] = largeur / hauteur totale du document
	 */
	winInfo:function() {
		var	n=navigator.userAgent.toLowerCase(),
			doc=document.compatMode && document.compatMode=="BackCompat" ? document.body : document.documentElement,
			doc1=n.indexOf("safari")>-1 ? document.body : doc;			// Forcage obligatoire avec Safari PC jusque version 4 incluse
		return { width:doc.clientWidth, height:doc.clientHeight, left:doc1.scrollLeft, top:doc1.scrollTop,
			totalHeight:Math.max(doc1.scrollHeight,doc.clientHeight), totalWidth:Math.max(doc1.scrollWidth,doc.clientWidth) }
	},

	// =================================================
	// Retourne la taille X / Y de l'objet obj, qui peut être l'ID d'un objet ou l'objet lui même
	objSize:function(obj) {
		if(typeof(obj)=='string') obj=document.getElementById(obj);
		if(obj) {
			this.makeReal(obj,1);
			ret= { width:obj.offsetWidth, height:obj.offsetHeight, totalWidth:obj.scrollWidth, totalHeight:obj.scrollHeight };
			this.makeReal(obj,0);
		}
		else ret= {width:0, height:0, totalWidth:0, totalHeight:0 };
		return ret;
	},

	// =================================================
	// Retourne la position X/Y absolue de l'objet X dans la fenêtre
	// Remonte l'arborescence des nodes container pour ajouter les offset des <div> quand c'est nécessaire
	// X peut être l'id de l'objet, ou l'objet lui même (détection auto)
	// Problèmes :
	//	-	Bug IE6/7 avec les <div> relative. S'ils sont dans un <td> centré verticalement, IE6/7 ajoute 2 fois la marge du haut dûe au centrage
	//		la variable last_h permet de corriger cette erreur. A voir selon les montages...
	//	-	Bug tous : avec les <div> static. Leur position X/Y ne doit pas être prise en compte s'ils sont suivis d'un div autre que absolu
	getXY:function(obj) {
		var	nav=navigator.userAgent.toLowerCase(),isIE=nav.indexOf('msie '),ielower=isIE!=-1 && parseInt(nav.substring(isIE+5),10)<8,
			x0=0, y0=0;
		if(typeof(obj)=='string') obj=document.getElementById(obj);
		if(obj) {
			var x0=obj.offsetLeft, y0=obj.offsetTop, x1, y1,prevpos, thispos='',last_h=0;
			while(obj.parentNode && obj.parentNode.tagName!='BODY') {
				obj=obj.parentNode;
				if(obj.tagName=='DIV') {
					x1=typeof(obj.offsetLeft)=='number' ? obj.offsetLeft : 0;
					y1=typeof(obj.offsetTop)=='number' ? obj.offsetTop : 0;
					prevpos=thispos;
					thispos=typeof(document.defaultView)=='undefined' ?		// IE 6/7. Ne pas tester sur currentStyle car Opera10 le reconnait
						obj.currentStyle.position : document.defaultView.getComputedStyle(obj, null).position;

					switch(thispos) {
					case 'static':
						if(prevpos && prevpos!='absolute') { x1=0; y1=0 }
						else if(!prevpos && !ielower)  { x1=0; y1=0 };		// Chablais, sauf le ielower (IE 6/7) -> pages modèle marchands PP
						break;
					case 'relative':
						if(ielower) {
							h=this.getStyle(obj)['height'];
							if(h!='auto') last_h=parseInt(h,10);
						}
						break;
					}
					x0+=x1; y0+=y1;
				}
				else if(obj.tagName=='TD') {
					if(ielower && last_h) {y0-=obj.offsetHeight-last_h; last_h=0 };		// bug IE6/IE7
				}
			}
		}
		return {x:x0, y:y0 };
	},

	// =================================================
	// Parse an object SRC
	parseSRC:function(obj) {
		if(typeof(obj)=='string') obj=document.getElementById(obj);
		return typeof(obj.src)=='undefined' ? '' : this.parseURL(obj.src);
	},

	parseURL:function(url) {
		var ret={ path:'', fullname:'', name:'', extension:'' },
			x=url.split('/');
		if(url) {
			ret.fullname=x.pop();
			ret.path=x.join('/') + (x.length ? '/' : '');
			x=ret.fullname.split('.');
			ret.extension=x.length ? x.pop() : '';
			ret.name=x.join('.');
		}
		return ret;
	},

	// =================================================
	// Preload an image
	preload:new Array(),
	preloadImg:function(imgURL) {
		var i=this.preload.length;
		this.preload[i]=new Image();
		this.preload[i].src=imgURL;
	},

	// =================================================
	// verify an email address, return true if address OK, or false otherwise
	emailCheck:function(email) {
		var email_addressRegxp = /^[!#-?A-~]+@([-_a-z0-9]{2,}\.)*[-_a-z0-9]{2,}(\.[a-z0-9]{2,4}){1,2}$/i;
		return email_addressRegxp.test(email);
	},

	// =================================================
	// verify a URL, return true if address OK, or false otherwise
	urlCheck:function(url) {
		var url_addressRegxp = /^(http(s)?:\/\/)?([-_a-z0-9]{1,63}\.){1,10}([a-z0-9]{2,4}$|([a-z0-9]{2,4})(\/[\x20-\x7E]+)*$)/i;
		return url_addressRegxp.test(url) && !(/"/.test(url));
	},



	// *************** EVENTS MANAGEMENT ************************ //

	/* Event manager for "obj" object. Usage : NC.tools.addEvent( object, event, fct, params, direct ) where event=onload, onresize, onclick, etc.
	 * ATTENTION : the function name must **NOT** be enclosed in quotes! use fct_name(), and not 'fct_name()
	 * params = parameters to store in the object, ie: { easing: 'easeOutBounce', duration:1000 }. They will be stored in obj.SFparams{}
	 * they will be accessible in the event handler as source.SFparams.easing, source.SFparams.duration
	 * With IE6/7 or 8+ in compatible mode, it can be tricky to get a reference to the object that trigered the event handler ( "this", in DOM2 )
	 * to solve this issue, use NC.tools.addEvent() tool and build your event-called functions like this :
	 *
	 * -- how to add the event handler on the page:
	 *	NC.tools.addEvent( obj, 'onclick', myFct, {easing:'easeDirect'} ) 	// 'obj' is the object to add the event handler to (pointer to the object, or string(id))
	 *
	 * -- the event handler itself:
	 *	function myFct(o) {
	 *		var source=NC.tools.eventSource(o,this)		// returned 'source' will be a pointer to the object that triggered the event, whatever the browser or version
	 *		var id=source.id, easing=source.SFparams.easing;
	 *		...
	 *	}
	 */
	addEvent: function(obj, what, func, params, direct) {
		if(typeof(direct)=='undefined') var direct=false;		// used to force IE style management
		if(typeof(obj)=='string') obj=document.getElementById(obj);
		// Parameters management
		if(typeof(params)=='undefined') var params={};
		if(params) {
			if(typeof(obj.SFparams=='undefined')) obj.SFparams={};
			for(var i in params) obj.SFparams[i]=params[i];
		}
		// FF, Safari, Opera, Chrome, IE8+ in their own mode: nothing to do, "this" will be directly available within 'func' as a reference to the source object of the event
		if(obj.addEventListener && !direct) obj.addEventListener(what.substr(2), func, false );

		// IE 6/7 or 8+ in compatible mode : must keep old event and propagate source object of event (='this') to each one
		else {
			var oldEvent=obj[what];
			obj[what]=function(source) { if(!source) var source=this; R=true; if(typeof(oldEvent)=='function') var R=oldEvent(source); if(R || typeof(R)=='undefined') return func(source) };
		}
	},

	// return a handler to the object which is the source of an event (equivalent to "this" in DOM2 events management)
	eventSource: function(x,y) {
		return window.addEventListener ? y : x;
	},


	// *************** COOKIE MANAGEMENT ************************ //
	
	// Return cookie "name" content, or an empty string if doesn't exist
	getCookie:function(name) {
		var i=document.cookie.indexOf(name + '='), content='';
		if (i>-1) {
			var	j=document.cookie.indexOf(';',i+name.length+1);
			if(j==-1) j=document.cookie.length;
			content=document.cookie.substring(i+name.length+1,j);
		}
		return decodeURIComponent(content);
	},

	/* Store/delete a cookie
	 * Params = {name,value,expires,path,domain,secure}
	 * path -> nothing (= '/') or '/xxxxx' = from where this cookie will be visible
	 * value = content of the cookie, unescaped, or nothing -> delete the cookie
	 * expires : nothing -> cookie is deleted when the browser is closed, or numeric value -> seconds before the cookie dies
	 * domain = nothing or '.mydomain.com' for subdomains (to verify)
	 * secure -> true -> the cookie can only be used from a SSL server (https://etc.)
	 * Only the name is required. If value='' -> delete the cookie 'name', if such
	 * MAX	- total cookies on a browser = 300
	 *		- total cookies from one server or domain = 20
	 * 		- cookie size = 4KB 
	 */
	storeCookie:function(params) {
		if(params.expires) {
			var expire = new Date();
			expire.setTime(expire.getTime() + (params.expires*1000));
			params.expires = expire.toGMTString();
		}
		document.cookie=
			  params.name + '='
			+ (params.value ? encodeURIComponent(params.value) : '')
			+ (params.expires ? '; expires=' + params.expires : '')		// if no expires -> cookie will be erased after all browser's windows are closed
			+ '; path=' + (params.path ? params.path : '/')				// no path = / -> cookie is visible from root and all subfolders
			+ (params.domain ? '; domain=' + params.domain : '')		// should work only with subdomains
			+ (params.secure ? '; secure' : '')							// send cookie only on https secured connexions
		;
	},


	// *************** NODES OPERATIONS ************************ //

	// =================================================
	// Appends a nodes structure to 'target' object or objectID
	// struct = { type: 'div', id: 'blabla', style: { ... }, childs: Array( { new defs } ) }
	// return the first created element
	appendStructure: function(target,struct) {
		var i, j, s, el, childs, ret=false; 
		target=this.checkTarget(target);
		if(target) {
			for(var i in struct) {
				el=document.createElement(struct[i].type);
				if(!ret) ret=el;
				childs=false;
				for(var j in struct[i]) {
					if(j=='style') { for(s in struct[i][j]) el[j][s]=struct[i][j][s]; }
					else if(j=='childs') childs=struct[i][j];
					else if(j!='type') el[j]=struct[i][j];
				}
				target.appendChild(el);
				if(childs) this.appendStructure(el,childs);
			}
		}
		return ret;
	},

	// Move a node from actual attachement to new target node
	moveNode: function(target, node, xid, id) {
		if(typeof(node)=='string') node=document.getElementById(node);
		if(node) {
			if(typeof(xid!='undefined')) this.changeNodeIDs(node, xid, id);
			target=this.checkTarget(target);
			if(target && node.parentNode!=target) target.appendChild(node);
		}
	},

	// IE9 natif mode patch : check & change "target" if it is document.body, as moving nodes here is not supported
	checkTarget: function(target) {
		if(typeof(target)=='string') target=document.getElementById(target);
		if(target==document.body && nc_nav.which=='IE' && nc_nav.majVersion==9) target=document.getElementById('pageroot');
		return(target);
	},

	// Recursively change IDs within node "source"
	changeNodeIDs:function(source, xid, id) {
		var i, child;
		if(source.nodeType!=3) {
			if(source.id) source.id=this.getNewID(source.id, xid, id);
			if(source.name) source.name=this.getNewID(source.name, xid, id);
			if(source.htmlFor) source.htmlFor=this.getNewID(source.htmlFor, xid, id);
		}
		child=source.childNodes;
		for(i=0; i<child.length; i++) this.changeNodeIDs(child[i], xid, id);
	},

	getNewID:function(old, xid, id) {
		var i=old.indexOf(xid);
		if(i!=-1) old=old.split(xid).join(id);
		return old;
	},

	// *************** CLASSES MANAGEMENT ************************ //

	// Retourne un pointeur vers la classe cssname si elle existe, ou null sinon
	// Permet de lire ou modifier une classe CSS globale document, par exemple : sfa_css['paddingLeft']='8px'
	getCSSpointer:function(cssname) {
		var i=0,CSStab=new Array(),sfa_css=null,css='';
		if(document.styleSheets) do {
			css=typeof(document.styleSheets[i]);
			if(css!='undefined' && css!='unknown') {
				css=document.styleSheets[i];
				if (css.rules) CSStab=document.styleSheets[i].rules;				// IE
				else if(css.cssRules) CSStab=document.styleSheets[i].cssRules;		// Others
				else CSStab=new Array();
				for(var j in CSStab) if(CSStab[j].selectorText=='.'+cssname) sfa_css=CSStab[j]['style']; //;for(var k in sfa_css) alert(k +' -> '+sfa_css[k]) }
				// cssText = chaine de texte du style (PADDING-RIGHT : 8px) ou paddingRight=8px
				i++;
			}
			else css='';
		} while(css && sfa_css==null);
		return sfa_css;
	},

	/* Change oldclass name with newclass name on object "object".
	 * If no newclass specified, remove the old one. If no oldclass specified, adds the new one at the end.
	 * if both new and old are the same, toggle the class on/off
	 * if force=true -> add the new class even if the old is not found
	 */
	changeClass:function(object, newclass, oldclass, force) {
		if(typeof(object)=='string') object=document.getElementById(object);
		if(object) {
			if(typeof('force')=='undefined') var force=false;
			var classes=object.className.split(' '),
				result='',
				found=false;
				if(typeof(oldclass)=='undefined') var oldclass='';
			for(var i in classes) {
				if(classes[i]) {
					if(classes[i]==oldclass) {
						found=true;
						if(oldclass==newclass) classes[i]='';
						else classes[i]=newclass;		// Replace the old one by the new one
					}
					else if(classes[i]==newclass) {
						found=true;
						if(oldclass==newclass) classes[i]='';
						else newclass='';				// If old class is found later on, do not add the new one as it's already there
					}
				}
			}
			if((newclass && !oldclass) || (!found && (newclass==oldclass || force))) classes[i+1]=newclass;
			for(var i in classes) { if(classes[i]) result+=' ' + classes[i]; }
			object.className=result.trim();
		}

	},

	// return an array of ressources towards each document's object using theclass
	searchClass:function(theclass) { return this.searchClassBrowse(theclass, new Array(), document.body); },

	searchClassBrowse:function(theclass,result,obj) {
		var i, thisclass;
		if(obj.nodeType!=3 && obj.className) {		// 3 = 'undefined' ( text content ? )
			thisclass=obj.className.split(' ');
			for(i in thisclass) if(thisclass[i]==theclass) result[result.length]=obj;
		}
		var child=obj.childNodes;
		for(i=0;i<child.length;i++) result=this.searchClassBrowse(theclass,result,child[i]);
		return result;
	},



	// ********** TAB BARS AND PANES UTILITIES *************
	// Open the pane 'id'. We check if any pane is already opened, and if yes (and not the same as the one to open) close it first, then open the new pane
	// panes ID must be xxxx-0, xxxx-1 etc.
	openPane: function(id) {
		var i=id.lastIndexOf('-'), j=0, rootname, obj, style, className, isactive;
		if(i!=-1) {
			rootname=id.substr(0,i); i=id.substr(i+1);
			while(obj=document.getElementById(rootname+'-'+j)) {
				if(j!=i) {
					className=obj.className;
					isactive=className.substring(className.length - (rootname + '-active').length) == rootname + '-active';
					// close old pane
					if(isactive) {
						this.changeClass(obj, rootname + '-inactive', rootname + '-active');
						obj=document.getElementById('tab-' + obj.id);			// tab container (text + background image)
						if(obj) {
							this.changeClass(obj, 'inactive', 'active');		// tab container will look inactive
							obj.style.zIndex=1;									// and is pushed down
						}
					}
				}
				j++;
			}
			obj=document.getElementById(id);
			// open new pane
			if(obj) {
				this.changeClass(obj, rootname + '-active', rootname + '-inactive');
				obj=document.getElementById('tab-' + id);
				if(obj) {
					this.changeClass(obj, 'active', 'hover');					// tab container will look active
					obj.style.zIndex=2;											// and is pulled up
				}
			}
		}
	},




	// ********** BASIC FORM UTILITIES *****************

	// =================================================

	focusedNode: false,		// reference to focused field node, or false. Used to prevent the "enter" key (13) interception within textarea fields

	// return 'form' elements values as an object { fieldname:'value', fieldname2:'value2', ... } where value=field value, or Array(Fieldvalue1, fieldvalue2, etc.) for checkboxes
	formGetContent: function(form) {
		var content={};
		if(form && typeof(form)=='string') form=document.getElementById(form);
		if(form) content=this.formParse(form, content, true);
		return(content);
	},

	/* Reverse function to formGetContent() : preset 'form' with 'content'
	 * If content['_name'] is not set, it is automatically set to form name
	 */
	formPutContent: function(form, content) {
		if(form && typeof(form)=='string') form=document.getElementById(form);
		if(form) {
			if(typeof(content['_name'])=='undefined') content['_name']=form.name;
			this.formParse(form, content, false);
		}
	},

	// Form parser, used to get (get=true) or preset (get=false) a form
	formParse: function(obj, content, get) {
		if(obj.nodeType!=3 && obj.name && (obj.tagName=='INPUT' || obj.tagName=='SELECT' || obj.tagName=='TEXTAREA')) {

			var name=obj.name, i, value;
			if(name.substr(name.length-2)=='[]') name=name.substr(0, name.length-2);
			if(!get) value=typeof(content[name])=='undefined' ? '' : content[name];
			if(obj.type=='checkbox') {
				if(get) {
					if(!content[name]) content[name]=new Array();
					if(obj.checked) content[name][content[name].length]=obj.value;
				}
				else if(value) {
					for(i in content[name]) { if(content[name][i]==obj.value) obj.checked=true; }
				}
			}
			else {
				if(get && !content[name]) content[name]= '';
				if(obj.type=='radio') {
					if(get && obj.checked) content[name]=obj.value;
					else if(!get && value==obj.value) obj.checked=true;
				}
				else if(obj.tagName=="TEXTAREA" || obj.type=='text' || obj.type=='hidden') {
					if(get) content[name]=obj.value;
					else obj.value=value;
				}
			}
		}
		var child=obj.childNodes;
		for(var i=0;i<child.length;i++) {
			if(get) content=this.formParse(child[i], content, get);
			else this.formParse(child[i], content, get);
		}
		if(get) return(content);

	},


	// ********** BASIC VISUAL EFFECTS ( alpha fade, color morph, easing, ...) *************** //


	/* easing X (and/or) movement using NC standard easings (JQuery compatible)
	 * keep the handle to setTimeOut into NC.tools.easeTimer[objectID], so that it can be canceled if required
	 * to be as fluent as possible, no check is done on parameters -> caller must check them by itself...
	 * callback is a callback optionnal function passed as a string. CAUTION : must not contain any "
	 */
	easeTimer: {},
	ease: function(objectID, timerName, ease, time, xstart, xamplitude, ystart, yamplitude, duration, callback)  {
		var obj=document.getElementById(objectID);
		time+=50; if(time>duration) time=duration;
		if(xamplitude) {
			offset=parseFloat(eval('NC.easing.' + ease + "('" + objectID + "'," + time + ',' + xstart + ',' + xamplitude + ',' + duration + ')'),10);
			obj.style.left= offset+'px';
		}
		if(yamplitude) {
			offset=parseFloat(eval('NC.easing.' + ease + "('" + objectID + "'," + time + ',' + ystart + ',' + yamplitude + ',' + duration + ')'),10);
			obj.style.top= offset+'px';
		}
		if(time<duration && ease!='easeDirect') this.easeTimer[timerName]=setTimeout("NC.tools.ease('" + objectID + "','" + timerName + "','" +  ease + "',"
			+ time + ',' + xstart + ',' + xamplitude + ',' + ystart + ',' + yamplitude + ',' + duration + ',"' + callback + '")', 50);
		else {
			this.easeTimer[timerName]=false;
			if(callback) eval(callback);
		}
	},

	/* =================================================
	/* alpha fade in /out. Params : id, [from], to, [step], [display block (default) or inline)], [delay], [callback]=callback function
	/* fullHide : true (default) -> set "display:none" when alpha reach 0, false -> do not hide the object
	 */
	fademem: {},
	fade:function(params) {
		if(typeof(params.id)=='string') params.id=document.getElementById(params.id);
		if(params.id) {
	
			var id=params.id.id;

			// clear any previous fadein/out on this object
			if(this.fademem[id]) clearTimeout(this.fademem[id]);

			// calculate object start opacity
			var style=this.getStyle(params.id),
				display=style.display
			;
			if(params.from || params.from=='0') alpha=params.from;
			else {
				if(display=='none') alpha=0;
				else {
					if(style.opacity!='' && typeof(style.opacity)!='undefined') var alpha=style.opacity*100;
					else if(style.filter && style.filter!='undefined') {
						var i=style.filter.indexOf('opacity=');
						if(i>-1) alpha=parseInt(style.filter.substring(i+8),10);
					}
					else alpha=100;
				}
			}

			// calculate object end opacity
			if(params.to<0) params.to=0; else if(params.to>100) params.to=100;

			// calculate actual step
			if(!params.step) params.step=34;		// fast animation
			params.step=Math.abs(params.step);
			if(params.to<alpha) params.step=-params.step;

			// launch fadein/out
			if(typeof(params.callBack)=='undefined') params.callBack='';
			if(typeof(params.callBackDelay)=='undefined') params.callBackDelay=0;
			else params.callBack=params.callBack.replace(/'/g,'"');
			if(typeof(params.fullHide)=='undefined') params.fullHide=true;
			this.fademem[id]=setTimeout(
				(display=='none' ?
					  "document.getElementById('"+id+"').style.display='"
					+ (params['display'] ? params['display'] : 'inline')
					+ "';" : '')
				+ "NC.tools.alphaStep('" + id + "'," + alpha + "," + params.to + "," + params.step + ",'" + params.callBack + "',"
					+ params.callBackDelay + "," + params.fullHide + ")"
				,params['delay'] ? params['delay'] : 1);
		}
	},

	// For speed reasons, this function is the only one which does not use NC.tools.setAlpha()
	alphaStep:function(id,alpha,target,step,callback,callbackDelay, fullHide) {
		var object=document.getElementById(id);
		if(alpha<target && step<0) alpha0=target; else if(alpha>target && step>0) alpha0=target; else alpha0=alpha;
		if(nc_nav.which=='IE' && nc_nav.majVersion<9) object.style.filter='alpha(opacity=' + alpha0 + ')';
		else object.style.opacity=alpha0/100;
		if(alpha==alpha0 && alpha!=target) this.fademem[id]=setTimeout("NC.tools.alphaStep('" + id + "',"
			+ (alpha+step) + "," + target + "," + step + ",'" + callback +"'," + callbackDelay + "," + fullHide + ")", 50);
		else {
			if(alpha0==0 && fullHide) object.style.display='none';
			if(callback) this.fademem[id]=setTimeout(callback, callbackDelay);
			else this.fademem[id]=false;
		} 
	},

	// alpha set, IE6+ compatible. Alpha=0..1
	setAlpha:function(obj,alpha) {
		if(typeof(obj)=='string') obj=document.getElementById(obj);
		if(obj) {
			if(nc_nav.which=='IE' && nc_nav.majVersion<9) obj.style.filter='alpha(opacity=' + (alpha*100) + ')';
			else obj.style.opacity=alpha;
		}
	},

	/* morph a caracteristic from current CSS style to params
	 * Params:	className = target class name
	 *			what = what to change (color, background (=color + image), backgroundColor, backgroundImage, and more to come)
 	 *			duration = morphing duration in msec (default = 1000)
 	 */
	morph:function(obj, params) {
		if(typeof(obj)=='string') obj=document.getElementById(obj);
		if(obj) {
			if(!params.duration) params.duration=1000;
			var	what=params.what,
				target=this.getCSSpointer(params.className);
			if(target) {
				if(what=='color' || what=='backgroundColor' || what=='background') this.morphRGB(obj, target, params);
				if(what=='background' || what=='backgroundImage') this.morphBGIMG(obj, target);
				else if(what=='textDecoration' || what=='fontStyle') setTimeout("NC.tools.morphSet('" + obj.id + "', '" + params.what + "','" + target[params.what] + "')", params.duration);
			}
		}
	},


	// Direct change of a CSS characteristic that cannot be faded (textDecoration, fontStyle etc.)
	morphSet:function(objID, what, value) { 
		var obj=document.getElementById(objID);
		if(obj) obj.style[what]=value;
	},

	// Morph 'what' RGB color from actual CSS style to a new CSS className
	morphRGB:function(obj, target, params) {
		if(params.what=='background') params.what='backgroundColor';
		if(target[params.what]) this.changeRGB(obj, {color: target[params.what], what: params.what, duration:params.duration} );
	},

	/* Morph background Image. No real morph, but an image change at beginning of morph sequence
	 * to address URLs issues, background morph works only if there is already an image in the same folder
	 */
	morphBGIMG:function(obj, target) {
		var style=this.getStyle(obj), imgInfo, targetInfo;
		if(style.backgroundImage) {
			imgInfo=this.parseURL(this.getCSSURL(style.backgroundImage));
			if(target.backgroundImage) {
				targetInfo=this.parseURL(this.getCSSURL(target.backgroundImage));
				obj.style.backgroundImage='url(' + imgInfo.path + targetInfo.fullname + ')';
			}
			style=this.getStyle(obj);
			if(style.backgroundImage) {
				if(target['backgroundPosition']) obj.style.backgroundPosition=target['backgroundPosition'];
				if(target['backgroundRepeat']) obj.style.backgroundRepeat=target['backgroundRepeat'];
			}
		}
	},

	// Return the URL of a CSS background image def
	getCSSURL:function(url) {
		if(url.substr(0,4).toLowerCase()=='url(') url=url.substring(4, url.length-1);
		return url;
	},

	/* Color morphing effect, from actual color to target color.
	 * Params	color = target color to go to, can be #RRGGBB, #RGB, rgb(r,g,b) or rgba(r, g, b, a)
	 *			what = what to change : 'color' (default if not specified), or 'backgroundColor'
 	 *			duration = morphing duration in msec (default = 1000)
	 */
	changeRGB:function(obj, params, isnext) {
		if(typeof(obj)=='string') obj=document.getElementById(obj);
		if(obj) {
			if(!obj.id) obj.id='fx-changeRGB-'+this.changeRGB_nb++;
			var objID=obj.id, what=params.what;
			if(typeof(isnext)=='undefined' || !isnext) {
				// init of a new cycle
				if(!params.duration) params.duration=1000;
				if(!what) what='color';
				var	style=this.getStyle(obj);
				if(!this.changeRGB_memo[objID]) this.changeRGB_memo[objID]={};
				this.changeRGB_memo[objID][what]={
					start:this.readColor(style[what]),
					end:this.readColor(params.color),
					step:1,
					stepNb:Math.round(params.duration/50),
					timeout:false
				};
			}
			if(this.changeRGB_memo[objID][what].timeout) clearTimeout(this.changeRGB_memo[objID][what].timeout);
			var percent=this.changeRGB_memo[objID][what].step++/this.changeRGB_memo[objID][what].stepNb;
			r='#';
			for(var i=0;i<5;i+=2) r+=this.toHex(Math.floor(
				parseInt(this.changeRGB_memo[objID][what].start.substr(i,2),16)*(1-percent)
			  + parseInt(this.changeRGB_memo[objID][what].end.substr(i,2),16)*percent
			));
			obj.style[what]=r;
			if(percent<1) this.changeRGB_memo[objID][what].timeout=setTimeout("NC.tools.changeRGB('" + objID + "',{what:'" + what + "'},1)",50);
			else this.changeRGB_memo[objID][what].timeout=false;
		}
	},
	changeRGB_memo: {},
	changeRGB_nb: 0,


	/* **************** internal only entries, do not use externaly ******************** */
	hexa:new Array(0,1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F'),
	toHex:function(dec) {	// Decimal to hexa conversion
		if (dec < 0) return('00');
		else if (dec > 255) return('FF');
		else return new String(this.hexa[Math.floor(dec/16)]) + new String(this.hexa[Math.floor(dec%16)]);
	},
	readColor:function(color) {	// Returns a color (#rgb or #rrggbb or color(r,g,b) as rrggbb
		if(color=='transparent') color='#fff';
		if(color.substring(0, 4)=="rgba") {		// Safari 5 : RGB + alpha
			eval('var rgb=new Array' + color.substring(4));
			if(!rgb[3]) color="#ffffff";
			else color="#" + this.toHex(rgb[0]) + this.toHex(rgb[1]) + this.toHex(rgb[2]);
		}
		else if(color.substring(0, 3)=="rgb") {		// Safari <5
			eval('var rgb=new Array' + color.substring(3));
			color="#" + this.toHex(rgb[0]) + this.toHex(rgb[1]) + this.toHex(rgb[2]);
		}
		else if(color.length == 4) color= 
			  "#" + color.substring(1, 2) + color.substring(1, 2)
			+ color.substring(2, 3) + color.substring(2, 3)
			+ color.substring(3, 4) + color.substring(3, 4);
		return color.substring(1);
	},



	// *************** PRIVATE FUNCTIONS ************************ //

	// Rend l'objet obj réel s'il est display:none ou width/height=0, afin de pouvoir lire certaines de ses propiétés comme sa taille, ou de modifier les objets qu'il contient (IE6/7/8/9)
	// Appeler une fois avec cde=1 -> rend l'objet réel, puis avec cde=0 -> le replace dans son état d'origine
	// Attention avec IE6/7, si on rend un conteneur réel pour modifier un objet dedans il faut attendre ~300 msec avant de restituer l'état du conteneur 
	makeReal:function(obj,cde) {
		if(typeof(obj)=='string') obj=document.getElementById(obj);
		if(obj) {
			if(!obj.SFparams) obj.SFparams={};					// Sifacile temporary store area
			if(cde) {
				var style=this.getStyle(obj);
				if(style.display=='none' || style.width=='0px' || style.width=='0%' || style.height=='0px' || style.height=='0%') {
					obj.SFparams.changed=true;
					obj.SFparams.opacity=typeof(style.opacity)=='undefined' ? 0 : style.opacity;
					obj.SFparams.filter=typeof(style.filter)=='undefined'? '' : style.filter;
					obj.SFparams.width=style.width;
					obj.SFparams.height=style.height;
					this.setAlpha(obj,0);
					obj.style.display='block';
					if(style.height=='0px' || style.height=='0%') obj.style.height='1px';		// Required by IE6..9
					if(style.width=='0px' || style.width=='0%') obj.style.width='1px';			// same
				}
				else obj.SFparams.changed=false;
			}
			else {
				var changed=obj.SFparams.changed;
				if(typeof(changed)!='undefined' && changed) {
					obj.style.display='none';
					obj.style.opacity=obj.SFparams.opacity;
					obj.style.filter=obj.SFparams.filter;
					obj.style.width=obj.SFparams.width;
					obj.style.height=obj.SFparams.height;
					obj.SFparams.changed=false;
				}
			}
		}
	}

}


// ******* JQuery compatible easing methods
// Other easings are located in the file fx/FX_easings.js ---------------
if(typeof(NC.easing)=='undefined') NC.easing={};
NC.tools.extend(NC.easing, {

	easeDirect: function(x, t, b, c, d) {
		return b+c;		// NO easing
	},

	easeLinear: function(x, t, b, c, d) {
		return c*t/d + b;
	}
});


