/**
 * Iddrinet 2009
 * 
 * archibald@picq.fr
 * 
 * http://www.picq.fr/
 */
(function() {
if (window.launchApp)
	return ;

/**
 * Some extension to jQuery
 */
jQuery.extend({
	unparam: function(str) {
		str = str.split('&');
		var opts = {};
		for (var i = 0; i < str.length; i++) {
			var spt = str[i].indexOf('=');
			var name = spt >= 0? str[i].substr(0, spt): str[i];
			if (!name)
				continue ;
			var value = spt >= 0? str[i].substr(spt+1): undefined;
			
			value = decodeURIComponent(value? value.replace(/\+/g, ' '): '');
			if (name.substr(name.length-2) == '[]') {
				name = name.substr(0, name.length-2);
				if (!opts[name] || !jQuery.isArray(opts[name]))
					opts[name] = [];
				opts[name].push(value);
			}
			else
				opts[name] = value;
		}
		return opts;
	},
	params: function(obj) {
		var t = '';
		for (var i in obj)
			if (obj[i])
				t += (t? '&': '')+i+'='+encodeURIComponent(obj[i]);
		return t.replace(/\'/g, '%27').replace(/%40/g, '@');
	},
	/** jQuery COOKIE extension
	 */
	defaultCookiePath: '/',
	deleteCookie: function(name) {
		document.cookie = name + "=;path=/;expires=Thu, 01-Jan-1970 00:00:01 GMT";
	},
	cookie: function(name, value, options) {
		if (typeof value != 'undefined') { // name and value given, set cookie
			options = options || {};
			var expires = '';
			if (options.expires && (typeof options.expires == 'number' || options.expires.toGMTString)) {
				var date;
				if (typeof options.expires == 'number') {
					date = new Date();
					date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
				} else
					date = options.expires;
				expires = '; expires=' + date.toGMTString(); // use expires attribute, max-age is not supported by IE
			}
			var path = '; path='+(options.path || jQuery.defaultCookiePath);
			var domain = options.domain ? '; domain=' + options.domain : '';
			var secure = options.secure ? '; secure' : '';
//			console.warn("Set-Cookie: "+[name, '=', encodeURIComponent(value), expires, path, domain, secure].join(''));
			document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
		} else { // only name given, get cookie
			var cookieValue = null;
			if (document.cookie && document.cookie != '') {
				var cookies = document.cookie.split(';');
				for (var i = 0; i < cookies.length; i++) {
					var cookie = jQuery.trim(cookies[i]);
					// Does this cookie string begin with the name we want?
					if (cookie.substring(0, name.length + 1) == (name + '=')) {
						cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
						break;
					}
				}
			}
			return cookieValue;
		}
	}
});

if (!jQuery.handleError)
	jQuery.handleError = function	handleError( s, xhr, status, e ) {
		// If a local callback was specified, fire it
		if ( s.error ) s.error( xhr, status, e );
	
		// Fire the global callback
		if ( s.global )
			jQuery.event.trigger( "ajaxError", [xhr, s, e] );
	};

/**
 * The replacement function that load scripts file using a <script> tag
 *  - script file are no longer loader using the _=875674548 param (force uncaching)
 *  - script file know their own source location (<script src="..." />)
 *  - script file are no longer shown in XHR tab in firebug
 *  - best thing off all of that: debugguers understand the script file as a script file
 *                                and are able to designate exception's lines
 */
var nativeAjax = jQuery.ajax;
var jXhr = jQuery.ajaxSettings.xhr;
jQuery.ajaxSettings.xhr = function() {
	try {var s = jXhr();try {s.withCredentials = true;}catch(e){}; return s;}catch(e){}
	try {return new XMLHttpRequest();}catch(e){}
	alert("Impossible de communiquer avec le serveur.\n\n" +
			"Veuillez activer l'ActiveX permettant les requêtes XML ou\n" +
			"approuver le domaine « "+document.domain+" » dans votre zone de confiance");
	return null;
};

jQuery.ajaxSetup({
	error: function(info, status) {
		if (status == 'notmodified' || (this.dataType !== 'json' && this.dataType !== 'script'))
			return ;
		if (typeof info.skip != 'undefined')
			return warningAjax(this, info, this.retry || nativeAjax);
		var self = this;
		if (!this.oldError || this.oldError(info) !== false) {
			jQuery.errorAlert({
				className: 'error '+this.errorClassName,
				title: this.errorTitle || 'Erreur de communication',
				text: info,
				ok: function() {
					if (self.errorOk)
						self.errorOk();
					else if (!self.oldError && self.error && self.error != jQuery.ajaxSettings.error)
						self.error(info, status);
				}
			});
		}
	}
});

function	dispatchException(e, ajax, ondone) {
	if (e == 'NotLoggued') {
		Application.cancelauth();
		Application.authenticated(function() {
			delete jQuery.lastModified[ajax.url];
			jQuery.ajax(ajax);
		});
		return true;
	}
	return false;
};

var iframe = {};
function	loadIframeProxy(host, loaded) {
	var dom = get_common_domain(host, ''+document.domain);
	try {window.document.domain = dom;}
	catch(e) {console.warn('failed to set the domain');return;}
	if (iframe[host]) {
		var xhr = null;
		try { xhr = iframe[host][0].contentWindow.xhr; }
		catch(e) {
			jQuery.errorAlert({
				title: 'Erreur de communication',
				text: "Impossible de contacter le serveur"
			});
			return ;
		}
		return loaded(xhr);
	}
//	console.info('load iframe at '+host+' for dom '+dom);
	iframe[host] = jQuery('<iframe style="display:none"></iframe>');
	iframe[host].load(function() {
		iframe[host][0].contentWindow.eval((jQuery.cookie('auth')? 'window.document.cookie="auth='+jQuery.cookie('auth')+'; expires=; path=/";': '')+'window.xhr='+jXhr);
		loaded(iframe[host][0].contentWindow.xhr);
	}).error(function(){
		console.info('error while loading iframe proxy');
	}).appendTo(document.body).attr({src:'http://'+host+window.iframeProxy});
};
var selfdomain = window.document.domain;
function	diffDomain(url) {
	var host = gethost(url);
	return host != selfdomain && getdomain(host) == getdomain(selfdomain)? host: false;
};
function	get_common_domain(u1, u2) {
	var comm = [];
	for (u1 = u1.split('.'), u2 = u2.split('.'), i1=u1.length-1, i2=u2.length-1; i1 >= 0 && i2 >= 0; i1--, i2--)
		if (u1[i1] != u2[i2])
			break ;
		else
			comm.push(u1[i1]);
	return comm.reverse().join('.');
};
window.gethost = function	gethost(url) {
	var m = (url || location.href).match(/^https?\:\/\/([^\/]*)\/.*$/);
	return (m? m[1]: m) || gethost(location.href);
};
window.getdomain = function	getdomain(host) {
	var t = host.split('.');
	return t.slice(t.length-2).join('.');
};
jQuery.ajax = function(s) {
	var host;
	return !window.iframeProxy	// proxy not specified
			|| s.noproxy		// on-demand skip proxy
			|| jQuery.inArray(s.dataType, ['json', 'html', 'xml', 'text']) == -1	// proxy only support raw data
			|| !(host = diffDomain(s.url))	// if a common host with a proxy (not checked) can be found
			? trackedAjax(s): loadIframeProxy(host, function(func) {
		s.xhr = func;
		var success = s.success;
		s.success = function() {
			var cookie = iframe[host][0].contentWindow.eval('window.document.cookie');
			var cookies = cookie.split('; ');
			for (var i = 0; i<cookies.length; i++) {
				var tab = cookies[i].split('=');
				jQuery.cookie(tab[0], tab[1]);
			}
			success.apply(s, arguments);
		};
		trackedAjax(s);
	});
};
function	warningAjax(s, json, recall) {
//	console.warn('warn');
//	console.info(s);
//	console.info(json);
//	console.info(recall);
	if (!s.warnings)
		s.warnings = [];
	if (s.options)
		s.options.warnings = s.warnings;
	if (jQuery.inArray(json.skip, s.warnings) == -1)
		s.warnings.push(json.skip);
	var warns = [];
	for (var i=0; i<s.warnings.length; i++)
		warns.push('warnings[]='+s.warnings[i]);
	if (s.type.toUpperCase() == 'GET') {
		for (var i=0; i<s.warnings.length; i++)
			if (s.url.indexOf(s.warnings[i]) == -1)
				s.url += (s.url.indexOf('&') != -1? '&': '?')+'warnings[]='+s.warnings[i];
	}
	else if (s.type.toUpperCase() == 'POST') {
		for (var i=0; i<s.warnings.length; i++) {
			if (typeof s.data == 'undefined')
				s.data = '';
			if (s.data.indexOf(s.warnings[i]) == -1)
				s.data += '&warnings[]='+s.warnings[i];
		}
	}
	jQuery.errorAlert({
		className: 'error '+s.errorClassName,
		title: s.errorTitle || 'Erreur de communication',
		text: json.error,
		details: json.details,
		buttons: json.buttons && json.buttons == 'YESNO'?
		{
			'Oui': function() {
				recall(s);
			},
			'Non': function() {
				
			}
		}: {
			'Forcer': function() {
				recall(s);
			},
			'Annuler': function() {
				
			}
		}
	});
};
jQuery.ajaxSettings.dataFilter = function(str, type) {
	if (type == 'json' && typeof str == 'string')
		str = str.replace('for(;;){}', '').replace(/(\/\*([^*]|[\r\n]|(\*+([^*\/]|[\r\n])))*\*+\/)|(\/\/.*)/g, '');
	return str;
};
function	trackedAjax(s) {
	if (s.oldError)
		return nativeAjax(s);
	s.oldError = s.error;
	
	if (s.url && s.url.charAt(0) == '?' && (''+window.location.href).indexOf('#') !== -1) {
		var prefix = (''+window.location.href).match(/http[s]?\:\/\/[\-.\w]+([^#]*)/);
		if (prefix)
			s.url = prefix[1]+s.url;
	}
	s = jQuery.extend({}, jQuery.ajaxSettings, s);

	var oldSuccess = s.success;
	s.error = jQuery.ajaxSettings.error;
	s.success = function(json) {
//		console.info(json);
		if (json && typeof json['exception'] == 'string' && dispatchException(json['exception'], s))
			return ;
		
		/** display error */
		if (json && typeof json.error != 'undefined')
			return this.error(json);

		/** protect the execution of the callable success */
		if (jQuery.cookie('dexc')=='1')
			oldSuccess && oldSuccess.apply(this, [json]);
		else {
			try {
				oldSuccess && oldSuccess.apply(this, [json]);
			}catch(e){
				warnException(e, function() {});
				throw e;
			}
		}
	};
	
	if (s.dataType != 'script' || !s.async)
		return nativeAjax(s);
	s.global && !jQuery.active++ && jQuery.event.trigger('ajaxStart');
	var xml = jQuery.extend(document.createElement('script'), {
		type: 'text/javascript',
		src: s.url,
		onload: function(e) {
			onreadystatechange('success');
		},
		onreadystatechange: function(e) {
			if (typeof(this.readyState)=='string' && this.readyState!='loaded' && this.readyState!='complete')return ;
			onreadystatechange('success');
		},
		onerror: function(e) {
			onreadystatechange('failed');
		}
	});
	s.beforeSend && s.beforeSend(xml);
	s.global && jQuery.event.trigger('ajaxSend', [xml, s]);
	var ival = null, requestDone = false, onreadystatechange = function(status) {
		if (requestDone)
			return ;
		requestDone = true;
		ival && clearInterval(ival) && (ival = null);
		if (status == 'success') {
			s.ifModified && modRes && (jQuery.lastModified[s.url] = modRes);
			s.success && s.success('', status);
			s.global && jQuery.event.trigger('ajaxSuccess', [xml, s] );
		} else
			jQuery.handleError(s, xml, status);
		s.global && jQuery.event.trigger('ajaxComplete', [xml, s]);
		s.global && ! --jQuery.active && jQuery.event.trigger('ajaxStop');
		s.complete && s.complete(xml, status);
		xml.parentNode.removeChild(xml);
		xml = null;
	};
	s.timeout > 0 && setTimeout(onreadystatechange, s.timeout);
	return document.getElementsByTagName('head')[0].appendChild(xml);
};


function	get_stack() {
	var stack = [];
	for (var cur=arguments.callee.caller; cur; cur = cur.caller) {
		var fn = cur.toString();
		var name = /function[\s]+([a-zA-Z0-9]+)\(/.exec(fn);
		stack.push(name? name[1]: 'anonymous');
	}
	return stack;
};
function	get_opera_stack(e) {
	var stack = [];
	var lines = e.message.split("\n");
//	alert(lines);
//	for (var cur=arguments.callee.caller; cur; cur = cur.caller) {
//		var fn = cur.toString();
//		alert(fn);
//		var name = /function[\s]+([a-zA-Z0-9]+)\(/.exec(fn);
//		stack.push(name? name[1]: 'anonymous');
//	}
//	for (var i=0, len=lines.length; i<len; i++) {
//		if (!lines[i].match(/^\s*[A-Za-z0-9\-_\$]+\(/))
//			continue ;
//		var entry = lines[i];
//		//Append next line also since it has the file info
//		if (lines[i+1]) {
//			entry += ' at ' + lines[i+1];
//			i++;
//		}
//		stack.push(entry);
//	}
	stack.shift();
	return stack;
};

function	make_user_string(config) {
	if (!config)
		return null;
	return concat_ws(' ', config.prenom, config.nom, concat('<', config.email, '>'));
};

function	warnException(e, disable) {
	if (!e.stack) {
		try {
			e.stack = (window.opera && e.message? get_opera_stack(e): get_stack()).join("\n");
		}catch(f){}
	}
//	var str = '<br />';
//	for (var i in e) {
//		str += e[i]+"\n";
//	}
//	alert(str);
	if (!e.fileName && !e.lineNumber && e.stack) {
		var matches = /\((.*):([0-9]+):[0-9]+\)/.exec(e.stack);
		if (matches) {
			e.fileName = matches[1];
			e.lineNumber = matches[2];
		}
	}
	var details = e.message;
	if (e.fileName)
		details += ' at '+e.fileName+(e.lineNumber? ':'+e.lineNumber: '');
	
	jQuery.errorAlert({
		title: "Erreur d'exécution JavaScript",
		text: "<p>" +
				"Une erreur d'éxécution s'est produite et les informations affichées sur la " +
				"page ne sont peut-être plus correctes. Un message a été envoyé contenant " +
				"uniquement les informations nécessaires à la résolution de ce problème." +
			"</p>" +
			"<p>" +
				"Vous devriez actualiser la page en appuyant sur F5 afin de ré-initialiser " +
				"l'environement avec les données de base." +
			"</p>",
		details: details,
		className: 'exception',
		buttons: {
			'Désactiver les messages d\'erreur': function() {
				disable && disable();
				jQuery.cookie('dexc', 1);
			},
/*			'Envoyer un rapport': function() {
				jQuery.ajax({
					url: window.server+'?ctrl=dev&action=exception',
					dataType: 'json',
					type: 'post',
					data: e,
					success: function(json) {
						jQuery.infoAlert({
							title: "Erreur d'éxécution JavaScript",
							text: "Un rapport d'erreur a été envoyé.<br />Nous vous remercions pour votre soutient. Ce rapport sera analysé " +
									"et les corrections nécessaires seront apportées au plus vite."
						});
					},
					error: function() {
						return false;
					}
				});
			},*/
			'OK': function() {}
		}
	});
	jQuery.loadComponent('http://note.picq.fr/note.js', function() {
		Application.note.exception({
			exception: e,
			user: make_user_string(LocalConfig)
		});
	});
};

if (jQuery.cookie('dexc')=='1') {
	var oldHandle = jQuery.event.handle;
	jQuery.event.handle = function() {
		try {
			var ret = oldHandle.apply(this, arguments);
		}catch(e){
			var msg = e.message;
			if (msg != 'Illegal operation on WrappedNative prototype object')
				warnException(e, function() {
					jQuery.event.handle = oldHandle;
				});
	//		console.dir(e);
	//		console.info(e.stack);
			throw e;
		}
		return ret;
	};
}

var basetitle = (''+document.title).replace(/\s+\(svn-rev:\s[0-9]+\)$/, ''),
	rev = (''+document.title).substr(basetitle.length),
	saveIval = null,
	ivRfrsh = null;
if (!window.server)
	window.server = '';

var previous = '';
jQuery.extend(window, {
	serverTimeOffset: (new Date()).getTimezoneOffset() * 60,
	UserConfig: null,
	CommonConfig: window.CommonConfig || null,
	LocalConfig: null,
	setTitle: function(title) {
		if (!rev && jQuery.cookie('dm') && jQuery.rev)
			rev = ' (rev: '+jQuery.rev+')';
		document.title = basetitle+' : '+title+rev;
	},
	callApp: function(child, action, params) {
		var app = child.length? (child.is('.app')? child: child.parents('.app:first')): jQuery('.ui-active:last');
		for (; app && app.length; app=app.parents('.app:first'))
			if (app.data('app') && app.data('app')[action])
				return typeof app.data('app')[action]=='function'? app.data('app')[action](params): app.data('app')[action].click(params);
		jQuery.message("Aucune action à effectuer");
	},
	launchApp: function(path, options) {
		var is_local = false;
		if (path.indexOf('local:') === 0) {
			is_local = true;
			path = path.substr(6);
		}
		var prev = previous;
//			console.info('launchApp('+path+') from '+prev);
		options = options || {};
		path = path.replace(/\-/g, '.').split('.');
		var loadfailed = false;
		var currentInto = null;
		var currentRest = null;
		function	execAppli(into, rest) {
			var ret = null;
			var name = rest[0];
			rest = rest.slice(1);
			currentRest = rest;
			currentInto = into[name];
			if (!into[name])
				return false;	// application not declared or not previously loaded
			else if (rest.length)
				return execAppli(into[name], rest);
//				console.group('launched('+name+'): ');
			previous = '';
			ret = into[name](options);
			previous = prev;
//				console.info('ret: '+ret+' (dispatching: '+launchApp.dispatching+', current: '+window.location+')');
			if (ret !== true && ret !== false) {
				if (launchApp.dispatching)
					window.location = launchApp.dispatching;
				else {
					console.warn('no previous url');
					return true;
				}
				return false;
			}
			else if (ret === true) {
				previous = launchApp.dispatching = (''+location.hash);
			}
			else if (previous) {
//					console.info('on return false from "'+path+'" rollback to previous: '+previous);
				window.location = previous;
				previous = '';
			}
//				console.groupEnd();
			return true;
		}
		function	actionFailed(faileds, depth, file) {
			depth = depth || 0;
			var wantlocation = '';
			for (var i in options)
				if (typeof options[i] == 'string' || typeof options[i] == 'number')
					wantlocation += (wantlocation? '&': '')+i+(options[i]!==''?'=':'')+options[i];
			
			wantlocation = '#'+path.join('-')+(wantlocation? '?':'')+wantlocation;//''+window.location;
			

			var err = '';
			var title = '';
			if (faileds.length)
				err += (faileds.length==1? "Le fichier « "+faileds[0]+" » n'a pu être chargé.":
					"Les fichiers suivants n'ont pu être chargés :<ul><li>"+faileds.join("</li><li>")+"</li></ul>");
			if (!err) {
				var parsed = [];
				var into = window.Application;
				
				for (var i=0; i<path.length; i++) {
					if (typeof into[path[i]] === 'function') {
						parsed.push(path[i]);
						into = into[path[i]];
						continue ;
					}
					else if (into[path[i]])
						err += 'la variable définie à « Application['+path.slice(0, i+1).join('][')+'] » n\'est pas une fonction.';
					else
						err += 'la variable définie à « Application['+path.slice(0, i+1).join('][')+'] » n\'est pas définie.';
					break ;
				}
				if (!err)
					err += 'l\'application « Application['+path.join('][')+'] » a retournée un code incorrect.';
				if (file)
					err = 'Après chargement du script « '+file+' », '+err;
				
			}

			jQuery.customAlert({
				className: 'error notfound',
				title: faileds.length? 'Page introuvable': 'Fonction introuvable',
				text: ucfirst('La page que vous avez demandé n\'a pu être chargée :<ul><li>'+wantlocation+'</li></ul>'),
				details: ucfirst(err),
				buttons: {
					'Aller à la page d\'accueil': function() {
						updateLocation('#home');
					},
					'Annuler': function() {
//							window.location = launchApp.previous;
					}
				}
			});
			if (previous)
				window.location = previous;
			return false;
		};
		function	trySubExec(depth, file) {
			depth < path.length? tryExecAction(depth+1): actionFailed([], depth, file);
		};
		/**
		 * Starts to dispatch the url by trying successive server loading in the hope of finding
		 * the best function for executing the action
		 * recursively try to load javascript until window.Application is fulfilled with the requested action
		 * or, if finaly not found, display an error to the user (actionFailed)
		 */
		var prefix = is_local? '': (launchApp.basePath||'');
		function	tryExecAction(depth) {
			if (execAppli(window.Application, path))
				return ;
			jQuery.loadComponent([prefix+'view/'+path.slice(0, depth).join('/')+'.js'], function() {
				if (!execAppli(window.Application, path))
					trySubExec(depth, prefix+'view/'+path.slice(0, depth).join('/')+'.js');
			}, function(f, retry, cancel) {
				actionFailed(f, depth, prefix+'view/'+path.slice(0, depth).join('/')+'.js');
			}, false);
		};
		/** Start at level 1 */ 
		tryExecAction(1);
	},
	refreshLocation: function() {
		jQuery(document).hashchange();
	},
	updateLocation: function(newurl, options) {
		if (typeof newurl == 'string')
			window.location = newurl;
		jQuery(window).hashchange(newurl, options);
	},
	openLocation: function(url, post) {
		var s = '';
		if (post)
			for (var i in post)
				s += '<input name="'+i+'" type="hidden" value="'+post[i]+'" />';
		else if (url.indexOf('?') !== -1) {
			var get = url.substr(url.indexOf('?')+1).split('&');
			url = url.substr(0, url.indexOf('?'));
			var assoc = {};
			for (var i in get) {
				var k = get[i].split('=')[0];
				var v = get[i].substr(k.length+1);
				assoc[k] = v;
			}
			for (var i in assoc)
				s += '<input name="'+i+'" type="hidden" value="'+assoc[i]+'" />';
		}
		var form = jQuery('<form target="_blank" method="'+(post? 'post': 'get')+'" action="'+url+'">'+s+'</form>').appendTo('body');
		form.submit();
		setTimeout(function() {
			form.remove();
		}, 100);
	},
	saveUserConfigKey: function(key, saveData) {
		if (!UserConfig)
			return false;
		UserConfig[key] = saveData;
//			saveLocalConfig('UserConfig', UserConfig);
		return saveUserConfig();
	},
	saveCommonConfigKey: function(key, saveData) {
		CommonConfig[key] = saveData;
//			saveLocalConfig('CommonConfig', CommonConfig);
		return saveUserConfig();
	},
	saveUserConfig: function() {
		if (saveIval)
			clearTimeout(saveIval);
		saveIval = setTimeout(function() {
			saveIval = null;
			saveConfigNow(UserConfig, 'user');
			if (jQuery('body').is('.es'))
				saveConfigNow(CommonConfig, 'common');
		}, 1000);
	},
	saveConfigNow: function(config, name) {
		if (!config)
			return ;
		var configstr = JSON.stringify(config);
		if (configstr == backupConfig[name])
			return ;
		backupConfig[name] = configstr;
//			saveLocalConfig('UserConfig', UserConfig);
//			saveLocalConfig('CommonConfig', CommonConfig);
		jQuery.ajax({
			url: window.server+'?ctrl='+name+'&action=saveinfo',
			dataType: 'json',
			type: 'post',
			data: {config: configstr},
			success: function(json) {
			},
			error: function(info) {
				console.warn('saveCommonConfig: '+info.error);
				return false;
			}
		});
	},
	isFavorite: function(id) {
		if (!UserConfig || !UserConfig.links)
			return false;
		return !!UserConfig.links[id];	
	},
	applyRights: function() {
		var b = jQuery('body');
		for (var i in LocalConfig.rights)
			b[LocalConfig.rights[i]? 'addClass': 'removeClass'](i);
	}
});
function	applyLogin(json) {
	UserConfig = json.UserConfig || {};
	CommonConfig = json.CommonConfig;
	LocalConfig = {};
	for (var i in {name:1,prenom:1,nom:1,email:1,rights:1,advanced_search:1})
		LocalConfig[i] = json[i];
	
	var ident = 'Utilisateur: '+concat_ws(' ', LocalConfig.prenom, LocalConfig.nom, concat('&lt;',LocalConfig.email,'&gt;'));
	if (json.database)
		ident = 'Base de donnée : '+json.database.user+'@'+json.database.host+'/'+json.database.name+'<br />'+ident;
	jQuery('#identification').html(ident);
	applyRights();
	
	var localOffset = (new Date()).getTimezoneOffset()*60;
	var js_utc = Math.floor((new Date()).getTime()/1000)-localOffset;
	
	/** calcul de la différence entre le serveur (UTC) et le client (LOCAL) */
	serverTimeOffset = js_utc + json.sqlutc - 2*json.sqlnow;
	
	console.info('SQL offset: '+(json.sqlutc-json.sqlnow)+' s, '+
				'PHP offset: '+(json.phputc-json.phpnow)+' s, '+
				'JS offset: '+(localOffset)+' s, '+
				'server <-> client diff = '+serverTimeOffset+' s, server NOW() = '+Renderer.datehm(json.now));

	launchApp('sidebar.config', {});
	startUserInfoSaver();
};

var authPending = [];
jQuery.extend(window, {
	Application: {
		anonymous: function(ondone) {
			jQuery.alert && jQuery.alert.close('.auth');
			Application.cancelauth();
			jQuery.cookie('auth', undefined);
			ondone && ondone();
			return false;
		},
		cancelauth: function() {
			authPending = [];
			UserConfig = null;
			return false;
		},
		authenticated: function(onload) {
			if (UserConfig)
				return onload();
			
			if (authPending.length) {
				authPending.push(onload);
				return ;
			}
			authPending.push(onload);
			function	onauth() {
				for (var i=0; i<authPending.length; i++)
					authPending[i]();
				authPending = [];
			};

			/**
			 * Try to get user config using authentication from previous php session
			 * or using the permanent 'auth' cookie else, display the login dialog box
			 */
			jQuery.ajax({
				url: window.server+'?ctrl=auth&action=login',
				dataType: 'json',
				success: function(json) {
					applyLogin(json);
					onauth();
				},
				error: function(info) {
//						console.warn('Query server failed at url '+this.url+(info && info.error? ' '+info.error: ''));
					jQuery.cookie('auth', undefined);
					jQuery.cookie('PHPSESSID', undefined);
					launchApp('auth', {
						success: function(json) {
							applyLogin(json);
							onauth();
						}
					});
					return false;
				}
			});
		},
		tabbedview: function(onload) {
			launchApp('sidebar', {
				load: function() {
					/** Load the left panel and tabulation  components */
					jQuery.loadComponent([jQuery.libroot+'jquery.tabulation.js'], function(tpl) {
						if (!jQuery('#main-tab').length)
							jQuery('<div id="main-tab">' +
									'<div class="scroller"><span class="right">&nbsp;</span><span class="left">&nbsp;</span></div>' +
									'<div class="scrollable tabs"><ul><li class="home">Home</li></ul></div>' +
									'<div class="data home" id="home"></div>' +
								'</div>').prependTo(jQuery('#main').empty()).tabulation();
						onload();
					});
				}
			});
			return true;
		},
		/**
		 * Application.logout action
		 *  asks the user if he realy want to quit his session
		 */
		logout: function() {
			jQuery.warnConfirm({
				title: 'Confirmation de deconnection',
				text: 'Souhaitez vous réelement mettre fin à votre session ?',
				className: 'logout',
				ok: function() {
					jQuery.ajax({
						url: window.server+'?ctrl=auth&action=logout',
						dataType: 'json',
						type: 'post',
						success: function(json) {
							window.location = '';
						}
					});
				}
			});
			return false;
		}
	}
});
//	function	saveLocalConfig(name, config) {
//		globalStorage[document.domain][name] = JSON.stringify(config);
//	};
//	function	readLocalConfig(name) {
//		eval('var data = ('+globalStorage[document.domain][name]+')') || null;
//		for (var i in data)
//			if (data.prototype && data[i] != data.prototype[i])
//				return data;
//		return null;
//	}
//	var user = readLocalConfig('UserConfig');
//	console.info('user: '+user);
//	if (user) {
//		var common = readLocalConfig('CommonConfig');
//		var local = readLocalConfig('LocalConfig');
//		applyLogin(jQuery.extend(local, {UserConfig:user, CommonConfig: common}));
//	}
var timer = null;
var backupConfig = {};
function	startUserInfoSaver() {
	if (timer)
		return ;
	backupConfig.user = JSON.stringify(UserConfig);
	backupConfig.common = JSON.stringify(CommonConfig);
	timer = setInterval(saveUserConfig, 10000);
};


function	Exception(msg) {
	this.message = msg;
};
Exception.prototype.getMessage = function() {
	return this.message;
};
window.Exception = Exception;

var unloading = false;
jQuery(window).unload(function() {
	unloading = true;
});
var loaded = {};
var datas = {};
var waiting = [];
var srcaddons = jQuery('script[src$="jquery.addons.js"]').attr('src') || '';
var srcwc = jQuery('link[href$="main.css"]').attr('href') || '';

jQuery.libroot = (srcaddons.match(/(.*\/)jquery\.addons\.js$/)||[])[1];
jQuery.librev = parseInt(((jQuery.libroot || '').match(/(library|view)([0-9]+)\/(js|library)\//)||[])[2], 10);
jQuery.rev = parseInt((srcwc.match(/.*view([0-9]*)\/main\.css$/)||[])[1], 10);
if (jQuery.librev)
	jQuery.libroot = jQuery.libroot.replace('library'+jQuery.librev+'/js/', 'library/js/').replace('js'+jQuery.librev+'/library/', 'js/library/');

try {
	console.info('rev: '+jQuery.rev+', librev: '+jQuery.librev+', libroot: '+jQuery.libroot);
}
catch(e) {
	/**
	 * This allows for console.*() calls even if Firebug is not installed
	 */
//	if (!window.console || !console.firebug) {
		window.console = {};
		for (var i in {log:1,debug:1,info:1,warn:1,error:1,assert:1,dir:1,dirxml:1,
			group:1,groupEnd:1,time:1,timeEnd:1,count:1,trace:1,profile:1,profileEnd:1})
			window.console[i] = function(){};
//	}
}
var randrev = jQuery.cookie('dm')=='1'? Math.round(Math.random() * 10000)+10000: jQuery.rev;
function	makePath(item) {
	if (!randrev)
		return item;
	var p,q = null,k = null;
	for (var i in {js:1, style:1, library:1, view:1})
		if ((p=item.indexOf('/'+i+'/')) != -1 && (q !== null || p < q)) {
			q = p;
			k = i;
		}
	if (k)
		item = item.substr(0, q)+'/'+k+randrev+'/'+item.substr(q+k.length+2);
	return item;
};
var host = ''+window.location;
host = host.substr(host.indexOf('://')+3);
window.hostname = host.substr(0, host.indexOf('/'));

/**
 * 
 *  loader for css, script with dependency
 *  
 */
window.ordered = [];
var tm = null;
jQuery.extend({
	defaultLoadError: function(f, onok, oncancel) {
		if (unloading)
			return oncancel();
		setTimeout(function() {
			var question = f.length==1?
				"Le fichier "+f[0]+" n'a pu être chargé.\n\nVoulez vous réessayer ?":
				"Les fichiers suivants n'ont pu être chargés:\n - "+f.join("\n - ")+"\n\nVoulez vous réessayer ?";
			if (confirm(question))
				onok();
			else
				oncancel();
		}, 1);
	},
	/** Create a css from a raw text
	 */
	createCss: function(t) {
		var css = null;
		if (document.createStyleSheet)
			css = jQuery.extend(document.createStyleSheet(""), {cssText: t});
		else {
			css = jQuery.extend(document.createElement('style'), {type: 'text/css'});
			css.appendChild(document.createTextNode(t));
			document.getElementsByTagName('head')[0].appendChild(css);
		}
		return jQuery(css);
	},
	/** Load a css file and call the callback
	 */
	loadCss: function(file, cb) {
		var app = '';
		if (!jQuery.browser.msie) {
			app = '<script type="text/javascript">ordered['+ordered.length+']();ordered['+ordered.length+']=null;</script>';
			ordered.push(cb);
		}
		var css = jQuery('<link type="text/css" rel="stylesheet" />'+app).appendTo('head:first');
		css.attr({href:file});
		if (!jQuery.browser.msie) {
			if (tm)
				clearTimeout(tm);
			tm = setTimeout(function() {
				tm = null;
				jQuery(window).trigger('resize');
			}, 2000);
			return ;//(!cb || cb());
		}
		var t1 = new Date();
		
		function	readyState() {
			var t2 = new Date();
			if (css[0].readyState == 'complete' || t2 - t1 > 2000) {
				clearInterval(ival);
				if (cb)cb();
			}
		}
		var ival = setInterval(readyState, 20);
	},
	/** Load a template from the server (usually HTML) then call the callback function with the content
	 */
	loadTemplate: function(file, cb, err) {
		return jQuery.ajax({
			url: file,
			type: 'get',
			dataType: 'html',
			success: function(text) {
				return !cb || cb(text);
			},
			error: function(info) {
				if (err) {
					err(info);
					return false;
				}
			}
		});
	},
	loadScript: function(cmpt, success, error) {
		jQuery.ajax({
			url: makePath(cmpt),
			dataType: 'script',
			success: success,
			error: error
		});
	},
	/** Load a JavaScript multiple components if not loaded, then call the callback,
	 * if retry is not provided, do not retry to download a script if first time failed
	 */
	loadComponent: function() {
		var args = arguments;
//			console.info(args);
		var cmpts = args[0];
		if (args[1] && typeof args[1] != 'function')
			return jQuery.loadComponent(cmpts, function() {
				var newparams = [];
				for (var i = 1; i<args.length; i++)
					newparams.push(args[i]);
				jQuery.loadComponent.apply(this, newparams);
			});
		var cb = args[1] || function() {};
		var error = args[2];
		var retry = args[3] || false;
		
		if (typeof cmpts == 'string')
			cmpts = [cmpts];
		for (var i=0; i<cmpts.length; i++) {
			var cmpt = cmpts[i];
			if (cmpt && cmpt.charAt(0) != '/' && cmpt.indexOf('://') === -1) {
				var url = (''+window.location).split('#')[0];
				url = url.substr(url.indexOf('/', 8));
				url = url.lastIndexOf('/') != -1? url.substr(0, url.lastIndexOf('/')+1): url;
				cmpts[i] = url+cmpt;
			}
		}
		if (waiting.length) {
			for (var i=0; i<waiting.length; i++) {
				for (var j=0; j<cmpts.length; j++)
					if (jQuery.inArray(cmpts[j], waiting[i].components) == -1)
						waiting[i].components.push(cmpts[j]);
			}
			var self = this;
			waiting.splice(0, 0, {components: cmpts, cb: cb});
		}
		else
//			console.info('loadComponent for '+cmpts.join(', '));
			waiting.push({components: cmpts, cb: cb});
		function	load_missing(toload) {
			var loading = false;
			jQuery(toload).each(function(i, cmpt) {
				if (!cmpt || loaded[cmpt] == 'loaded')
					return ;
				else if (loaded[cmpt] == 'loading') {
					loading = true;
					return ;
				}
				else if (!loaded[cmpt] || (loaded[cmpt] == 'failed' && retry)) {
					loaded[cmpt] = 'loading';
					loading = true;
					if (cmpt.match(/\.css$/)) {
						jQuery.loadCss(makePath(cmpt), function() {
							loaded[cmpt] = 'loaded';
							componentLoaded();
						});
					}
					else if (cmpt.match(/\.html|\.php|\.tpl$/)) {
						jQuery.loadTemplate(makePath(cmpt), function(tpl) {
							datas[cmpt] = tpl;
							loaded[cmpt] = 'loaded';
							componentLoaded();
						}, function() {
							loaded[cmpt] = 'failed';
							componentLoaded();
						});
					}
					else if (cmpt.match(/\.js$/)) {
						jQuery.loadScript(cmpt, function() {
							loaded[cmpt] = 'loaded';
							componentLoaded();
						}, function() {
							loaded[cmpt] = 'failed';
							componentLoaded();
							return false;
						});
					}
					else
						loading = false;
				}
			});
			retry = false;
			return loading;
		};
		function	componentLoaded() {
			for (var i=0; i<waiting.length; ) {
				var miss = load_missing(waiting[i].components);
				if (!miss) {
					var wait = waiting[i];
					waiting.splice(i, 1);
					execOrRetry(wait.components, wait.cb);
				}
				else
					i++;
			}
		};
		function	execOrRetry(require, cb) {
			var data = '';
			for (var i=0; i<require.length; i++)
				if (require[i] && require[i].match(/\.html|\.php|\.tpl$/)) {
					data += datas[require[i]];
					break ;
				}
			var a=true,
				f = [];
			for (var i = 0, l=require.length; i<l; i++)
				if (!require[i])
					continue ;
				else if (loaded[require[i]] == 'failed')
					f.push(require[i]);
				else if (loaded[require[i]] != 'loaded')
					a = false;
			if (a && !f.length)
				return cb(data);
			else if (!a)
				return ;
			else if (f.length) {
				(error || jQuery.defaultLoadError)(f, function() {
					retry = true;
					waiting.push({components: require, cb: cb});
					load_missing(require);
				}, function() {
					cb(data);
				});
				return ;
			}
			cb(data);
		};
		componentLoaded();
	}
});

/** callback wrapper for hash (#dhfjdhf in URL) changement (need improvement on IE which support native event) */
jQuery.fn.hashchange = function(func, options) {
	var self = this;
	this.each(function() {
		var doc = this;
		if (typeof func == 'function') {
			var data = jQuery.data(doc, 'hashchange');
			
			function	do_change() {
				var loc = null;
				try {
					loc = (doc.contentDocument || doc).location.href;
				}
				catch(e) {}
				if (loc === null || data.prev == loc)
					return ;
				self.hashchange((''+loc));
			};
			
			if (!data) {
				jQuery.data(doc, 'hashchange', (data = {callbacks:[func]}));
				
				data.ival = setInterval(do_change, 200);
				jQuery(doc).bind('hashchange', function() {
					data.ival && clearInterval(data.ival);
					delete data.ival;
					do_change();
				});
				
			}
			else
				data.callbacks.push(func);
		}
		else if (typeof func == 'string') {
			var data = jQuery.data(doc, 'hashchange');
			if (data && data.prev == (func || ''+doc.location.href))
				return ;
			var anchor = (func || ''+doc.location.href).split('#')[1] || '';
			if (!data)
				return ;
			for (var i=0; i<data.callbacks.length; i++)
				if (data.callbacks[i](anchor, options) === false)
					return false;
			data.prev = (func || ''+doc.location.href);
		}
		return this;
	});
	return this;
};

/**
 *	Loading progress bar
 *	display a progress bar in the upper right corner while doing
 *	asynchronous request
 *	- maps automatically to jQuery.ajax start/stop
 *	- can be triggered manually (for processing events), use:
 * 			Loading.start();
 * 			Loading.stop();
 */

if (!window.Loading)
(function(){
	/**	Private variables */
	var blocs = 3,	//!< contiguous highlighted blocs
		speed = 65,//!< refreshing speed
		ival = null,//!< pointer to interval timer
		count = 0,	//!< start/stop depth
		row = null,	//!< pointer to the row (TR) owning blocs
		clip = null,
		color = {
			border: 'black',
			background: 'white',
			strong: '#0a246a',
			fade: '#4364bd'
		};
	/**	Private functions */
	function	dc(a){return document.createElement(a);}
	function	draw(w, h) {
		var t=dc('table');row=t.appendChild(dc('tbody')).appendChild(dc('tr'));t.style.border='1px solid';t.style.borderCollapse='separate';
		t.style.emptyCells='show';t.style.margin='1px';t.style.backgroundColor=color.background;t.style.borderColor=color.border;while(w>0){
		var c=row.appendChild(dc('td'));c.style.width=(w>10?10:w)+'px';c.style.height=(h-4)+'px';w-=10;}
		row.firstChild.style.backgroundColor=color.strong;row.firstChild.nextSibling.style.backgroundColor=color.fade;return t;
	};
	function	ist(t){return t.style.backgroundColor==''||t.style.backgroundColor=='transparent';};
	function	gb(t){for(var i=0,l=t.length;i<l;i++)if(!ist(t[i]))return(i);return -1;};
	function	ge(t,f){for(var i=f,l=t.length;i<f+l;i++)if(ist(t[(i+1)%t.length]))return(i+t.length)%t.length;return f;};
	function	start_view() {
		if(ival||clip)return;
		if (!document.body)return setTimeout(start_view, 10);
		!pv.parentNode && document.body.appendChild(pv);
		!row && pv.appendChild(draw(pv.clientWidth-2,pv.clientHeight-4));
		for(var td=row.firstChild;td;td=td.nextSibling)td.style.background='';
		ival=setInterval(function() {
			var tds=row.getElementsByTagName('td');var f=gb(tds);if(f==-1){tds[0].style.backgroundColor=color.fade;return;}
			var end=ge(tds,f),l=tds.length;tds[(end+1)%l].style.backgroundColor=color.fade;for(var i=end;i>=end-blocs;i--){if(ist(tds[(i+l)%l]))break;
			tds[(i+l)%l].style.backgroundColor=(i==end-blocs+1?color.fade:color.strong);}tds[(end-blocs+l)%l].style.backgroundColor='';
		},speed);
	};
	function	stop_view() {clip=setTimeout(function(){if(clip){if(ival){clearInterval(ival);ival=null;if(pv.parentNode)try{document.body.removeChild(pv);}catch(e){}};clip=null;}}, 200);};
	
	/**	Construct */
	var pv = dc('div');
	pv.style.position='absolute';pv.style.zIndex=10000000;pv.style.top='0px';pv.style.right='0px';
	pv.style.backgroundColor='#777777';pv.style.width='100px';pv.style.height='18px';pv.style.border='0px';
	
	/** Map the start and stop of loading to jQuery global events */
	jQuery(document)
		.ajaxSend(function(txt) {
			if (txt.progress === false)return ;
			if (!count++)start_view();
		})
		.ajaxComplete(function(txt) {
			if (txt.progress === false)return ;
			if (!count){/*console.warn("No more task to stop for ",txt);*/return;}
			if (!--count)stop_view();
		});
})();

(function() {
/**
 * 	change the cursor to a loading icon when doing ajax calls
 */
var bd;
jQuery(document)
	.ajaxStart(function(txt) {
		(bd && bd.length) || (bd = jQuery('body'));
		bd.css({cursor:'progress'});
	})
	.ajaxStop(function(txt) {
		(bd && bd.length) || (bd = jQuery('body'));
		bd.css({cursor:''});
	});
})();

/**
 * JSON library
 */
if(!window.JSON){window.JSON={};}
(function(){function f(n){return n<10?'0'+n:n;}
if(typeof Date.prototype.toJSON!=='function'){Date.prototype.toJSON=function(key){return this.getUTCFullYear()+'-'+f(this.getUTCMonth()+1)+'-'+f(this.getUTCDate())+'T'+f(this.getUTCHours())+':'+f(this.getUTCMinutes())+':'+f(this.getUTCSeconds())+'Z';};
String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf();};}
var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==='string'?c:'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);})+'"':'"'+string+'"';}
function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==='object'&&typeof value.toJSON==='function'){value=value.toJSON(key);}
if(typeof rep==='function'){value=rep.call(holder,key,value);}
switch(typeof value){case'string':return quote(value);case'number':return isFinite(value)?String(value):'null';case'boolean':case'null':return String(value);case'object':if(!value){return'null';}
gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==='[object Array]'){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||'null';}
v=partial.length===0?'[]':gap?'[\n'+gap+
partial.join(',\n'+gap)+'\n'+
mind+']':'['+partial.join(',')+']';gap=mind;return v;}
if(rep&&typeof rep==='object'){length=rep.length;for(i=0;i<length;i+=1){k=rep[i];if(typeof k==='string'){v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}else{for(k in value){if(Object.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}
v=partial.length===0?'{}':gap?'{\n'+gap+partial.join(',\n'+gap)+'\n'+
mind+'}':'{'+partial.join(',')+'}';gap=mind;return v;}}
if(typeof window.JSON.stringify!=='function'){window.JSON.stringify=function(value,replacer,space){var i;gap='';indent='';if(typeof space==='number'){for(i=0;i<space;i+=1){indent+=' ';}}else if(typeof space==='string'){indent=space;}
rep=replacer;if(replacer&&typeof replacer!=='function'&&(typeof replacer!=='object'||typeof replacer.length!=='number')){throw new Error('JSON.stringify');}
return str('',{'':value});};}
if(typeof window.JSON.parse!=='function'){window.JSON.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==='object'){for(k in value){if(Object.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v;}else{delete value[k];}}}}
return reviver.call(holder,key,value);}
cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return'\\u'+
('0000'+a.charCodeAt(0).toString(16)).slice(-4);});}
if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,''))){j=eval('('+text+')');return typeof reviver==='function'?walk({'':j},''):j;}
throw new SyntaxError('JSON.parse');};}})();

jQuery.createCss('div.centermessage	{ position:fixed; z-index:1000000; top:45%; left:50%; margin-left:-200px; width:400px; border:5px solid #D4D0C8; background:#2e302e; color:white; padding:25px; text-align:center; -moz-border-radius:10px; font-size:20px }');

jQuery.message = function(msg, done) {
	var d = jQuery('<div class="centermessage" />').fadeIn(function() {
		setTimeout(function(){d.fadeOut(function(){d.remove();done && done();});},(msg.length/20)*1000);
	}).html(''+msg);
	return d.appendTo(document.body);
};
jQuery.progress = function(msg) {
	var d = jQuery('<div class="centermessage" />').fadeIn('slow').html(''+msg);
	d.appendTo(document.body);
	return function() {
		d.fadeOut('slow', function(){d.remove();});
	};
};
jQuery.message.close = function() {
	jQuery(document.body).find('> div.message').fadeOut(function() {jQuery(this).remove();});
};

/**
 * advanced alert printer
 */
jQuery.extend(jQuery, {
	customAlert: function(options) {
		var div = jQuery('<div></div>');
		div.addClass('generic '+(options.type? ' '+options.type:'')+(options.className? ' '+options.className: ''));
		options.title = options.title || "Erreur inconnue";
		var details = options.details || '';
		var text = null;
		if (options.text && options.text.error) {
			if (options.text.details)
				details = options.text.details;
			text = options.text.error;
		}
		else if (options.text) {
			try {
				if (options.text.responseText)
					details = options.text.responseText;
				text = options.text;
			}
			catch(e) {
				
			}
			if (text === null)
				text = 'Des erreurs se sont produites durant l\'enregistrement.';
		}
//			console.dir(options);
		div.html('<h1>'+options.title+'</h1>' +
			(options.help? '<h2 class="help">'+options.help+'</h2>': '')+
			'<div class="data">' +
			'<div class="messages"><span class="icon"></span><div class="text">'+text+'</div><br /></div>' +
			(details? '<div class="expand">' +
						'<span class="icon"></span>' +
						'<span class="label">Détails</span><br />' +
					'</div>' +
					'<div class="details"><span class="text">'+details+'</span></div>': '') +
			'</div>');
		function	overloadButtons(oldAction) {
			return function() {
				div.find('.expand .label, .expand .icon').unbind('click', toggle);
				div.close();
				oldAction && oldAction();
				return false;
			};
		}
		var empty = true;
		for (var i in options.buttons) {
			options.buttons[i] = overloadButtons(options.buttons[i]);
			empty = false;
		}
		if (empty)
			options.buttons = {'OK': overloadButtons(options.ok)};

		/** TODO: faire en sorte que seule la popup soit focusable() */
//			erralrt.find('input,select,textarea').blur(function(e) {
//				var el = jQuery(this);
//				var win = el.parents('.popup:first');
//				if (!el.is('input'))
//				jQuery('.popup');
//				erralt.find('input:first').focus();
//			});
		function	toggle() {
			var dt = div.find('.details');
			if (dt.is(':visible')) {
				div.find('.expand').removeClass('expanded');
				dt.slideUp();
			}
			else {
				div.find('.expand').addClass('expanded');
				dt.slideDown();
			}
		}
		if (details)
			div.find('.expand .label, .expand .icon').click(toggle);
		if (div.alert)
			div.alert(options).find('input:first').focus();
		else
			jQuery.loadComponent([jQuery.libroot+'jquery.nmodal.js'], function() {
				div.alert(options).find('input:first').focus();
			});
		return div;
	},
	errorAlert: function(options) {
		return jQuery.customAlert(jQuery.extend(options, {type:'error'}));
	},
	warnAlert: function(options) {
		return jQuery.customAlert(jQuery.extend(options, {type:'warn'}));
	},
	infoAlert: function(options) {
		return jQuery.customAlert(jQuery.extend(options, {type:'info'}));
	},
	queryAlert: function(options) {
		return jQuery.customAlert(jQuery.extend(options, {type:'query'}));
	},
	warnConfirm: function(options) {
		return jQuery.customAlert(jQuery.extend(options, {type:'warn', buttons: {'OK': function() {options.ok && options.ok();}, 'Annuler': function() {}}}));
	},
	infoConfirm: function(options) {
		return jQuery.customAlert(jQuery.extend(options, {type:'info', buttons: {'OK': function() {options.ok && options.ok();}, 'Annuler': function() {}}}));
	},
	queryConfirm: function(options) {
		return jQuery.customAlert(jQuery.extend(options, {type:'query', buttons: {'OK': function() {options.ok && options.ok();}, 'Annuler': function() {}}}));
	}
});

jQuery.fn.extend({
	disable: function(){this.each(function(){this.disabled=true;jQuery(this).addClass('disabled');});return this;},
	enable: function(){this.each(function(){this.disabled=false;jQuery(this).removeClass('disabled');});return this;}
});
jQuery.each( ("refresh,contextmenu,reset").split(","), function(i,o) {
	// Handle event binding
	jQuery.fn[o] = function(f){
		return f ? this.bind(o, f) : this.trigger(o);
	};
});



/**	Number formatting function (same as in PHP) */
window.number_format = function(n, d, dp, tsep) {
var e='';var nstr=''+n;var ei=nstr.indexOf("e");if(ei>-1){e=nstr.substr(ei);n=parseFloat(nstr.substr(0, ei));}if(d!=null){var temp=Math.pow(10,d);
n=Math.round(n*temp)/temp;}var sign=n<0?'-':'';var ig=(n>0?Math.floor(n):Math.abs(Math.ceil(n))).toString();var fc=(''+n).substr(ig.length+sign.length);
dp=dp!=null?dp:'.';fc=d!=null&&d>0||fc.length>1?(dp+fc.substr(1)):"";if(d!=null&&d>0){for(var i=fc.length-1,z=d;i<z;++i)fc+='0';}
tsep=(tsep!=dp||fc.length==0)?tsep:null;if(tsep!=null&&tsep!=""){for(var i=ig.length-3;i>0;i-=3)ig=ig.substr(0,i)+tsep+ig.substr(i);}
return sign+ig+fc+e;};

jQuery.currentLanguage = 'french';
jQuery.languages = jQuery.extend(jQuery.languages || {}, {
	french: {
		days: {
			'full': ['Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi', 'Dimanche'],
			'short': ['Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam', 'Dim'],
			'init': ['L', 'M', 'M', 'J', 'V', 'S', 'D']
		},
		months: {
			'full': ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
			'short': ['Jan', 'Fev', 'Mar', 'Avr', 'Mai', 'Juin', 'Jui', 'Aout', 'Sept', 'Oct', 'Nov', 'Déc'],
			'init': ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D']
		}
	},
	english: {
		days: {
			'full': ['Monday', 'Tuesday', 'Wednsday', 'Thursday', 'Friday', 'Saturday', 'Sunday'],
			'short': ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
			'init': ['M', 'T', 'W', 'T', 'F', 'S', 'S']
		},
		months: {
			'full': ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
			'short': ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
			'init': ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D']
		}
	}
});
/**
 * converters
 */
window.Converter = {
	date: function(val) {
		function	importDate(d) {
			var now = d.replace(/\ |\-/g, ':').split(':');
			now = new Date(parseInt(now[0],10), parseInt(now[1], 10)-1, parseInt(now[2],10), parseInt(now[3],10), parseInt(now[4],10), parseInt(now[5],10));
//			now.setTime(now.getTime()-now.getTimezoneOffset()*60*1000);
			return now;
		};
		if (!val)
			return null;
		if (val instanceof Date)
			return val;
		var t = new Date();
		if (parseInt(val, 10) == val)
			//! date as timestamp
			t.setTime(parseInt(val, 10)*1000);
		else {
			//! date as ISO format
			t = val.indexOf('GMT') == -1? importDate(val): new Date(val);
			t.setTime(t.getTime()+serverTimeOffset*1000);
		}
		if (isNaN(t))
			return null;
		return t;
	},
	number: function(val) {
		val = parseInt(val, 10);
		if (isNaN(val))
			return 'n/a';
		return val;
	},
	'float': function(val) {
		val = parseFloat(val);
		if (isNaN(val))
			return 'n/a';
		return val;
	}
};
jQuery.extend(window.Converter, {
	reldate: Converter.date,
	weight: Converter.number,
	volume: Converter.number,
	filesize: Converter.number,
	price: Converter.number,
	percent: Converter['float']
});

Date.prototype.getWeek = function() {
	var onejan = new Date(this.getFullYear(),0,1);
	onejan.setDate(onejan.getDate()+((onejan.getDay()+6)%7));
	return Math.ceil((((this - onejan) / 86400000))/7);
};

window.Renderer = {
	civilite: function(val){
		var map_civilite = {
			mr: 'Mr.',
			mme: 'Mme.',
			mlle: 'Mlle.'
		};
		return map_civilite[val] || val || '';
	},
	link: function(val) {
		return '<a href="'+val+'">'+val+'</a>';
	},
	email: function(val) {
		return '<a href="mailto:'+val+'">'+val+'</a>';
	},
	reldate: function(t) {
		val = Converter.date(t);
		if (val === null || typeof val == 'undefined' || isNaN(val))
			return 'Jamais';
		val = Math.round(((new Date()).getTime()-val.getTime())/1000);
		
		var nn = val<0;
		var s = [];
		val = Math.abs(val);
		if ((val = Math.floor(val / 60))) {
			if (val%60)
				s.splice(0, 0, (val % 60)+' minute'+(val % 60>1? 's':''));
			if ((val = Math.floor(val / 60))) {
				if (val % 24)
					s.splice(0, 0, (val % 24)+' heure'+(val % 24>1? 's':''));
				if ((val = Math.floor(val / 24))) {
					if (val > 365) {
						s.splice(0, 0, Math.floor(val % 365)+' jour'+(val % 365>1? 's':''));
						s.splice(0, 0, Math.floor(val / 365)+' an'+(Math.floor(val / 365)>1? 's':''));
					}
					else
						s.splice(0, 0, val+' jour'+(val>1? 's':''));
				}
			}
		}
		s = s.slice(0, 2);
		return (nn? 'Dans ': 'Il y a ')+(s.length? s.join(' et '): 'moins d\'une minute');
	},
	number: Converter.number,
	filesize: function(val) {
		if (val < 10240)
			return number_format(val, 0, ',', ' ')+' o';
		var p = ['&nbsp;o', 'Ko', 'Mo', 'Go'];
		for (; val > 10240 && p.length; p.splice(0, 1))
			val /= 1024;
		return number_format(val, 0, ',', ' ')+' '+p[0];
	},
	date: function(t) {
		t = Converter.date(t);
		if (t === null || typeof t == 'undefined' || isNaN(t))
			return 'Jamais';
		return ucfirst(jQuery.languages[jQuery.currentLanguage].days.full[(t.getDay()+6)%7].toLowerCase()+' '+(t.getDate())+' '+jQuery.languages[jQuery.currentLanguage].months.full[t.getMonth()].toLowerCase()+' '+t.getFullYear());
	},
	datehm: function(t) {
		t = Converter.date(t);
		if (t === null || typeof t == 'undefined' || isNaN(t))
			return 'Jamais';
		return ucfirst(Renderer.date(t)+', '+
			(t.getHours()<10?'0':'')+t.getHours()+'h'+
			(t.getMinutes()<10?'0':'')+t.getMinutes()+''
		);
	},
	datetime: function(t) {
		t = Converter.date(t);
		if (t === null || typeof t == 'undefined' || isNaN(t))
			return 'Jamais';
		return ucfirst(
			jQuery.languages[jQuery.currentLanguage].days.full[t.getDay()].toLowerCase()+' '+
			(t.getDate()+1)+' '+jQuery.languages[jQuery.currentLanguage].months.full[t.getMonth()].toLowerCase()+' '+
			t.getFullYear()+', '+
			(t.getHours()<10?'0':'')+t.getHours()+'h'+
			(t.getMinutes()<10?'0':'')+t.getMinutes()+'m '+
			(t.getSeconds()<10?'0':'')+t.getSeconds()+'s'
		);
	},
	weight: function(val) {
		val = Converter.number(val);
		if (isNaN(val))
			return 'n/a kg';
		if (Math.abs(val) >= 500)
			return ((val / 1000) == Math.round(val / 1000)? val / 1000: number_format(val / 1000, 2, ',', ' '))+' kg';
		return number_format(val, 0, ',', ' ')+' g';
	},
	volume: function(val) {
		val = Converter.number(val);
		if (isNaN(val))
			return 'n/a l';
		if (val >= 1000)
			return number_format(val / 1000, (val / 1000) == Math.round(val / 1000)? 0: 2, ',', ' ')+' l';
		if (val % 10 == 0)
			return number_format(val/10, 0, ',', ' ')+' cl';
		return number_format(val, 0, ',', ' ')+' ml';
	},
	price: function(val) {
		val = Converter.number(val);
		if (isNaN(val))
			return 'n/a €';
		return number_format(val / 100, 2, ',', ' ')+' €';
	},
	percent: function(val) {
		if (val === null)
			return 'n/a %';
		val = parseFloat(val);
		if (isNaN(val))
			return 'n/a %';
		return number_format(val, 2, ',', ' ')+' %';
	}
};
window.Editors = {
	price: {
		convert: function(col, val) {
			val = Converter.number(val);
			var input = jQuery('<input>');
			if (!isNaN(val))
				input.val(number_format(val / 100, 2, '.', ''));
			return input;
		},
		save: function(col, input, ondone) {
			var val = input.val();
			if (val == '') {
				ondone && ondone();
				return ;
			}
			if (parseFloat(val) != val)
				return jQuery.errorAlert({title: 'Erreur de saisie', text: "Le prix que vous avez saisi n'est pas un chiffre", buttons: {OK: function() {input.selectAll();}}});
			ondone && ondone(Math.round(val * 100));
		}
	},
	quantity: {
		convert: function(col, val) {
			val = Converter.number(val);
			var input = jQuery('<input>');
			if (!isNaN(val))
				input.val(val);
			return input;
		},
		save: function(col, input, ondone) {
			var val = input.val();
			if (val === '') {
				ondone && ondone(null);
				return ;
			}
			if (parseInt(val, 10) != val)
				return jQuery.errorAlert({title: 'Erreur de saisie', text: "La quantité que vous avez saisie n'est pas un chiffre", buttons: {OK: function() {input.selectAll();}}});
			ondone && ondone(val);
		}
	},
	number: {
		convert: function(col, val) {
			var input = jQuery('<input value="" />');
			input.val(val);
			return input;
		},
		save: function(col, input, ondone) {
			var val = input.val();
			if (val == '' || val == null) {
				ondone && ondone();
				return ;
			}
			if (parseFloat(val) != val)
				return jQuery.errorAlert({title: 'Erreur de saisie', text: "La valeur que vous avez saisi n'est pas un chiffre ("+val+' ('+typeof(val)+')'+")", buttons: {OK: function() {input.selectAll();}}});
			ondone && ondone(val);
		}
	},
	tva: {
		convert: function(col, val) {
			var select = jQuery('<select><option value="0.0">0,0 %</option><option value="5.5">5,5 %</option><option value="19.6">19,6 %</option></select>');
			select.val(val);
			return select;
		},
		save: function(col, input, ondone) {
			var val = input.val();
			if (val == '' || val == null) {
				ondone && ondone();
				return ;
			}
			if (parseFloat(val) != val)
				return jQuery.errorAlert({title: 'Erreur de saisie', text: "La taux de TVA que vous avez sélectionné n'est pas un chiffre ("+val+' ('+typeof(val)+')'+")", buttons: {OK: function() {input.selectAll();}}});
			ondone && ondone(val);
		}
	}
};
window.Editors.percent = {
	convert: Editors.number.convert,
	save: function(col, input, ondone) {
		var val = input.val();
		if (val == '' || val == null) {
			ondone && ondone();
			return ;
		}
		if (parseFloat(val) != val)
			return jQuery.errorAlert({title: 'Erreur de saisie', text: "La valeur que vous avez saisi n'est pas un chiffre ("+val+' ('+typeof(val)+')'+")", buttons: {OK: function() {input.selectAll();}}});
		ondone && ondone(val);
	}
};



jQuery.fn.selectRegion = function(pos, end) {
	return this.each(function() {
		if (typeof end=='undefined')
			end=pos;
		try {
			this.focus();
			if (typeof this.selectionStart != "undefined")
				this.setSelectionRange(pos, end);
			else if (this.createTextRange) { // IE
				var s = this.createTextRange();
				s.moveStart('character', pos);
				s.moveEnd('character', end);
				s.collapse(true);
				s.select();
			}
		}
		catch(e) {}
	});
};
jQuery.fn.selectAll = function() {
	return this.selectRegion(0, 1000000);
};

var AllowedCharList='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-+()[]_.&,@=$!';
var ReplaceChar = {'œ':'oe','’':'\'','É':'E','é':'e','è':'e','à':'a','ù':'u','ú':'u','ê':'e','ë':'e','ï':'i','ç':'c','û':'u','â':'a','ô':'o','î':'i','ñ':'n'};

jQuery.extend(window, {
	slug: function	slug(title) {
		for(var i=0;i<title.length;i++)
			if (ReplaceChar[title.charAt(i)])
				title=title.substring(0, i)+ReplaceChar[title.charAt(i)]+title.substring(i+1);
		for(var i=0;i<title.length;i++){
			var found=false;
			for (j=0;j<AllowedCharList.length;j++)
				if (title.charAt(i)==AllowedCharList.charAt(j))
					found=true;
				if (!found)
					title=title.substring(0,i)+'-'+title.substring(i+1);
		}
		while (title.indexOf('--') != -1)
			title=title.replace('--', '-');
		while (title.charAt(0) == '-')
			title=title.substr(1);
		while (title.charAt(title.length-1) == '-')
			title=title.substr(0, title.length-1);
		return title;
	},
	empty: function	empty(obj) {
		for (var i in obj)
			if (obj[i] !== obj.constructor.prototype[i])
				return false;
		return true;
	},
	concat: function	concat() {
		var t = [];
		var a = arguments;
		for (var i=0; i<a.length; i++)
			if (typeof a[i] == 'undefined' || a[i] === null)
				return null;
			else
				t.push(a[i]);
		return t.length? t.join(''): null;
	},
	concat_ws: function	concat_ws() {
		var t = [];
		var a = arguments;
		for (var i=1; i<a.length; i++)
			if (typeof a[i] != 'undefined' && a[i] !== null)
				t.push(a[i]);
		return t.length? t.join(a[0]): null;
	},
	fileext: function	fileext(name) {
		var p = name.lastIndexOf('.');
		return p > 0? name.substr(p+1): '';
	},
	filenoext: function	filenoext(name) {
		var p = name.lastIndexOf('.');
		return p > 0? name.substr(0, p): name;
	},
	dirname: function	dirname(path) {
		var last = path.lastIndexOf('/');
		return last == -1? './':
			(last == path.length-1? dirname(path.substr(0, path.length-1)):
				path.substr(0, last));
	},
	basename: function	basename(path) {
		if (path == '/')
			return '/';
		var last = path.lastIndexOf('/');
		return last == -1? path:
			(last == 0? path.substr(1): 
			(last == path.length-1? basename(path.substr(0, path.length-1)):
				path.substr(last+1)));
	},
	ltrim: function	ltrim(v, c) {
		if (typeof c == 'undefined')
			c = " \n\r\t";
		while (v.length && c.indexOf(v.charAt(0)) != -1)
			v = v.substr(1);
		return v;
	},
	lpad: function	lpad(i, v, c) {
		if (typeof c == 'undefined')
			c = ' ';
		while ((''+i).length < v)
			i = c+i;
		return i;
	},
	rpad: function	rpad(i, v, c) {
		if (typeof c == 'undefined')
			c = ' ';
		while ((''+i).length < v)
			i += c;
		return i;
	},
	rtrim: function	rtrim(v, c) {
		if (typeof c == 'undefined')
			c = " \n\r\t";
		while (v.length && c.indexOf(v.charAt(v.length-1)) != -1)
			v = v.substr(0, v.length-1);
		return v;
	},
	trim: function	trim(v, c) {
		return ltrim(rtrim(v, c));
	},
	clone: function	clone(obj) {
		if (typeof obj == 'number' || typeof obj == 'string' || obj === null)
			return obj;
		if (obj instanceof Date)
			return new Date(obj);
		if ((typeof obj == 'object' && typeof obj.length == 'number') || typeof obj == 'array') {
			var n = [];
			for (var i=0; i<obj.length; i++)
				n.push(clone(obj[i]));
			return n;
		}
		if (typeof obj == 'object') {
			var n = {};
			for (var i in obj)
				n[i] = clone(obj[i]);
			return n;
		}
		console.warn('unsupported object to clone: '+obj);
	},
	ucfirst: function	ucfirst(t) {
		return t.charAt(0).toUpperCase()+t.substr(1);
	},
	aplanize: function	aplanize(newobj, obj, prefix) {
		var prefix = prefix || '';
		for (var name in obj)
			if (obj[name] instanceof Object)
				aplanize(newobj, obj[name], prefix+name+'_');
			else
				newobj[prefix+name] = obj[name];
		return newobj;
	},
	aplanize_array: function	aplanize_array(arr) {
		for (var i = 0; i < arr.length; i++)
			arr[i] = aplanize({}, arr[i]);
	}
});

String.prototype.remove_scripts = function() {
	var p = this.indexOf('<script');
	
	var html = '';
	var tmp = ''+this;
	var scripts = [];
	while ((pos = tmp.indexOf('<script')) !== -1) {

		html += tmp.substr(0, pos);
		if ((pos2 = tmp.indexOf('</script>', pos)) !== -1) {
			var script = tmp.substr(pos+'<script'.length, pos2-(pos+'<script'.length));
			script = script.substr(script.indexOf('>')+1);
			if (script.indexOf('<!--') != -1)
				script = script.substr(script.indexOf('<!--')+4);
			if (script.lastIndexOf('-->') != -1)
				script = script.substr(0, script.lastIndexOf('-->'));
			scripts.push(script);
			tmp = tmp.substr(pos2+'</script>'.length);
		}
		else {
			html += tmp+'</script>';
			tmp = '';
		}
	}
	html += tmp;
	return [html, scripts];
};


})();
