/*
 * jQuery Address Plugin v1.0
 * http://www.asual.com/jquery/address/
 *
 * Copyright (c) 2009 Rostislav Hristov
 * Dual licensed under the MIT and GPL licenses.
 * http://docs.jquery.com/License
 *
 * Date: 2009-04-28 16:54:00 +0300 (Tue, 28 Apr 2009)
 * Revision: 399
 */
(function(a){a.address=(function(){var c=function(i){a(this).trigger(a.extend(a.Event(i),(function(){var af={value:this.value(),path:this.path(),pathNames:this.pathNames(),parameterNames:this.parameterNames(),parameters:{},queryString:this.queryString()};for(var ae=0,p=af.parameterNames.length;ae<p;ae++){af.parameters[af.parameterNames[ae]]=this.parameter(af.parameterNames[ae])}return af}).call(this)))};var U=function(){c.call(a.address,"init")};var n=function(){c.call(a.address,"change")};var O=function(){var i=T.href.indexOf("#");return i!=-1?ac(o(T.href.substr(i+1))):""};var g=function(){try{top.document;return top}catch(i){return window}};var E=function(p,i){if(B.strict){p=i?(p.substr(0,1)!="/"?"/"+p:p):(p==""?"/":p)}return p};var q=function(i,p){return(h&&T.protocol=="file:")?(p?P.replace(/\?/,"%3F"):P.replace(/%253F/,"?")):i};var ad=function(ag){for(var ae=0,p=ag.childNodes.length,af;ae<p;ae++){if(ag.childNodes[ae].src){k=String(ag.childNodes[ae].src)}if(af=ad(ag.childNodes[ae])){return af}}};var W=function(){if(!s){var p=O();var i=!(P==p);if(t&&r<523){if(D!=X.length){D=X.length;if(typeof z[D-1]!=F){P=z[D-1]}S()}}else{if(h&&i){if(r<7){T.reload()}else{G.value(p)}}else{if(i){P=p;S()}}}}};var S=function(){n();m(v,10)};var v=function(){var p=(T.pathname+(/\/$/.test(T.pathname)?"":"/")+C.value()).replace(/\/\//,"/").replace(/^\/$/,"");var i=window[B.tracker];if(typeof i==f){i(p)}else{if(typeof pageTracker!=F&&typeof pageTracker._trackPageview==f){pageTracker._trackPageview(p)}else{if(typeof urchinTracker==f){urchinTracker(p)}}}};var e=function(){var i=w.contentWindow.document;i.open();i.write("<html><head><title>"+aa.title+"</title><script>var "+x+' = "'+O()+'";<\/script></head></html>');i.close()};var M=function(){if(!R){R=true;a("a").attr("xref",function(){return a(this).attr("href")});if(h&&r<8){aa.body.innerHTML='<iframe id="'+x+'" src="javascript:false;" width="0" height="0"></iframe>'+aa.body.innerHTML;w=aa.getElementById(x);m(function(){a(w).bind("load",function(){var i=w.contentWindow;var p=i.location.href;P=(typeof i[x]!=F?i[x]:"");if(P!=O()){S();T.hash=q(P,true)}});if(typeof w.contentWindow[x]==F){e()}},50)}else{if(t){if(r<418){a(aa.body).append('<form id="'+x+'" style="position:absolute;top:-9999px;" method="get"></form>');I=aa.getElementById(x)}if(typeof T[x]==F){T[x]={}}if(typeof T[x][T.pathname]!=F){z=T[x][T.pathname].split(",")}}}m(function(){U();n();v()},1);if(h&&r>=8){aa.body.onhashchange=W}else{u(W,50)}a("a").attr("href",function(){return a(this).attr("xref")}).removeAttr("xref");a("a[rel*=address:]").address()}};var C={baseURL:function(){var i=T.href;if(i.indexOf("#")!=-1){i=i.substr(0,i.indexOf("#"))}if(i.substr(i.length-1)=="/"){i=i.substr(0,i.length-1)}return i},strict:function(){return B.strict},history:function(){return B.history},tracker:function(){return B.tracker},title:function(){return aa.title},value:function(){if(!Z){return null}return o(E(q(P,false),false))},path:function(){var i=this.value();return(i.indexOf("?")!=-1)?i.split("?")[0]:i},pathNames:function(){var p=this.path();var i=p.split("/");if(p.substr(0,1)=="/"||p.length==0){i.splice(0,1)}if(p.substr(p.length-1,1)=="/"){i.splice(i.length-1,1)}return i},queryString:function(){var p=this.value();var i=p.indexOf("?");return(i!=-1&&i<p.length)?p.substr(i+1):""},parameter:function(aj){var ag=this.value();var ae=ag.indexOf("?");if(ae!=-1){ag=ag.substr(ae+1);var ai=ag.split("&");var ah,af=ai.length;while(af--){ah=ai[af].split("=");if(ah[0]==aj){return ah[1]}}}},parameterNames:function(){var af=this.value();var p=af.indexOf("?");var ag=[];if(p!=-1){af=af.substr(p+1);if(af!=""&&af.indexOf("=")!=-1){var ah=af.split("&");var ae=0;while(ae<ah.length){ag.push(ah[ae].split("=")[0]);ae++}}}return ag}};var G={strict:function(i){B.strict=i},history:function(i){B.history=i},tracker:function(i){B.tracker=i},title:function(i){m(function(){H=aa.title=i;if(J&&w&&w.contentWindow&&w.contentWindow.document){w.contentWindow.document.title=i;J=false}if(!L&&Y){T.replace(T.href.indexOf("#")!=-1?T.href:T.href+"#")}L=false},50)},value:function(ae){ae=ac(o(E(ae,true)));if(ae=="/"){ae=""}if(P==ae){return}L=true;P=ae;s=true;S();z[X.length]=P;if(t){if(B.history){T[x][T.pathname]=z.toString();D=X.length+1;if(r<418){if(T.search==""){I.action="#"+P;I.submit()}}else{if(r<523||P==""){var i=aa.createEvent("MouseEvents");i.initEvent("click",true,true);var p=aa.createElement("a");p.href="#"+P;p.dispatchEvent(i)}else{T.hash="#"+P}}}else{T.replace("#"+P)}}else{if(P!=O()){if(B.history){T.hash="#"+q(P,true)}else{T.replace("#"+P)}}}if((h&&r<8)&&B.history){m(e,50)}if(t){m(function(){s=false},1)}else{s=false}}};var x="jQueryAddress",f="function",F="undefined",A=a.browser,r=parseFloat(a.browser.version),Y=A.mozilla,h=A.msie,K=A.opera,t=A.safari,Z=false,N=g(),aa=N.document,X=N.history,T=N.location,u=setInterval,m=setTimeout,o=decodeURI,ac=encodeURI,ab=navigator.userAgent,w,I,k,H=aa.title,D=X.length,s=false,R=false,L=true,J=true,z=[],y={},P=O(),j={},B={history:true,strict:true};if(h){r=parseFloat(ab.substr(ab.indexOf("MSIE")+4))}Z=(Y&&r>=1)||(h&&r>=6)||(K&&r>=9.5)||(t&&r>=312);if(Z){for(var V=1;V<D;V++){z.push("")}z.push(O());if(h&&T.hash!=O()){T.hash="#"+q(O(),true)}if(K){history.navigationMode="compatible"}ad(document);var b=k.indexOf("?");if(k&&b>-1){var l,d=k.substr(b+1).split("&");for(var V=0,Q;Q=d[V];V++){l=Q.split("=");if(/^(history|strict)$/.test(l[0])){B[l[0]]=(isNaN(l[1])?/^(true|yes)$/i.test(l[1]):(parseInt(l[1])!=0))}if(/^tracker$/.test(l[0])){B[l[0]]=l[1]}}}a(M)}else{if((!Z&&T.href.indexOf("#")!=-1)||(t&&r<418&&T.href.indexOf("#")!=-1&&T.search!="")){aa.open();aa.write('<html><head><meta http-equiv="refresh" content="0;url='+T.href.substr(0,T.href.indexOf("#"))+'" /></head></html>');aa.close()}else{v()}}a.each(("init,change").split(","),function(ae,p){j[p]=function(af,i){a(a.address).bind(p,i||af,i&&af);return this}});a.each(("baseURL,strict,history,tracker,title,value").split(","),function(ae,p){j[p]=function(i){if(typeof i!="undefined"){if(Z){G[p](i)}return a.address}else{return C[p]()}}});a.each(("path,pathNames,queryString,parameter,parameterNames").split(","),function(ae,p){j[p]=function(i){return C[p](i)}});return j})();a.fn.address=function(b){a(this).click(function(){var c=b?b.call(this):/address:/.test(a(this).attr("rel"))?a(this).attr("rel").split("address:")[1].split(" ")[0]:a(this).attr("href").replace(/^#/,"");a.address.value(c);return false})}}(jQuery));
/*
 * Autocomplete - jQuery plugin 1.0.2.1
 *
 * Copyright (c) 2007 Dylan Verheul, Dan G. Switzer, Anjesh Tuladhar, Jörn Zaefferer
 *
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 *
 * Revision: $Id: jquery.autocomplete.js 5747 2008-06-25 18:30:55Z joern.zaefferer $
 *
 */
//*
(function($){$.fn.extend({liveautocomplete:function(urlOrData,options){var isUrl=typeof urlOrData=="string";options=$.extend({},$.Autocompleter.defaults,{url:isUrl?urlOrData:null,data:isUrl?null:urlOrData,delay:isUrl?$.Autocompleter.defaults.delay:10,max:options&&!options.scroll?10:150},options);options.highlight=options.highlight||function(value){return value;};options.formatMatch=options.formatMatch||options.formatItem;return this.each(function(){new $.Autocompleter(this,options);});},result:function(handler){return this.bind("result",handler);},search:function(handler){return this.trigger("search",[handler]);},flushCache:function(){return this.trigger("flushCache");},setOptions:function(options){return this.trigger("setOptions",[options]);},unautocomplete:function(){return this.trigger("unautocomplete");}});$.Autocompleter=function(input,options){var KEY={UP:38,DOWN:40,DEL:46,TAB:9,RETURN:13,ESC:27,COMMA:188,PAGEUP:33,PAGEDOWN:34,BACKSPACE:8};var $input=$(input).attr("autocomplete","off").addClass(options.inputClass);var timeout;var previousValue="";var cache=$.Autocompleter.Cache(options);var hasFocus=0;var lastKeyPressCode;var config={mouseDownOnSelect:false};var select=$.Autocompleter.Select(options,input,selectCurrent,config);var blockSubmit;$.browser.opera&&$(input.form).bind("submit.autocomplete",function(){if(blockSubmit){blockSubmit=false;return false;}});$input.bind(($.browser.opera?"keypress":"keydown")+".autocomplete",function(event){lastKeyPressCode=event.keyCode;switch(event.keyCode){case KEY.UP:event.preventDefault();if(select.visible()){select.prev();}else{onChange(0,true);}break;case KEY.DOWN:event.preventDefault();if(select.visible()){select.next();}else{onChange(0,true);}break;case KEY.PAGEUP:event.preventDefault();if(select.visible()){select.pageUp();}else{onChange(0,true);}break;case KEY.PAGEDOWN:event.preventDefault();if(select.visible()){select.pageDown();}else{onChange(0,true);}break;case options.multiple&&$.trim(options.multipleSeparator)==","&&KEY.COMMA:case KEY.TAB:case KEY.RETURN:if(selectCurrent()){event.preventDefault();blockSubmit=true;return false;}break;case KEY.ESC:select.hide();break;default:clearTimeout(timeout);timeout=setTimeout(onChange,options.delay);break;}}).focus(function(){hasFocus++;}).blur(function(){hasFocus=0;if(!config.mouseDownOnSelect){hideResults();}}).click(function(){if(hasFocus++>0&&!select.visible()){onChange(0,true);}}).bind("search",function(){var fn=(arguments.length>1)?arguments[1]:null;function findValueCallback(q,data){var result;if(data&&data.length){for(var i=0;i<data.length;i++){if(data[i].result.toLowerCase()==q.toLowerCase()){result=data[i];break;}}}if(typeof fn=="function")fn(result);else $input.trigger("result",result&&[result.data,result.value]);}$.each(trimWords($input.val()),function(i,value){request(value,findValueCallback,findValueCallback);});}).bind("flushCache",function(){cache.flush();}).bind("setOptions",function(){$.extend(options,arguments[1]);if("data"in arguments[1])cache.populate();}).bind("unautocomplete",function(){select.unbind();$input.unbind();$(input.form).unbind(".autocomplete");});function selectCurrent(){var selected=select.selected();if(!selected)return false;var v=selected.result;previousValue=v;if(options.multiple){var words=trimWords($input.val());if(words.length>1){v=words.slice(0,words.length-1).join(options.multipleSeparator)+options.multipleSeparator+v;}v+=options.multipleSeparator;}$input.val(v);hideResultsNow();$input.trigger("result",[selected.data,selected.value]);return true;}function onChange(crap,skipPrevCheck){if(lastKeyPressCode==KEY.DEL){select.hide();return;}var currentValue=$input.val();if(!skipPrevCheck&&currentValue==previousValue)return;previousValue=currentValue;currentValue=lastWord(currentValue);if(currentValue.length>=options.minChars){$input.addClass(options.loadingClass);if(!options.matchCase)currentValue=currentValue.toLowerCase();request(currentValue,receiveData,hideResultsNow);}else{stopLoading();select.hide();}};function trimWords(value){if(!value){return[""];}var words=value.split(options.multipleSeparator);var result=[];$.each(words,function(i,value){if($.trim(value))result[i]=$.trim(value);});return result;}function lastWord(value){if(!options.multiple)return value;var words=trimWords(value);return words[words.length-1];}function autoFill(q,sValue){if(options.autoFill&&(lastWord($input.val()).toLowerCase()==q.toLowerCase())&&lastKeyPressCode!=KEY.BACKSPACE){$input.val($input.val()+sValue.substring(lastWord(previousValue).length));$.Autocompleter.Selection(input,previousValue.length,previousValue.length+sValue.length);}};function hideResults(){clearTimeout(timeout);timeout=setTimeout(hideResultsNow,200);};function hideResultsNow(){var wasVisible=select.visible();select.hide();clearTimeout(timeout);stopLoading();if(options.mustMatch){$input.search(function(result){if(!result){if(options.multiple){var words=trimWords($input.val()).slice(0,-1);$input.val(words.join(options.multipleSeparator)+(words.length?options.multipleSeparator:""));}else
$input.val("");}});}if(wasVisible)$.Autocompleter.Selection(input,input.value.length,input.value.length);};function receiveData(q,data){if(data&&data.length&&hasFocus){stopLoading();select.display(data,q);autoFill(q,data[0].value);select.show();}else{hideResultsNow();}};function request(term,success,failure){if(!options.matchCase)term=term.toLowerCase();var data=cache.load(term);if(data&&data.length){success(term,data);}else if((typeof options.url=="string")&&(options.url.length>0)){var extraParams={timestamp:+new Date()};$.each(options.extraParams,function(key,param){extraParams[key]=typeof param=="function"?param():param;});$.ajax({mode:"abort",port:"autocomplete"+input.name,dataType:options.dataType,url:options.url,data:$.extend({q:lastWord(term),limit:options.max},extraParams),success:function(data){var parsed=options.parse&&options.parse(data)||parse(data);cache.add(term,parsed);success(term,parsed);}});}else{select.emptyList();failure(term);}};function parse(data){var parsed=[];var rows=data.split("\n");for(var i=0;i<rows.length;i++){var row=$.trim(rows[i]);if(row){row=row.split("|");parsed[parsed.length]={data:row,value:row[0],result:options.formatResult&&options.formatResult(row,row[0])||row[0]};}}return parsed;};function stopLoading(){$input.removeClass(options.loadingClass);};};$.Autocompleter.defaults={inputClass:"ac_input",resultsClass:"ac_results",loadingClass:"ac_loading",minChars:1,delay:400,matchCase:false,matchSubset:true,matchContains:false,cacheLength:10,max:100,mustMatch:false,extraParams:{},selectFirst:true,formatItem:function(row){return row[0];},formatMatch:null,autoFill:false,width:0,multiple:false,multipleSeparator:", ",highlight:function(value,term){return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)("+term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi,"\\$1")+")(?![^<>]*>)(?![^&;]+;)","gi"),"<strong>$1</strong>");},scroll:true,scrollHeight:180};$.Autocompleter.Cache=function(options){var data={};var length=0;function matchSubset(s,sub){if(!options.matchCase)s=s.toLowerCase();var i=s.indexOf(sub);if(i==-1)return false;return i==0||options.matchContains;};function add(q,value){if(length>options.cacheLength){flush();}if(!data[q]){length++;}data[q]=value;}function populate(){if(!options.data)return false;var stMatchSets={},nullData=0;if(!options.url)options.cacheLength=1;stMatchSets[""]=[];for(var i=0,ol=options.data.length;i<ol;i++){var rawValue=options.data[i];rawValue=(typeof rawValue=="string")?[rawValue]:rawValue;var value=options.formatMatch(rawValue,i+1,options.data.length);if(value===false)continue;var firstChar=value.charAt(0).toLowerCase();if(!stMatchSets[firstChar])stMatchSets[firstChar]=[];var row={value:value,data:rawValue,result:options.formatResult&&options.formatResult(rawValue)||value};stMatchSets[firstChar].push(row);if(nullData++<options.max){stMatchSets[""].push(row);}};$.each(stMatchSets,function(i,value){options.cacheLength++;add(i,value);});}setTimeout(populate,25);function flush(){data={};length=0;}return{flush:flush,add:add,populate:populate,load:function(q){if(!options.cacheLength||!length)return null;if(!options.url&&options.matchContains){var csub=[];for(var k in data){if(k.length>0){var c=data[k];$.each(c,function(i,x){if(matchSubset(x.value,q)){csub.push(x);}});}}return csub;}else
if(data[q]){return data[q];}else
if(options.matchSubset){for(var i=q.length-1;i>=options.minChars;i--){var c=data[q.substr(0,i)];if(c){var csub=[];$.each(c,function(i,x){if(matchSubset(x.value,q)){csub[csub.length]=x;}});return csub;}}}return null;}};};$.Autocompleter.Select=function(options,input,select,config){var CLASSES={ACTIVE:"ac_over"};var listItems,active=-1,data,term="",needsInit=true,element,list;function init(){if(!needsInit)return;element=$("<div/>").hide().addClass(options.resultsClass).css("position","absolute").appendTo(document.body);list=$("<ul/>").appendTo(element).mouseover(function(event){if(target(event).nodeName&&target(event).nodeName.toUpperCase()=='LI'){active=$("li",list).removeClass(CLASSES.ACTIVE).index(target(event));$(target(event)).addClass(CLASSES.ACTIVE);}}).click(function(event){$(target(event)).addClass(CLASSES.ACTIVE);select();input.focus();return false;}).mousedown(function(){config.mouseDownOnSelect=true;}).mouseup(function(){config.mouseDownOnSelect=false;});if(options.width>0)element.css("width",options.width);needsInit=false;}function target(event){var element=event.target;while(element&&element.tagName!="LI")element=element.parentNode;if(!element)return[];return element;}function moveSelect(step){listItems.slice(active,active+1).removeClass(CLASSES.ACTIVE);movePosition(step);var activeItem=listItems.slice(active,active+1).addClass(CLASSES.ACTIVE);if(options.scroll){var offset=0;listItems.slice(0,active).each(function(){offset+=this.offsetHeight;});if((offset+activeItem[0].offsetHeight-list.scrollTop())>list[0].clientHeight){list.scrollTop(offset+activeItem[0].offsetHeight-list.innerHeight());}else if(offset<list.scrollTop()){list.scrollTop(offset);}}};function movePosition(step){active+=step;if(active<0){active=listItems.size()-1;}else if(active>=listItems.size()){active=0;}}function limitNumberOfItems(available){return options.max&&options.max<available?options.max:available;}function fillList(){list.empty();var max=limitNumberOfItems(data.length);for(var i=0;i<max;i++){if(!data[i])continue;var formatted=options.formatItem(data[i].data,i+1,max,data[i].value,term);if(formatted===false)continue;var li=$("<li/>").html(options.highlight(formatted,term)).addClass(i%2==0?"ac_even":"ac_odd").appendTo(list)[0];$.data(li,"ac_data",data[i]);}listItems=list.find("li");if(options.selectFirst){listItems.slice(0,1).addClass(CLASSES.ACTIVE);active=0;}if($.fn.bgiframe)list.bgiframe();}return{display:function(d,q){init();data=d;term=q;fillList();},next:function(){moveSelect(1);},prev:function(){moveSelect(-1);},pageUp:function(){if(active!=0&&active-8<0){moveSelect(-active);}else{moveSelect(-8);}},pageDown:function(){if(active!=listItems.size()-1&&active+8>listItems.size()){moveSelect(listItems.size()-1-active);}else{moveSelect(8);}},hide:function(){element&&element.hide();listItems&&listItems.removeClass(CLASSES.ACTIVE);active=-1;},visible:function(){return element&&element.is(":visible");},current:function(){return this.visible()&&(listItems.filter("."+CLASSES.ACTIVE)[0]||options.selectFirst&&listItems[0]);},show:function(){var offset=$(input).offset();element.css({width:typeof options.width=="string"||options.width>0?options.width:$(input).width(),top:offset.top+input.offsetHeight,left:offset.left}).show();if(options.scroll){list.scrollTop(0);list.css({maxHeight:options.scrollHeight,overflow:'auto'});if($.browser.msie&&typeof document.body.style.maxHeight==="undefined"){var listHeight=0;listItems.each(function(){listHeight+=this.offsetHeight;});var scrollbarsVisible=listHeight>options.scrollHeight;list.css('height',scrollbarsVisible?options.scrollHeight:listHeight);if(!scrollbarsVisible){listItems.width(list.width()-parseInt(listItems.css("padding-left"))-parseInt(listItems.css("padding-right")));}}}},selected:function(){var selected=listItems&&listItems.filter("."+CLASSES.ACTIVE).removeClass(CLASSES.ACTIVE);return selected&&selected.length&&$.data(selected[0],"ac_data");},emptyList:function(){list&&list.empty();},unbind:function(){element&&element.remove();}};};$.Autocompleter.Selection=function(field,start,end){if(field.createTextRange){var selRange=field.createTextRange();selRange.collapse(true);selRange.moveStart("character",start);selRange.moveEnd("character",end);selRange.select();}else if(field.setSelectionRange){field.setSelectionRange(start,end);}else{if(field.selectionStart){field.selectionStart=start;field.selectionEnd=end;}}field.focus();};})(jQuery);
//*
/*
 * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/
 *
 * Uses the built in easing capabilities added In jQuery 1.1
 * to offer multiple easing options
 *
 * TERMS OF USE - jQuery Easing
 * 
 * Open source under the BSD License. 
 * 
 * Copyright © 2008 George McGinley Smith
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 * 
 * Redistributions of source code must retain the above copyright notice, this list of 
 * conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice, this list 
 * of conditions and the following disclaimer in the documentation and/or other materials 
 * provided with the distribution.
 * 
 * Neither the name of the author nor the names of contributors may be used to endorse 
 * or promote products derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 *  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
 * OF THE POSSIBILITY OF SUCH DAMAGE. 
 *
*/

// t: current time, b: begInnIng value, c: change In value, d: duration
jQuery.easing['jswing'] = jQuery.easing['swing'];

jQuery.extend( jQuery.easing,
{
	def: 'easeOutQuad',
	swing: function (x, t, b, c, d) {
		//alert(jQuery.easing.default);
		return jQuery.easing[jQuery.easing.def](x, t, b, c, d);
	},
	easeInQuad: function (x, t, b, c, d) {
		return c*(t/=d)*t + b;
	},
	easeOutQuad: function (x, t, b, c, d) {
		return -c *(t/=d)*(t-2) + b;
	},
	easeInOutQuad: function (x, t, b, c, d) {
		if ((t/=d/2) < 1) return c/2*t*t + b;
		return -c/2 * ((--t)*(t-2) - 1) + b;
	},
	easeInCubic: function (x, t, b, c, d) {
		return c*(t/=d)*t*t + b;
	},
	easeOutCubic: function (x, t, b, c, d) {
		return c*((t=t/d-1)*t*t + 1) + b;
	},
	easeInOutCubic: function (x, t, b, c, d) {
		if ((t/=d/2) < 1) return c/2*t*t*t + b;
		return c/2*((t-=2)*t*t + 2) + b;
	},
	easeInQuart: function (x, t, b, c, d) {
		return c*(t/=d)*t*t*t + b;
	},
	easeOutQuart: function (x, t, b, c, d) {
		return -c * ((t=t/d-1)*t*t*t - 1) + b;
	},
	easeInOutQuart: function (x, t, b, c, d) {
		if ((t/=d/2) < 1) return c/2*t*t*t*t + b;
		return -c/2 * ((t-=2)*t*t*t - 2) + b;
	},
	easeInQuint: function (x, t, b, c, d) {
		return c*(t/=d)*t*t*t*t + b;
	},
	easeOutQuint: function (x, t, b, c, d) {
		return c*((t=t/d-1)*t*t*t*t + 1) + b;
	},
	easeInOutQuint: function (x, t, b, c, d) {
		if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
		return c/2*((t-=2)*t*t*t*t + 2) + b;
	},
	easeInSine: function (x, t, b, c, d) {
		return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
	},
	easeOutSine: function (x, t, b, c, d) {
		return c * Math.sin(t/d * (Math.PI/2)) + b;
	},
	easeInOutSine: function (x, t, b, c, d) {
		return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
	},
	easeInExpo: function (x, t, b, c, d) {
		return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
	},
	easeOutExpo: function (x, t, b, c, d) {
		return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
	},
	easeInOutExpo: function (x, t, b, c, d) {
		if (t==0) return b;
		if (t==d) return b+c;
		if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
		return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
	},
	easeInCirc: function (x, t, b, c, d) {
		return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;
	},
	easeOutCirc: function (x, t, b, c, d) {
		return c * Math.sqrt(1 - (t=t/d-1)*t) + b;
	},
	easeInOutCirc: function (x, t, b, c, d) {
		if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
		return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
	},
	easeInElastic: function (x, t, b, c, d) {
		var s=1.70158;var p=0;var a=c;
		if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
		if (a < Math.abs(c)) { a=c; var s=p/4; }
		else var s = p/(2*Math.PI) * Math.asin (c/a);
		return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
	},
	easeOutElastic: function (x, t, b, c, d) {
		var s=1.70158;var p=0;var a=c;
		if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
		if (a < Math.abs(c)) { a=c; var s=p/4; }
		else var s = p/(2*Math.PI) * Math.asin (c/a);
		return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
	},
	easeInOutElastic: function (x, t, b, c, d) {
		var s=1.70158;var p=0;var a=c;
		if (t==0) return b;  if ((t/=d/2)==2) return b+c;  if (!p) p=d*(.3*1.5);
		if (a < Math.abs(c)) { a=c; var s=p/4; }
		else var s = p/(2*Math.PI) * Math.asin (c/a);
		if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
		return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
	},
	easeInBack: function (x, t, b, c, d, s) {
		if (s == undefined) s = 1.70158;
		return c*(t/=d)*t*((s+1)*t - s) + b;
	},
	easeOutBack: function (x, t, b, c, d, s) {
		if (s == undefined) s = 1.70158;
		return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
	},
	easeInOutBack: function (x, t, b, c, d, s) {
		if (s == undefined) s = 1.70158; 
		if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
		return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
	},
	easeInBounce: function (x, t, b, c, d) {
		return c - jQuery.easing.easeOutBounce (x, d-t, 0, c, d) + b;
	},
	easeOutBounce: function (x, t, b, c, d) {
		if ((t/=d) < (1/2.75)) {
			return c*(7.5625*t*t) + b;
		} else if (t < (2/2.75)) {
			return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
		} else if (t < (2.5/2.75)) {
			return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
		} else {
			return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
		}
	},
	easeInOutBounce: function (x, t, b, c, d) {
		if (t < d/2) return jQuery.easing.easeInBounce (x, t*2, 0, c, d) * .5 + b;
		return jQuery.easing.easeOutBounce (x, t*2-d, 0, c, d) * .5 + c*.5 + b;
	}
});

/*
 *
 * TERMS OF USE - EASING EQUATIONS
 * 
 * Open source under the BSD License. 
 * 
 * Copyright © 2001 Robert Penner
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 * 
 * Redistributions of source code must retain the above copyright notice, this list of 
 * conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice, this list 
 * of conditions and the following disclaimer in the documentation and/or other materials 
 * provided with the distribution.
 * 
 * Neither the name of the author nor the names of contributors may be used to endorse 
 * or promote products derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 *  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
 * OF THE POSSIBILITY OF SUCH DAMAGE. 
 *
 */
/*
 * jQuery Easing Compatibility v1 - http://gsgd.co.uk/sandbox/jquery.easing.php
 *
 * Adds compatibility for applications that use the pre 1.2 easing names
 *
 * Copyright (c) 2007 George Smith
 * Licensed under the MIT License:
 *   http://www.opensource.org/licenses/mit-license.php
 */

jQuery.extend( jQuery.easing,
{
	easeIn: function (x, t, b, c, d) {
		return jQuery.easing.easeInQuad(x, t, b, c, d);
	},
	easeOut: function (x, t, b, c, d) {
		return jQuery.easing.easeOutQuad(x, t, b, c, d);
	},
	easeInOut: function (x, t, b, c, d) {
		return jQuery.easing.easeInOutQuad(x, t, b, c, d);
	},
	expoin: function(x, t, b, c, d) {
		return jQuery.easing.easeInExpo(x, t, b, c, d);
	},
	expoout: function(x, t, b, c, d) {
		return jQuery.easing.easeOutExpo(x, t, b, c, d);
	},
	expoinout: function(x, t, b, c, d) {
		return jQuery.easing.easeInOutExpo(x, t, b, c, d);
	},
	bouncein: function(x, t, b, c, d) {
		return jQuery.easing.easeInBounce(x, t, b, c, d);
	},
	bounceout: function(x, t, b, c, d) {
		return jQuery.easing.easeOutBounce(x, t, b, c, d);
	},
	bounceinout: function(x, t, b, c, d) {
		return jQuery.easing.easeInOutBounce(x, t, b, c, d);
	},
	elasin: function(x, t, b, c, d) {
		return jQuery.easing.easeInElastic(x, t, b, c, d);
	},
	elasout: function(x, t, b, c, d) {
		return jQuery.easing.easeOutElastic(x, t, b, c, d);
	},
	elasinout: function(x, t, b, c, d) {
		return jQuery.easing.easeInOutElastic(x, t, b, c, d);
	},
	backin: function(x, t, b, c, d) {
		return jQuery.easing.easeInBack(x, t, b, c, d);
	},
	backout: function(x, t, b, c, d) {
		return jQuery.easing.easeOutBack(x, t, b, c, d);
	},
	backinout: function(x, t, b, c, d) {
		return jQuery.easing.easeInOutBack(x, t, b, c, d);
	}
});
/*
 * jQuery UI Effects 1.8.2
 *
 * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 * http://docs.jquery.com/UI/Effects/
 */
jQuery.effects||function(f){function k(c){var a;if(c&&c.constructor==Array&&c.length==3)return c;if(a=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(c))return[parseInt(a[1],10),parseInt(a[2],10),parseInt(a[3],10)];if(a=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(c))return[parseFloat(a[1])*2.55,parseFloat(a[2])*2.55,parseFloat(a[3])*2.55];if(a=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(c))return[parseInt(a[1],
16),parseInt(a[2],16),parseInt(a[3],16)];if(a=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(c))return[parseInt(a[1]+a[1],16),parseInt(a[2]+a[2],16),parseInt(a[3]+a[3],16)];if(/rgba\(0, 0, 0, 0\)/.exec(c))return l.transparent;return l[f.trim(c).toLowerCase()]}function q(c,a){var b;do{b=f.curCSS(c,a);if(b!=""&&b!="transparent"||f.nodeName(c,"body"))break;a="backgroundColor"}while(c=c.parentNode);return k(b)}function m(){var c=document.defaultView?document.defaultView.getComputedStyle(this,null):this.currentStyle,
a={},b,d;if(c&&c.length&&c[0]&&c[c[0]])for(var e=c.length;e--;){b=c[e];if(typeof c[b]=="string"){d=b.replace(/\-(\w)/g,function(g,h){return h.toUpperCase()});a[d]=c[b]}}else for(b in c)if(typeof c[b]==="string")a[b]=c[b];return a}function n(c){var a,b;for(a in c){b=c[a];if(b==null||f.isFunction(b)||a in r||/scrollbar/.test(a)||!/color/i.test(a)&&isNaN(parseFloat(b)))delete c[a]}return c}function s(c,a){var b={_:0},d;for(d in a)if(c[d]!=a[d])b[d]=a[d];return b}function j(c,a,b,d){if(typeof c=="object"){d=
a;b=null;a=c;c=a.effect}if(f.isFunction(a)){d=a;b=null;a={}}if(f.isFunction(b)){d=b;b=null}if(typeof a=="number"||f.fx.speeds[a]){d=b;b=a;a={}}a=a||{};b=b||a.duration;b=f.fx.off?0:typeof b=="number"?b:f.fx.speeds[b]||f.fx.speeds._default;d=d||a.complete;return[c,a,b,d]}f.effects={};f.each(["backgroundColor","borderBottomColor","borderLeftColor","borderRightColor","borderTopColor","color","outlineColor"],function(c,a){f.fx.step[a]=function(b){if(!b.colorInit){b.start=q(b.elem,a);b.end=k(b.end);b.colorInit=
true}b.elem.style[a]="rgb("+Math.max(Math.min(parseInt(b.pos*(b.end[0]-b.start[0])+b.start[0],10),255),0)+","+Math.max(Math.min(parseInt(b.pos*(b.end[1]-b.start[1])+b.start[1],10),255),0)+","+Math.max(Math.min(parseInt(b.pos*(b.end[2]-b.start[2])+b.start[2],10),255),0)+")"}});var l={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,
183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,
165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0],transparent:[255,255,255]},o=["add","remove","toggle"],r={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};f.effects.animateClass=function(c,a,b,d){if(f.isFunction(b)){d=b;b=null}return this.each(function(){var e=f(this),g=e.attr("style")||" ",h=n(m.call(this)),p,t=e.attr("className");f.each(o,function(u,
i){c[i]&&e[i+"Class"](c[i])});p=n(m.call(this));e.attr("className",t);e.animate(s(h,p),a,b,function(){f.each(o,function(u,i){c[i]&&e[i+"Class"](c[i])});if(typeof e.attr("style")=="object"){e.attr("style").cssText="";e.attr("style").cssText=g}else e.attr("style",g);d&&d.apply(this,arguments)})})};f.fn.extend({_addClass:f.fn.addClass,addClass:function(c,a,b,d){return a?f.effects.animateClass.apply(this,[{add:c},a,b,d]):this._addClass(c)},_removeClass:f.fn.removeClass,removeClass:function(c,a,b,d){return a?
f.effects.animateClass.apply(this,[{remove:c},a,b,d]):this._removeClass(c)},_toggleClass:f.fn.toggleClass,toggleClass:function(c,a,b,d,e){return typeof a=="boolean"||a===undefined?b?f.effects.animateClass.apply(this,[a?{add:c}:{remove:c},b,d,e]):this._toggleClass(c,a):f.effects.animateClass.apply(this,[{toggle:c},a,b,d])},switchClass:function(c,a,b,d,e){return f.effects.animateClass.apply(this,[{add:a,remove:c},b,d,e])}});f.extend(f.effects,{version:"1.8.2",save:function(c,a){for(var b=0;b<a.length;b++)a[b]!==
null&&c.data("ec.storage."+a[b],c[0].style[a[b]])},restore:function(c,a){for(var b=0;b<a.length;b++)a[b]!==null&&c.css(a[b],c.data("ec.storage."+a[b]))},setMode:function(c,a){if(a=="toggle")a=c.is(":hidden")?"show":"hide";return a},getBaseline:function(c,a){var b;switch(c[0]){case "top":b=0;break;case "middle":b=0.5;break;case "bottom":b=1;break;default:b=c[0]/a.height}switch(c[1]){case "left":c=0;break;case "center":c=0.5;break;case "right":c=1;break;default:c=c[1]/a.width}return{x:c,y:b}},createWrapper:function(c){if(c.parent().is(".ui-effects-wrapper"))return c.parent();
var a={width:c.outerWidth(true),height:c.outerHeight(true),"float":c.css("float")},b=f("<div></div>").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0});c.wrap(b);b=c.parent();if(c.css("position")=="static"){b.css({position:"relative"});c.css({position:"relative"})}else{f.extend(a,{position:c.css("position"),zIndex:c.css("z-index")});f.each(["top","left","bottom","right"],function(d,e){a[e]=c.css(e);if(isNaN(parseInt(a[e],10)))a[e]="auto"});
c.css({position:"relative",top:0,left:0})}return b.css(a).show()},removeWrapper:function(c){if(c.parent().is(".ui-effects-wrapper"))return c.parent().replaceWith(c);return c},setTransition:function(c,a,b,d){d=d||{};f.each(a,function(e,g){unit=c.cssUnit(g);if(unit[0]>0)d[g]=unit[0]*b+unit[1]});return d}});f.fn.extend({effect:function(c){var a=j.apply(this,arguments);a={options:a[1],duration:a[2],callback:a[3]};var b=f.effects[c];return b&&!f.fx.off?b.call(this,a):this},_show:f.fn.show,show:function(c){if(!c||
typeof c=="number"||f.fx.speeds[c])return this._show.apply(this,arguments);else{var a=j.apply(this,arguments);a[1].mode="show";return this.effect.apply(this,a)}},_hide:f.fn.hide,hide:function(c){if(!c||typeof c=="number"||f.fx.speeds[c])return this._hide.apply(this,arguments);else{var a=j.apply(this,arguments);a[1].mode="hide";return this.effect.apply(this,a)}},__toggle:f.fn.toggle,toggle:function(c){if(!c||typeof c=="number"||f.fx.speeds[c]||typeof c=="boolean"||f.isFunction(c))return this.__toggle.apply(this,
arguments);else{var a=j.apply(this,arguments);a[1].mode="toggle";return this.effect.apply(this,a)}},cssUnit:function(c){var a=this.css(c),b=[];f.each(["em","px","%","pt"],function(d,e){if(a.indexOf(e)>0)b=[parseFloat(a),e]});return b}});f.easing.jswing=f.easing.swing;f.extend(f.easing,{def:"easeOutQuad",swing:function(c,a,b,d,e){return f.easing[f.easing.def](c,a,b,d,e)},easeInQuad:function(c,a,b,d,e){return d*(a/=e)*a+b},easeOutQuad:function(c,a,b,d,e){return-d*(a/=e)*(a-2)+b},easeInOutQuad:function(c,
a,b,d,e){if((a/=e/2)<1)return d/2*a*a+b;return-d/2*(--a*(a-2)-1)+b},easeInCubic:function(c,a,b,d,e){return d*(a/=e)*a*a+b},easeOutCubic:function(c,a,b,d,e){return d*((a=a/e-1)*a*a+1)+b},easeInOutCubic:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a+b;return d/2*((a-=2)*a*a+2)+b},easeInQuart:function(c,a,b,d,e){return d*(a/=e)*a*a*a+b},easeOutQuart:function(c,a,b,d,e){return-d*((a=a/e-1)*a*a*a-1)+b},easeInOutQuart:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a*a+b;return-d/2*((a-=2)*a*a*a-2)+
b},easeInQuint:function(c,a,b,d,e){return d*(a/=e)*a*a*a*a+b},easeOutQuint:function(c,a,b,d,e){return d*((a=a/e-1)*a*a*a*a+1)+b},easeInOutQuint:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a*a*a+b;return d/2*((a-=2)*a*a*a*a+2)+b},easeInSine:function(c,a,b,d,e){return-d*Math.cos(a/e*(Math.PI/2))+d+b},easeOutSine:function(c,a,b,d,e){return d*Math.sin(a/e*(Math.PI/2))+b},easeInOutSine:function(c,a,b,d,e){return-d/2*(Math.cos(Math.PI*a/e)-1)+b},easeInExpo:function(c,a,b,d,e){return a==0?b:d*Math.pow(2,
10*(a/e-1))+b},easeOutExpo:function(c,a,b,d,e){return a==e?b+d:d*(-Math.pow(2,-10*a/e)+1)+b},easeInOutExpo:function(c,a,b,d,e){if(a==0)return b;if(a==e)return b+d;if((a/=e/2)<1)return d/2*Math.pow(2,10*(a-1))+b;return d/2*(-Math.pow(2,-10*--a)+2)+b},easeInCirc:function(c,a,b,d,e){return-d*(Math.sqrt(1-(a/=e)*a)-1)+b},easeOutCirc:function(c,a,b,d,e){return d*Math.sqrt(1-(a=a/e-1)*a)+b},easeInOutCirc:function(c,a,b,d,e){if((a/=e/2)<1)return-d/2*(Math.sqrt(1-a*a)-1)+b;return d/2*(Math.sqrt(1-(a-=2)*
a)+1)+b},easeInElastic:function(c,a,b,d,e){c=1.70158;var g=0,h=d;if(a==0)return b;if((a/=e)==1)return b+d;g||(g=e*0.3);if(h<Math.abs(d)){h=d;c=g/4}else c=g/(2*Math.PI)*Math.asin(d/h);return-(h*Math.pow(2,10*(a-=1))*Math.sin((a*e-c)*2*Math.PI/g))+b},easeOutElastic:function(c,a,b,d,e){c=1.70158;var g=0,h=d;if(a==0)return b;if((a/=e)==1)return b+d;g||(g=e*0.3);if(h<Math.abs(d)){h=d;c=g/4}else c=g/(2*Math.PI)*Math.asin(d/h);return h*Math.pow(2,-10*a)*Math.sin((a*e-c)*2*Math.PI/g)+d+b},easeInOutElastic:function(c,
a,b,d,e){c=1.70158;var g=0,h=d;if(a==0)return b;if((a/=e/2)==2)return b+d;g||(g=e*0.3*1.5);if(h<Math.abs(d)){h=d;c=g/4}else c=g/(2*Math.PI)*Math.asin(d/h);if(a<1)return-0.5*h*Math.pow(2,10*(a-=1))*Math.sin((a*e-c)*2*Math.PI/g)+b;return h*Math.pow(2,-10*(a-=1))*Math.sin((a*e-c)*2*Math.PI/g)*0.5+d+b},easeInBack:function(c,a,b,d,e,g){if(g==undefined)g=1.70158;return d*(a/=e)*a*((g+1)*a-g)+b},easeOutBack:function(c,a,b,d,e,g){if(g==undefined)g=1.70158;return d*((a=a/e-1)*a*((g+1)*a+g)+1)+b},easeInOutBack:function(c,
a,b,d,e,g){if(g==undefined)g=1.70158;if((a/=e/2)<1)return d/2*a*a*(((g*=1.525)+1)*a-g)+b;return d/2*((a-=2)*a*(((g*=1.525)+1)*a+g)+2)+b},easeInBounce:function(c,a,b,d,e){return d-f.easing.easeOutBounce(c,e-a,0,d,e)+b},easeOutBounce:function(c,a,b,d,e){return(a/=e)<1/2.75?d*7.5625*a*a+b:a<2/2.75?d*(7.5625*(a-=1.5/2.75)*a+0.75)+b:a<2.5/2.75?d*(7.5625*(a-=2.25/2.75)*a+0.9375)+b:d*(7.5625*(a-=2.625/2.75)*a+0.984375)+b},easeInOutBounce:function(c,a,b,d,e){if(a<e/2)return f.easing.easeInBounce(c,a*2,0,
d,e)*0.5+b;return f.easing.easeOutBounce(c,a*2-e,0,d,e)*0.5+d*0.5+b}})}(jQuery);

/*
 * jQuery UI Effects Slide 1.8.2
 *
 * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 * http://docs.jquery.com/UI/Effects/Slide
 *
 * Depends:
 *	jquery.effects.core.js
 */
(function(c){c.effects.slide=function(d){return this.queue(function(){var a=c(this),h=["position","top","left"],e=c.effects.setMode(a,d.options.mode||"show"),b=d.options.direction||"left";c.effects.save(a,h);a.show();c.effects.createWrapper(a).css({overflow:"hidden"});var f=b=="up"||b=="down"?"top":"left";b=b=="up"||b=="left"?"pos":"neg";var g=d.options.distance||(f=="top"?a.outerHeight({margin:true}):a.outerWidth({margin:true}));if(e=="show")a.css(f,b=="pos"?-g:g);var i={};i[f]=(e=="show"?b=="pos"?
"+=":"-=":b=="pos"?"-=":"+=")+g;a.animate(i,{queue:false,duration:d.duration,easing:d.options.easing,complete:function(){e=="hide"&&a.hide();c.effects.restore(a,h);c.effects.removeWrapper(a);d.callback&&d.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery);

/*!
 * jQuery Form Plugin
 * version: 2.43 (12-MAR-2010)
 * @requires jQuery v1.3.2 or later
 *
 * Examples and documentation at: http://malsup.com/jquery/form/
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 */
;(function($) {

/*
	Usage Note:
	-----------
	Do not use both ajaxSubmit and ajaxForm on the same form.  These
	functions are intended to be exclusive.  Use ajaxSubmit if you want
	to bind your own submit handler to the form.  For example,

	$(document).ready(function() {
		$('#myForm').bind('submit', function() {
			$(this).ajaxSubmit({
				target: '#output'
			});
			return false; // <-- important!
		});
	});

	Use ajaxForm when you want the plugin to manage all the event binding
	for you.  For example,

	$(document).ready(function() {
		$('#myForm').ajaxForm({
			target: '#output'
		});
	});

	When using ajaxForm, the ajaxSubmit function will be invoked for you
	at the appropriate time.
*/

/**
 * ajaxSubmit() provides a mechanism for immediately submitting
 * an HTML form using AJAX.
 */
$.fn.ajaxSubmit = function(options) {
	// fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
	if (!this.length) {
		log('ajaxSubmit: skipping submit process - no element selected');
		return this;
	}

	if (typeof options == 'function')
		options = { success: options };

	var url = $.trim(this.attr('action'));
	if (url) {
		// clean url (don't include hash vaue)
		url = (url.match(/^([^#]+)/)||[])[1];
   	}
   	url = url || window.location.href || '';

	options = $.extend({
		url:  url,
		type: this.attr('method') || 'GET',
		iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank'
	}, options || {});

	// hook for manipulating the form data before it is extracted;
	// convenient for use with rich editors like tinyMCE or FCKEditor
	var veto = {};
	this.trigger('form-pre-serialize', [this, options, veto]);
	if (veto.veto) {
		log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
		return this;
	}

	// provide opportunity to alter form data before it is serialized
	if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
		log('ajaxSubmit: submit aborted via beforeSerialize callback');
		return this;
	}

	var a = this.formToArray(options.semantic);
	if (options.data) {
		options.extraData = options.data;
		for (var n in options.data) {
		  if(options.data[n] instanceof Array) {
			for (var k in options.data[n])
			  a.push( { name: n, value: options.data[n][k] } );
		  }
		  else
			 a.push( { name: n, value: options.data[n] } );
		}
	}

	// give pre-submit callback an opportunity to abort the submit
	if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
		log('ajaxSubmit: submit aborted via beforeSubmit callback');
		return this;
	}

	// fire vetoable 'validate' event
	this.trigger('form-submit-validate', [a, this, options, veto]);
	if (veto.veto) {
		log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
		return this;
	}

	var q = $.param(a);

	if (options.type.toUpperCase() == 'GET') {
		options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
		options.data = null;  // data is null for 'get'
	}
	else
		options.data = q; // data is the query string for 'post'

	var $form = this, callbacks = [];
	if (options.resetForm) callbacks.push(function() { $form.resetForm(); });
	if (options.clearForm) callbacks.push(function() { $form.clearForm(); });

	// perform a load on the target only if dataType is not provided
	if (!options.dataType && options.target) {
		var oldSuccess = options.success || function(){};
		callbacks.push(function(data) {
			var fn = options.replaceTarget ? 'replaceWith' : 'html';
			$(options.target)[fn](data).each(oldSuccess, arguments);
		});
	}
	else if (options.success)
		callbacks.push(options.success);

	options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg
		for (var i=0, max=callbacks.length; i < max; i++)
			callbacks[i].apply(options, [data, status, xhr || $form, $form]);
	};

	// are there files to upload?
	var files = $('input:file', this).fieldValue();
	var found = false;
	for (var j=0; j < files.length; j++)
		if (files[j])
			found = true;

	var multipart = false;
//	var mp = 'multipart/form-data';
//	multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);

	// options.iframe allows user to force iframe mode
	// 06-NOV-09: now defaulting to iframe mode if file input is detected
   if ((files.length && options.iframe !== false) || options.iframe || found || multipart) {
	   // hack to fix Safari hang (thanks to Tim Molendijk for this)
	   // see:  http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
	   if (options.closeKeepAlive)
		   $.get(options.closeKeepAlive, fileUpload);
	   else
		   fileUpload();
	   }
   else
	   $.ajax(options);

	// fire 'notify' event
	this.trigger('form-submit-notify', [this, options]);
	return this;


	// private function for handling file uploads (hat tip to YAHOO!)
	function fileUpload() {
		var form = $form[0];

		if ($(':input[name=submit]', form).length) {
			alert('Error: Form elements must not be named "submit".');
			return;
		}

		var opts = $.extend({}, $.ajaxSettings, options);
		var s = $.extend(true, {}, $.extend(true, {}, $.ajaxSettings), opts);

		var id = 'jqFormIO' + (new Date().getTime());
		var $io = $('<iframe id="' + id + '" name="' + id + '" src="'+ opts.iframeSrc +'" onload="(jQuery(this).data(\'form-plugin-onload\'))()" />');
		var io = $io[0];

		$io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });

		var xhr = { // mock object
			aborted: 0,
			responseText: null,
			responseXML: null,
			status: 0,
			statusText: 'n/a',
			getAllResponseHeaders: function() {},
			getResponseHeader: function() {},
			setRequestHeader: function() {},
			abort: function() {
				this.aborted = 1;
				$io.attr('src', opts.iframeSrc); // abort op in progress
			}
		};

		var g = opts.global;
		// trigger ajax global events so that activity/block indicators work like normal
		if (g && ! $.active++) $.event.trigger("ajaxStart");
		if (g) $.event.trigger("ajaxSend", [xhr, opts]);

		if (s.beforeSend && s.beforeSend(xhr, s) === false) {
			s.global && $.active--;
			return;
		}
		if (xhr.aborted)
			return;

		var cbInvoked = false;
		var timedOut = 0;

		// add submitting element to data if we know it
		var sub = form.clk;
		if (sub) {
			var n = sub.name;
			if (n && !sub.disabled) {
				opts.extraData = opts.extraData || {};
				opts.extraData[n] = sub.value;
				if (sub.type == "image") {
					opts.extraData[n+'.x'] = form.clk_x;
					opts.extraData[n+'.y'] = form.clk_y;
				}
			}
		}

		// take a breath so that pending repaints get some cpu time before the upload starts
		function doSubmit() {
			// make sure form attrs are set
			var t = $form.attr('target'), a = $form.attr('action');

			// update form attrs in IE friendly way
			form.setAttribute('target',id);
			if (form.getAttribute('method') != 'POST')
				form.setAttribute('method', 'POST');
			if (form.getAttribute('action') != opts.url)
				form.setAttribute('action', opts.url);

			// ie borks in some cases when setting encoding
			if (! opts.skipEncodingOverride) {
				$form.attr({
					encoding: 'multipart/form-data',
					enctype:  'multipart/form-data'
				});
			}

			// support timout
			if (opts.timeout)
				setTimeout(function() { timedOut = true; cb(); }, opts.timeout);

			// add "extra" data to form if provided in options
			var extraInputs = [];
			try {
				if (opts.extraData)
					for (var n in opts.extraData)
						extraInputs.push(
							$('<input type="hidden" name="'+n+'" value="'+opts.extraData[n]+'" />')
								.appendTo(form)[0]);

				// add iframe to doc and submit the form
				$io.appendTo('body');
				$io.data('form-plugin-onload', cb);
				form.submit();
			}
			finally {
				// reset attrs and remove "extra" input elements
				form.setAttribute('action',a);
				t ? form.setAttribute('target', t) : $form.removeAttr('target');
				$(extraInputs).remove();
			}
		};

		if (opts.forceSync)
			doSubmit();
		else
			setTimeout(doSubmit, 10); // this lets dom updates render
	
		var domCheckCount = 100;

		function cb() {
			if (cbInvoked) 
				return;

			var ok = true;
			try {
				if (timedOut) throw 'timeout';
				// extract the server response from the iframe
				var data, doc;

				doc = io.contentWindow ? io.contentWindow.document : io.contentDocument ? io.contentDocument : io.document;
				
				var isXml = opts.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
				log('isXml='+isXml);
				if (!isXml && (doc.body == null || doc.body.innerHTML == '')) {
				 	if (--domCheckCount) {
						// in some browsers (Opera) the iframe DOM is not always traversable when
						// the onload callback fires, so we loop a bit to accommodate
				 		log('requeing onLoad callback, DOM not available');
						setTimeout(cb, 250);
						return;
					}
					log('Could not access iframe DOM after 100 tries.');
					return;
				}

				log('response detected');
				cbInvoked = true;
				xhr.responseText = doc.body ? doc.body.innerHTML : null;
				xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
				xhr.getResponseHeader = function(header){
					var headers = {'content-type': opts.dataType};
					return headers[header];
				};

				if (opts.dataType == 'json' || opts.dataType == 'script') {
					// see if user embedded response in textarea
					var ta = doc.getElementsByTagName('textarea')[0];
					if (ta)
						xhr.responseText = ta.value;
					else {
						// account for browsers injecting pre around json response
						var pre = doc.getElementsByTagName('pre')[0];
						if (pre)
							xhr.responseText = pre.innerHTML;
					}			  
				}
				else if (opts.dataType == 'xml' && !xhr.responseXML && xhr.responseText != null) {
					xhr.responseXML = toXml(xhr.responseText);
				}
				data = $.httpData(xhr, opts.dataType);
			}
			catch(e){
				log('error caught:',e);
				ok = false;
				xhr.error = e;
				$.handleError(opts, xhr, 'error', e);
			}

			// ordering of these callbacks/triggers is odd, but that's how $.ajax does it
			if (ok) {
				opts.success(data, 'success');
				if (g) $.event.trigger("ajaxSuccess", [xhr, opts]);
			}
			if (g) $.event.trigger("ajaxComplete", [xhr, opts]);
			if (g && ! --$.active) $.event.trigger("ajaxStop");
			if (opts.complete) opts.complete(xhr, ok ? 'success' : 'error');

			// clean up
			setTimeout(function() {
				$io.removeData('form-plugin-onload');
				$io.remove();
				xhr.responseXML = null;
			}, 100);
		};

		function toXml(s, doc) {
			if (window.ActiveXObject) {
				doc = new ActiveXObject('Microsoft.XMLDOM');
				doc.async = 'false';
				doc.loadXML(s);
			}
			else
				doc = (new DOMParser()).parseFromString(s, 'text/xml');
			return (doc && doc.documentElement && doc.documentElement.tagName != 'parsererror') ? doc : null;
		};
	};
};

/**
 * ajaxForm() provides a mechanism for fully automating form submission.
 *
 * The advantages of using this method instead of ajaxSubmit() are:
 *
 * 1: This method will include coordinates for <input type="image" /> elements (if the element
 *	is used to submit the form).
 * 2. This method will include the submit element's name/value data (for the element that was
 *	used to submit the form).
 * 3. This method binds the submit() method to the form for you.
 *
 * The options argument for ajaxForm works exactly as it does for ajaxSubmit.  ajaxForm merely
 * passes the options argument along after properly binding events for submit elements and
 * the form itself.
 */
$.fn.ajaxForm = function(options) {
	return this.ajaxFormUnbind().bind('submit.form-plugin', function(e) {
		e.preventDefault();
		$(this).ajaxSubmit(options);
	}).bind('click.form-plugin', function(e) {
		var target = e.target;
		var $el = $(target);
		if (!($el.is(":submit,input:image"))) {
			// is this a child element of the submit el?  (ex: a span within a button)
			var t = $el.closest(':submit');
			if (t.length == 0)
				return;
			target = t[0];
		}
		var form = this;
		form.clk = target;
		if (target.type == 'image') {
			if (e.offsetX != undefined) {
				form.clk_x = e.offsetX;
				form.clk_y = e.offsetY;
			} else if (typeof $.fn.offset == 'function') { // try to use dimensions plugin
				var offset = $el.offset();
				form.clk_x = e.pageX - offset.left;
				form.clk_y = e.pageY - offset.top;
			} else {
				form.clk_x = e.pageX - target.offsetLeft;
				form.clk_y = e.pageY - target.offsetTop;
			}
		}
		// clear form vars
		setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100);
	});
};

// ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
$.fn.ajaxFormUnbind = function() {
	return this.unbind('submit.form-plugin click.form-plugin');
};

/**
 * formToArray() gathers form element data into an array of objects that can
 * be passed to any of the following ajax functions: $.get, $.post, or load.
 * Each object in the array has both a 'name' and 'value' property.  An example of
 * an array for a simple login form might be:
 *
 * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
 *
 * It is this array that is passed to pre-submit callback functions provided to the
 * ajaxSubmit() and ajaxForm() methods.
 */
$.fn.formToArray = function(semantic) {
	var a = [];
	if (this.length == 0) return a;

	var form = this[0];
	var els = semantic ? form.getElementsByTagName('*') : form.elements;
	if (!els) return a;
	for(var i=0, max=els.length; i < max; i++) {
		var el = els[i];
		var n = el.name;
		if (!n) continue;

		if (semantic && form.clk && el.type == "image") {
			// handle image inputs on the fly when semantic == true
			if(!el.disabled && form.clk == el) {
				a.push({name: n, value: $(el).val()});
				a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
			}
			continue;
		}

		var v = $.fieldValue(el, true);
		if (v && v.constructor == Array) {
			for(var j=0, jmax=v.length; j < jmax; j++)
				a.push({name: n, value: v[j]});
		}
		else if (v !== null && typeof v != 'undefined')
			a.push({name: n, value: v});
	}

	if (!semantic && form.clk) {
		// input type=='image' are not found in elements array! handle it here
		var $input = $(form.clk), input = $input[0], n = input.name;
		if (n && !input.disabled && input.type == 'image') {
			a.push({name: n, value: $input.val()});
			a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
		}
	}
	return a;
};

/**
 * Serializes form data into a 'submittable' string. This method will return a string
 * in the format: name1=value1&amp;name2=value2
 */
$.fn.formSerialize = function(semantic) {
	//hand off to jQuery.param for proper encoding
	return $.param(this.formToArray(semantic));
};

/**
 * Serializes all field elements in the jQuery object into a query string.
 * This method will return a string in the format: name1=value1&amp;name2=value2
 */
$.fn.fieldSerialize = function(successful) {
	var a = [];
	this.each(function() {
		var n = this.name;
		if (!n) return;
		var v = $.fieldValue(this, successful);
		if (v && v.constructor == Array) {
			for (var i=0,max=v.length; i < max; i++)
				a.push({name: n, value: v[i]});
		}
		else if (v !== null && typeof v != 'undefined')
			a.push({name: this.name, value: v});
	});
	//hand off to jQuery.param for proper encoding
	return $.param(a);
};

/**
 * Returns the value(s) of the element in the matched set.  For example, consider the following form:
 *
 *  <form><fieldset>
 *	  <input name="A" type="text" />
 *	  <input name="A" type="text" />
 *	  <input name="B" type="checkbox" value="B1" />
 *	  <input name="B" type="checkbox" value="B2"/>
 *	  <input name="C" type="radio" value="C1" />
 *	  <input name="C" type="radio" value="C2" />
 *  </fieldset></form>
 *
 *  var v = $(':text').fieldValue();
 *  // if no values are entered into the text inputs
 *  v == ['','']
 *  // if values entered into the text inputs are 'foo' and 'bar'
 *  v == ['foo','bar']
 *
 *  var v = $(':checkbox').fieldValue();
 *  // if neither checkbox is checked
 *  v === undefined
 *  // if both checkboxes are checked
 *  v == ['B1', 'B2']
 *
 *  var v = $(':radio').fieldValue();
 *  // if neither radio is checked
 *  v === undefined
 *  // if first radio is checked
 *  v == ['C1']
 *
 * The successful argument controls whether or not the field element must be 'successful'
 * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
 * The default value of the successful argument is true.  If this value is false the value(s)
 * for each element is returned.
 *
 * Note: This method *always* returns an array.  If no valid value can be determined the
 *	   array will be empty, otherwise it will contain one or more values.
 */
$.fn.fieldValue = function(successful) {
	for (var val=[], i=0, max=this.length; i < max; i++) {
		var el = this[i];
		var v = $.fieldValue(el, successful);
		if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length))
			continue;
		v.constructor == Array ? $.merge(val, v) : val.push(v);
	}
	return val;
};

/**
 * Returns the value of the field element.
 */
$.fieldValue = function(el, successful) {
	var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
	if (typeof successful == 'undefined') successful = true;

	if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
		(t == 'checkbox' || t == 'radio') && !el.checked ||
		(t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
		tag == 'select' && el.selectedIndex == -1))
			return null;

	if (tag == 'select') {
		var index = el.selectedIndex;
		if (index < 0) return null;
		var a = [], ops = el.options;
		var one = (t == 'select-one');
		var max = (one ? index+1 : ops.length);
		for(var i=(one ? index : 0); i < max; i++) {
			var op = ops[i];
			if (op.selected) {
				var v = op.value;
				if (!v) // extra pain for IE...
					v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
				if (one) return v;
				a.push(v);
			}
		}
		return a;
	}
	return el.value;
};

/**
 * Clears the form data.  Takes the following actions on the form's input fields:
 *  - input text fields will have their 'value' property set to the empty string
 *  - select elements will have their 'selectedIndex' property set to -1
 *  - checkbox and radio inputs will have their 'checked' property set to false
 *  - inputs of type submit, button, reset, and hidden will *not* be effected
 *  - button elements will *not* be effected
 */
$.fn.clearForm = function() {
	return this.each(function() {
		$('input,select,textarea', this).clearFields();
	});
};

/**
 * Clears the selected form elements.
 */
$.fn.clearFields = $.fn.clearInputs = function() {
	return this.each(function() {
		var t = this.type, tag = this.tagName.toLowerCase();
		if (t == 'text' || t == 'password' || tag == 'textarea')
			this.value = '';
		else if (t == 'checkbox' || t == 'radio')
			this.checked = false;
		else if (tag == 'select')
			this.selectedIndex = -1;
	});
};

/**
 * Resets the form data.  Causes all form elements to be reset to their original value.
 */
$.fn.resetForm = function() {
	return this.each(function() {
		// guard against an input with the name of 'reset'
		// note that IE reports the reset function as an 'object'
		if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType))
			this.reset();
	});
};

/**
 * Enables or disables any matching elements.
 */
$.fn.enable = function(b) {
	if (b == undefined) b = true;
	return this.each(function() {
		this.disabled = !b;
	});
};

/**
 * Checks/unchecks any matching checkboxes or radio buttons and
 * selects/deselects and matching option elements.
 */
$.fn.selected = function(select) {
	if (select == undefined) select = true;
	return this.each(function() {
		var t = this.type;
		if (t == 'checkbox' || t == 'radio')
			this.checked = select;
		else if (this.tagName.toLowerCase() == 'option') {
			var $sel = $(this).parent('select');
			if (select && $sel[0] && $sel[0].type == 'select-one') {
				// deselect all other options
				$sel.find('option').selected(false);
			}
			this.selected = select;
		}
	});
};

// helper fn for console logging
// set $.fn.ajaxSubmit.debug to true to enable debug logging
function log() {
	if ($.fn.ajaxSubmit.debug) {
		var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
		if (window.console && window.console.log)
			window.console.log(msg);
		else if (window.opera && window.opera.postError)
			window.opera.postError(msg);
	}
};

})(jQuery);

(function($){var abs=Math.abs,max=Math.max,min=Math.min,round=Math.round;function div(){return $('<div/>')}$.imgAreaSelect=function(img,options){var $img=$(img),imgLoaded,$box=div(),$area=div(),$border=div().add(div()).add(div()).add(div()),$outer=div().add(div()).add(div()).add(div()),$handles=$([]),$areaOpera,left,top,imgOfs,imgWidth,imgHeight,$parent,parOfs,zIndex=0,position='absolute',startX,startY,scaleX,scaleY,resizeMargin=10,resize,minWidth,minHeight,maxWidth,maxHeight,aspectRatio,shown,x1,y1,x2,y2,selection={x1:0,y1:0,x2:0,y2:0,width:0,height:0},docElem=document.documentElement,$p,d,i,o,w,h,adjusted;function viewX(x){return x+imgOfs.left-parOfs.left}function viewY(y){return y+imgOfs.top-parOfs.top}function selX(x){return x-imgOfs.left+parOfs.left}function selY(y){return y-imgOfs.top+parOfs.top}function evX(event){return event.pageX-parOfs.left}function evY(event){return event.pageY-parOfs.top}function getSelection(noScale){var sx=noScale||scaleX,sy=noScale||scaleY;return{x1:round(selection.x1*sx),y1:round(selection.y1*sy),x2:round(selection.x2*sx),y2:round(selection.y2*sy),width:round(selection.x2*sx)-round(selection.x1*sx),height:round(selection.y2*sy)-round(selection.y1*sy)}}function setSelection(x1,y1,x2,y2,noScale){var sx=noScale||scaleX,sy=noScale||scaleY;selection={x1:round(x1/sx),y1:round(y1/sy),x2:round(x2/sx),y2:round(y2/sy)};selection.width=selection.x2-selection.x1;selection.height=selection.y2-selection.y1}function adjust(){if(!$img.width())return;imgOfs={left:round($img.offset().left),top:round($img.offset().top)};imgWidth=$img.width();imgHeight=$img.height();minWidth=options.minWidth||0;minHeight=options.minHeight||0;maxWidth=min(options.maxWidth||1<<24,imgWidth);maxHeight=min(options.maxHeight||1<<24,imgHeight);if($().jquery=='1.3.2'&&position=='fixed'&&!docElem['getBoundingClientRect']){imgOfs.top+=max(document.body.scrollTop,docElem.scrollTop);imgOfs.left+=max(document.body.scrollLeft,docElem.scrollLeft)}parOfs=$.inArray($parent.css('position'),['absolute','relative'])+1?{left:round($parent.offset().left)-$parent.scrollLeft(),top:round($parent.offset().top)-$parent.scrollTop()}:position=='fixed'?{left:$(document).scrollLeft(),top:$(document).scrollTop()}:{left:0,top:0};left=viewX(0);top=viewY(0);if(selection.x2>imgWidth||selection.y2>imgHeight)doResize()}function update(resetKeyPress){if(!shown)return;$box.css({left:viewX(selection.x1),top:viewY(selection.y1)}).add($area).width(w=selection.width).height(h=selection.height);$area.add($border).add($handles).css({left:0,top:0});$border.width(max(w-$border.outerWidth()+$border.innerWidth(),0)).height(max(h-$border.outerHeight()+$border.innerHeight(),0));$($outer[0]).css({left:left,top:top,width:selection.x1,height:imgHeight});$($outer[1]).css({left:left+selection.x1,top:top,width:w,height:selection.y1});$($outer[2]).css({left:left+selection.x2,top:top,width:imgWidth-selection.x2,height:imgHeight});$($outer[3]).css({left:left+selection.x1,top:top+selection.y2,width:w,height:imgHeight-selection.y2});w-=$handles.outerWidth();h-=$handles.outerHeight();switch($handles.length){case 8:$($handles[4]).css({left:w/2});$($handles[5]).css({left:w,top:h/2});$($handles[6]).css({left:w/2,top:h});$($handles[7]).css({top:h/2});case 4:$handles.slice(1,3).css({left:w});$handles.slice(2,4).css({top:h})}if(resetKeyPress!==false){if($.imgAreaSelect.keyPress!=docKeyPress)$(document).unbind($.imgAreaSelect.keyPress,$.imgAreaSelect.onKeyPress);if(options.keys)$(document)[$.imgAreaSelect.keyPress]($.imgAreaSelect.onKeyPress=docKeyPress)}if($.browser.msie&&$border.outerWidth()-$border.innerWidth()==2){$border.css('margin',0);setTimeout(function(){$border.css('margin','auto')},0)}}function doUpdate(resetKeyPress){adjust();update(resetKeyPress);x1=viewX(selection.x1);y1=viewY(selection.y1);x2=viewX(selection.x2);y2=viewY(selection.y2)}function hide($elem,fn){options.fadeSpeed?$elem.fadeOut(options.fadeSpeed,fn):$elem.hide()}function areaMouseMove(event){var x=selX(evX(event))-selection.x1,y=selY(evY(event))-selection.y1;if(!adjusted){adjust();adjusted=true;$box.one('mouseout',function(){adjusted=false})}resize='';if(options.resizable){if(y<=resizeMargin)resize='n';else if(y>=selection.height-resizeMargin)resize='s';if(x<=resizeMargin)resize+='w';else if(x>=selection.width-resizeMargin)resize+='e'}$box.css('cursor',resize?resize+'-resize':options.movable?'move':'');if($areaOpera)$areaOpera.toggle()}function docMouseUp(event){$('body').css('cursor','');if(options.autoHide||selection.width*selection.height==0)hide($box.add($outer),function(){$(this).hide()});options.onSelectEnd(img,getSelection());$(document).unbind('mousemove',selectingMouseMove);$box.mousemove(areaMouseMove)}function areaMouseDown(event){if(event.which!=1)return false;adjust();if(resize){$('body').css('cursor',resize+'-resize');x1=viewX(selection[/w/.test(resize)?'x2':'x1']);y1=viewY(selection[/n/.test(resize)?'y2':'y1']);$(document).mousemove(selectingMouseMove).one('mouseup',docMouseUp);$box.unbind('mousemove',areaMouseMove)}else if(options.movable){startX=left+selection.x1-evX(event);startY=top+selection.y1-evY(event);$box.unbind('mousemove',areaMouseMove);$(document).mousemove(movingMouseMove).one('mouseup',function(){options.onSelectEnd(img,getSelection());$(document).unbind('mousemove',movingMouseMove);$box.mousemove(areaMouseMove)})}else $img.mousedown(event);return false}function fixAspectRatio(xFirst){if(aspectRatio)if(xFirst){x2=max(left,min(left+imgWidth,x1+abs(y2-y1)*aspectRatio*(x2>x1||-1)));y2=round(max(top,min(top+imgHeight,y1+abs(x2-x1)/aspectRatio*(y2>y1||-1))));x2=round(x2)}else{y2=max(top,min(top+imgHeight,y1+abs(x2-x1)/aspectRatio*(y2>y1||-1)));x2=round(max(left,min(left+imgWidth,x1+abs(y2-y1)*aspectRatio*(x2>x1||-1))));y2=round(y2)}}function doResize(){x1=min(x1,left+imgWidth);y1=min(y1,top+imgHeight);if(abs(x2-x1)<minWidth){x2=x1-minWidth*(x2<x1||-1);if(x2<left)x1=left+minWidth;else if(x2>left+imgWidth)x1=left+imgWidth-minWidth}if(abs(y2-y1)<minHeight){y2=y1-minHeight*(y2<y1||-1);if(y2<top)y1=top+minHeight;else if(y2>top+imgHeight)y1=top+imgHeight-minHeight}x2=max(left,min(x2,left+imgWidth));y2=max(top,min(y2,top+imgHeight));fixAspectRatio(abs(x2-x1)<abs(y2-y1)*aspectRatio);if(abs(x2-x1)>maxWidth){x2=x1-maxWidth*(x2<x1||-1);fixAspectRatio()}if(abs(y2-y1)>maxHeight){y2=y1-maxHeight*(y2<y1||-1);fixAspectRatio(true)}selection={x1:selX(min(x1,x2)),x2:selX(max(x1,x2)),y1:selY(min(y1,y2)),y2:selY(max(y1,y2)),width:abs(x2-x1),height:abs(y2-y1)};update();options.onSelectChange(img,getSelection())}function selectingMouseMove(event){x2=resize==''||/w|e/.test(resize)||aspectRatio?evX(event):viewX(selection.x2);y2=resize==''||/n|s/.test(resize)||aspectRatio?evY(event):viewY(selection.y2);doResize();return false}function doMove(newX1,newY1){x2=(x1=newX1)+selection.width;y2=(y1=newY1)+selection.height;$.extend(selection,{x1:selX(x1),y1:selY(y1),x2:selX(x2),y2:selY(y2)});update();options.onSelectChange(img,getSelection())}function movingMouseMove(event){x1=max(left,min(startX+evX(event),left+imgWidth-selection.width));y1=max(top,min(startY+evY(event),top+imgHeight-selection.height));doMove(x1,y1);event.preventDefault();return false}function startSelection(){adjust();x2=x1;y2=y1;doResize();resize='';if($outer.is(':not(:visible)'))$box.add($outer).hide().fadeIn(options.fadeSpeed||0);shown=true;$(document).unbind('mouseup',cancelSelection).mousemove(selectingMouseMove).one('mouseup',docMouseUp);$box.unbind('mousemove',areaMouseMove);options.onSelectStart(img,getSelection())}function cancelSelection(){$(document).unbind('mousemove',startSelection);hide($box.add($outer));selection={x1:selX(x1),y1:selY(y1),x2:selX(x1),y2:selY(y1),width:0,height:0};options.onSelectChange(img,getSelection());options.onSelectEnd(img,getSelection())}function imgMouseDown(event){if(event.which!=1||$outer.is(':animated'))return false;adjust();startX=x1=evX(event);startY=y1=evY(event);$(document).one('mousemove',startSelection).one('mouseup',cancelSelection);return false}function windowResize(){doUpdate(false)}function imgLoad(){imgLoaded=true;setOptions(options=$.extend({classPrefix:'imgareaselect',movable:true,resizable:true,parent:'body',onInit:function(){},onSelectStart:function(){},onSelectChange:function(){},onSelectEnd:function(){}},options));$box.add($outer).css({visibility:''});if(options.show){shown=true;adjust();update();$box.add($outer).hide().fadeIn(options.fadeSpeed||0)}setTimeout(function(){options.onInit(img,getSelection())},0)}var docKeyPress=function(event){var k=options.keys,d,t,key=event.keyCode;d=!isNaN(k.alt)&&(event.altKey||event.originalEvent.altKey)?k.alt:!isNaN(k.ctrl)&&event.ctrlKey?k.ctrl:!isNaN(k.shift)&&event.shiftKey?k.shift:!isNaN(k.arrows)?k.arrows:10;if(k.arrows=='resize'||(k.shift=='resize'&&event.shiftKey)||(k.ctrl=='resize'&&event.ctrlKey)||(k.alt=='resize'&&(event.altKey||event.originalEvent.altKey))){switch(key){case 37:d=-d;case 39:t=max(x1,x2);x1=min(x1,x2);x2=max(t+d,x1);fixAspectRatio();break;case 38:d=-d;case 40:t=max(y1,y2);y1=min(y1,y2);y2=max(t+d,y1);fixAspectRatio(true);break;default:return}doResize()}else{x1=min(x1,x2);y1=min(y1,y2);switch(key){case 37:doMove(max(x1-d,left),y1);break;case 38:doMove(x1,max(y1-d,top));break;case 39:doMove(x1+min(d,imgWidth-selX(x2)),y1);break;case 40:doMove(x1,y1+min(d,imgHeight-selY(y2)));break;default:return}}return false};function styleOptions($elem,props){for(option in props)if(options[option]!==undefined)$elem.css(props[option],options[option])}function setOptions(newOptions){if(newOptions.parent)($parent=$(newOptions.parent)).append($box.add($outer));$.extend(options,newOptions);adjust();if(newOptions.handles!=null){$handles.remove();$handles=$([]);i=newOptions.handles?newOptions.handles=='corners'?4:8:0;while(i--)$handles=$handles.add(div());$handles.addClass(options.classPrefix+'-handle').css({position:'absolute',fontSize:0,zIndex:zIndex+1||1});if(!parseInt($handles.css('width')))$handles.width(5).height(5);if(o=options.borderWidth)$handles.css({borderWidth:o,borderStyle:'solid'});styleOptions($handles,{borderColor1:'border-color',borderColor2:'background-color',borderOpacity:'opacity'})}scaleX=options.imageWidth/imgWidth||1;scaleY=options.imageHeight/imgHeight||1;if(newOptions.x1!=null){setSelection(newOptions.x1,newOptions.y1,newOptions.x2,newOptions.y2);newOptions.show=!newOptions.hide}if(newOptions.keys)options.keys=$.extend({shift:1,ctrl:'resize'},newOptions.keys);$outer.addClass(options.classPrefix+'-outer');$area.addClass(options.classPrefix+'-selection');for(i=0;i++<4;)$($border[i-1]).addClass(options.classPrefix+'-border'+i);styleOptions($area,{selectionColor:'background-color',selectionOpacity:'opacity'});styleOptions($border,{borderOpacity:'opacity',borderWidth:'border-width'});styleOptions($outer,{outerColor:'background-color',outerOpacity:'opacity'});if(o=options.borderColor1)$($border[0]).css({borderStyle:'solid',borderColor:o});if(o=options.borderColor2)$($border[1]).css({borderStyle:'dashed',borderColor:o});$box.append($area.add($border).add($handles).add($areaOpera));if($.browser.msie){if(o=$outer.css('filter').match(/opacity=([0-9]+)/))$outer.css('opacity',o[1]/100);if(o=$border.css('filter').match(/opacity=([0-9]+)/))$border.css('opacity',o[1]/100)}if(newOptions.hide)hide($box.add($outer));else if(newOptions.show&&imgLoaded){shown=true;$box.add($outer).fadeIn(options.fadeSpeed||0);doUpdate()}aspectRatio=(d=(options.aspectRatio||'').split(/:/))[0]/d[1];if(options.disable||options.enable===false){$box.unbind('mousemove',areaMouseMove).unbind('mousedown',areaMouseDown);$img.add($outer).unbind('mousedown',imgMouseDown);$(window).unbind('resize',windowResize)}else if(options.enable||options.disable===false){if(options.resizable||options.movable)$box.mousemove(areaMouseMove).mousedown(areaMouseDown);if(!options.persistent)$img.add($outer).mousedown(imgMouseDown);$(window).resize(windowResize)}options.enable=options.disable=undefined}this.remove=function(){$img.unbind('mousedown',imgMouseDown);$box.add($outer).remove()};this.getOptions=function(){return options};this.setOptions=setOptions;this.getSelection=getSelection;this.setSelection=setSelection;this.update=doUpdate;$p=$img;while($p.length){zIndex=max(zIndex,!isNaN($p.css('z-index'))?$p.css('z-index'):zIndex);if($p.css('position')=='fixed')position='fixed';$p=$p.parent(':not(body)')}zIndex=options.zIndex||zIndex;if($.browser.msie)$img.attr('unselectable','on');$.imgAreaSelect.keyPress=$.browser.msie||$.browser.safari?'keydown':'keypress';if($.browser.opera)$areaOpera=div().css({width:'100%',height:'100%',position:'absolute',zIndex:zIndex+2||2});$box.add($outer).css({visibility:'hidden',position:position,overflow:'hidden',zIndex:zIndex||'0'});$box.css({zIndex:zIndex+2||2});$area.add($border).css({position:'absolute',fontSize:0});img.complete||img.readyState=='complete'||!$img.is('img')?imgLoad():$img.one('load',imgLoad)};$.fn.imgAreaSelect=function(options){options=options||{};this.each(function(){if($(this).data('imgAreaSelect')){if(options.remove){$(this).data('imgAreaSelect').remove();$(this).removeData('imgAreaSelect')}else $(this).data('imgAreaSelect').setOptions(options)}else if(!options.remove){if(options.enable===undefined&&options.disable===undefined)options.enable=true;$(this).data('imgAreaSelect',new $.imgAreaSelect(this,options))}});if(options.instance)return $(this).data('imgAreaSelect');return this}})(jQuery);

(function($){$.fn.editable=function(target,options){if('disable'==target){$(this).data('disabled.editable',true);return;}
if('enable'==target){$(this).data('disabled.editable',false);return;}
if('destroy'==target){$(this).unbind($(this).data('event.editable')).removeData('disabled.editable').removeData('event.editable');return;}
var settings=$.extend({},$.fn.editable.defaults,{target:target},options);var plugin=$.editable.types[settings.type].plugin||function(){};var submit=$.editable.types[settings.type].submit||function(){};var buttons=$.editable.types[settings.type].buttons||$.editable.types['defaults'].buttons;var content=$.editable.types[settings.type].content||$.editable.types['defaults'].content;var element=$.editable.types[settings.type].element||$.editable.types['defaults'].element;var reset=$.editable.types[settings.type].reset||$.editable.types['defaults'].reset;var callback=settings.callback||function(){};var onedit=settings.onedit||function(){};var onsubmit=settings.onsubmit||function(){};var onreset=settings.onreset||function(){};var onerror=settings.onerror||reset;if(settings.tooltip){$(this).attr('title',settings.tooltip);}
settings.autowidth='auto'==settings.width;settings.autoheight='auto'==settings.height;return this.each(function(){var self=this;var savedwidth=$(self).width();var savedheight=$(self).height();$(this).data('event.editable',settings.event);if(!$.trim($(this).html())){$(this).html(settings.placeholder);}
$(this).bind(settings.event,function(e){if(true===$(this).data('disabled.editable')){return;}
if(self.editing){return;}
if(false===onedit.apply(this,[settings,self])){return;}
e.preventDefault();e.stopPropagation();if(settings.tooltip){$(self).removeAttr('title');}
if(0==$(self).width()){settings.width=savedwidth;settings.height=savedheight;}else{if(settings.width!='none'){settings.width=settings.autowidth?$(self).width():settings.width;}
if(settings.height!='none'){settings.height=settings.autoheight?$(self).height():settings.height;}}
if($(this).html().toLowerCase().replace(/(;|")/g,'')==settings.placeholder.toLowerCase().replace(/(;|")/g,'')){$(this).html('');}
self.editing=true;self.revert=$(self).html();$(self).html('');var form=$('<form />');if(settings.cssclass){if('inherit'==settings.cssclass){form.attr('class',$(self).attr('class'));}else{form.attr('class',settings.cssclass);}}
if(settings.style){if('inherit'==settings.style){form.attr('style',$(self).attr('style'));form.css('display',$(self).css('display'));}else{form.attr('style',settings.style);}}
var input=element.apply(form,[settings,self]);var input_content;if(settings.loadurl){var t=setTimeout(function(){input.disabled=true;content.apply(form,[settings.loadtext,settings,self]);},100);var loaddata={};loaddata[settings.id]=self.id;if($.isFunction(settings.loaddata)){$.extend(loaddata,settings.loaddata.apply(self,[self.revert,settings]));}else{$.extend(loaddata,settings.loaddata);}
$.ajax({type:settings.loadtype,url:settings.loadurl,data:loaddata,async:false,success:function(result){window.clearTimeout(t);input_content=result;input.disabled=false;}});}else if(settings.data){input_content=settings.data;if($.isFunction(settings.data)){input_content=settings.data.apply(self,[self.revert,settings]);}}else{input_content=self.revert;}
content.apply(form,[input_content,settings,self]);input.attr('name',settings.name);buttons.apply(form,[settings,self]);$(self).append(form);plugin.apply(form,[settings,self]);$(':input:visible:enabled:first',form).focus();if(settings.select){input.select();}
input.keydown(function(e){if(e.keyCode==27){e.preventDefault();reset.apply(form,[settings,self]);}});var t;if('cancel'==settings.onblur){input.blur(function(e){t=setTimeout(function(){reset.apply(form,[settings,self]);},500);});}else if('submit'==settings.onblur){input.blur(function(e){t=setTimeout(function(){form.submit();},200);});}else if($.isFunction(settings.onblur)){input.blur(function(e){settings.onblur.apply(self,[input.val(),settings]);});}else{input.blur(function(e){});}
form.submit(function(e){if(t){clearTimeout(t);}
e.preventDefault();if(false!==onsubmit.apply(form,[settings,self])){if(false!==submit.apply(form,[settings,self])){if($.isFunction(settings.target)){var str=settings.target.apply(self,[input.val(),settings]);$(self).html(str);self.editing=false;callback.apply(self,[self.innerHTML,settings]);if(!$.trim($(self).html())){$(self).html(settings.placeholder);}}else{var submitdata={};submitdata[settings.name]=input.val();submitdata[settings.id]=self.id;if($.isFunction(settings.submitdata)){$.extend(submitdata,settings.submitdata.apply(self,[self.revert,settings]));}else{$.extend(submitdata,settings.submitdata);}
if('PUT'==settings.method){submitdata['_method']='put';}
$(self).html(settings.indicator);var ajaxoptions={type:'POST',data:submitdata,dataType:'html',url:settings.target,success:function(result,status){if(ajaxoptions.dataType=='html'){$(self).html(result);}
self.editing=false;callback.apply(self,[result,settings]);if(!$.trim($(self).html())){$(self).html(settings.placeholder);}},error:function(xhr,status,error){onerror.apply(form,[settings,self,xhr]);}};$.extend(ajaxoptions,settings.ajaxoptions);$.ajax(ajaxoptions);}}}
$(self).attr('title',settings.tooltip);return false;});});this.reset=function(form){if(this.editing){if(false!==onreset.apply(form,[settings,self])){$(self).html(self.revert);self.editing=false;if(!$.trim($(self).html())){$(self).html(settings.placeholder);}
if(settings.tooltip){$(self).attr('title',settings.tooltip);}}}};});};$.editable={types:{defaults:{element:function(settings,original){var input=$('<input type="hidden"></input>');$(this).append(input);return(input);},content:function(string,settings,original){$(':input:first',this).val(string);},reset:function(settings,original){original.reset(this);},buttons:function(settings,original){var form=this;if(settings.submit){if(settings.submit.match(/>$/)){var submit=$(settings.submit).click(function(){if(submit.attr("type")!="submit"){form.submit();}});}else{var submit=$('<button type="submit" />');submit.html(settings.submit);}
$(this).append(submit);}
if(settings.cancel){if(settings.cancel.match(/>$/)){var cancel=$(settings.cancel);}else{var cancel=$('<button type="cancel" />');cancel.html(settings.cancel);}
$(this).append(cancel);$(cancel).click(function(event){if($.isFunction($.editable.types[settings.type].reset)){var reset=$.editable.types[settings.type].reset;}else{var reset=$.editable.types['defaults'].reset;}
reset.apply(form,[settings,original]);return false;});}}},text:{element:function(settings,original){var input=$('<input />');if(settings.width!='none'){input.width(settings.width);}
if(settings.height!='none'){input.height(settings.height);}
input.attr('autocomplete','off');$(this).append(input);return(input);}},textarea:{element:function(settings,original){var textarea=$('<textarea />');if(settings.rows){textarea.attr('rows',settings.rows);}else if(settings.height!="none"){textarea.height(settings.height);}
if(settings.cols){textarea.attr('cols',settings.cols);}else if(settings.width!="none"){textarea.width(settings.width);}
$(this).append(textarea);return(textarea);}},select:{element:function(settings,original){var select=$('<select />');$(this).append(select);return(select);},content:function(data,settings,original){if(String==data.constructor){eval('var json = '+data);}else{var json=data;}
for(var key in json){if(!json.hasOwnProperty(key)){continue;}
if('selected'==key){continue;}
var option=$('<option />').val(key).append(json[key]);$('select',this).append(option);}
$('select',this).children().each(function(){if($(this).val()==json['selected']||$(this).text()==$.trim(original.revert)){$(this).attr('selected','selected');}});}}},addInputType:function(name,input){$.editable.types[name]=input;}};$.fn.editable.defaults={name:'value',id:'id',type:'text',width:'auto',height:'auto',event:'click.editable',onblur:'cancel',loadtype:'GET',loadtext:'Loading...',placeholder:'Click to edit',loaddata:{},submitdata:{},ajaxoptions:{}};})(jQuery);
/*
 * multiselect2side jQuery plugin
 *
 * Copyright (c) 2010 Giovanni Casassa (senamion.com - senamion.it)
 *
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 * http://www.senamion.com
 *
 */

(function($) {
jQuery.fn.multiselect2side = function(o) {
  
    o = $.extend({
      selectedPosition: 'right',
      moveOptions: true,
      //TODO:TRANSLATE
      labelTop: 'Top',
      labelBottom: 'Bottom',
      labelUp: 'Up',
      labelDown: 'Down',
      labelSort: 'Sort',
      labelsx: 'Available',
      labeldx: 'Selected',
      maxSelected: -1
    }, o);


    return this.each(function() {
      var el = $(this);

      var originalName = $(this).attr("name");
      if (originalName.indexOf('[') != -1)
        originalName = originalName.substring(0, originalName.indexOf('['));

      var nameDx = originalName + "ms2side__dx";
      var nameSx = originalName + "ms2side__sx";
      var size = $(this).attr("size");
      // SIZE MIN
      if (size < 6) {
        $(this).attr("size", "6");
        size = 6;
      }

      // UP AND DOWN
      //TODO:TRANSLATE
      var divUpDown =
					"<div class='ms2side__updown'>" +
						"<p class='SelSort' title='Trier'>" + o.labelSort + "</p>" +
						"<p class='MoveTop' title='En haut'>" + o.labelTop + "</p>" +
						"<p class='MoveUp' title='Vers le haut'>" + o.labelUp + "</p>" +
						"<p class='MoveDown' title='Vers le bas'>" + o.labelDown + "</p>" +
						"<p class='MoveBottom' title='En bas'>" + o.labelBottom + "</p>" +
					"</div>";

      // CREATE NEW ELEMENT (AND HIDE IT) AFTER THE HIDDED ORGINAL SELECT
      //TODO:TRANSLATE
      var htmlToAdd =
				"<div class='ms2side__div'>" +
						((o.selectedPosition != 'right' && o.moveOptions) ? divUpDown : "") +
					"<div class='ms2side__select'>" +
						(o.labelsx ? ("<div class='ms2side__header'>" + o.labelsx + "</div>") : "") +
						"<select title='" + o.labelsx + "' name='" + nameSx + "' id='" + nameSx + "' size='" + size + "' multiple='multiple' ></select>" +
					"</div>" +
					"<div class='ms2side__options'>" +
						((o.selectedPosition == 'right')
						?
						("<p class='AddOne' title='Ajouter'>&rsaquo;</p>" +
						"<p class='AddAll' title='Tout ajouter'>&raquo;</p>" +
						"<p class='RemoveOne' title='Supprimer'>&lsaquo;</p>" +
						"<p class='RemoveAll' title='Tout supprimer'>&laquo;</p>")
						:
						("<p class='AddOne' title='Ajouter'>&lsaquo;</p>" +
						"<p class='AddAll' title='Tout ajouter'>&laquo;</p>" +
						"<p class='RemoveOne' title='Supprimer'>&rsaquo;</p>" +
						"<p class='RemoveAll' title='Tout supprimer'>&raquo;</p>")
						) +
					"</div>" +
					"<div class='ms2side__select'>" +
						(o.labeldx ? ("<div class='ms2side__header'>" + o.labeldx + "</div>") : "") +
						"<select title='" + o.labeldx + "' name='" + nameDx + "' id='" + nameDx + "' size='" + size + "' multiple='multiple' ></select>" +
					"</div>" +
					((o.selectedPosition == 'right' && o.moveOptions) ? divUpDown : "") +
				"</div>";
      $(this).after(htmlToAdd).hide();

      // ELEMENTS
      var allSel = $(this).next().find("select");
      var leftSel = (o.selectedPosition == 'right') ? allSel.eq(0) : allSel.eq(1);
      var rightSel = (o.selectedPosition == 'right') ? allSel.eq(1) : allSel.eq(0);
      // HEIGHT DIV
      var heightDiv = $(".ms2side__select").eq(0).height();

      // CENTER MOVE OPTIONS AND UPDOWN OPTIONS
      $(this).next().find('.ms2side__options, .ms2side__updown').each(function() {
        var top = ((heightDiv / 2) - ($(this).height() / 2));
        if (top > 0)
          $(this).css('padding-top', top + 'px');
      })

      // MOVE SELECTED OPTION TO RIGHT, NOT SELECTED TO LEFT
      $(this).find("option:selected").clone().appendTo(rightSel);
      $(this).find("option:not(:selected)").clone().appendTo(leftSel);

      // SELECT FIRST LEFT ITEM
      if (!($.browser.msie && $.browser.version == '6.0'))
        leftSel.find("option").eq(0).attr("selected", true);

      // ON CHANGE REFRESH ALL BUTTON STATUS
      allSel.change(function() {
        var div = $(this).parent().parent();
        var selectSx = leftSel.children();
        var selectDx = rightSel.children();
        var selectedSx = leftSel.find("option:selected");
        var selectedDx = rightSel.find("option:selected");

        if (selectedSx.size() == 0 ||
						(o.maxSelected >= 0 && (selectedSx.size() + selectDx.size()) > o.maxSelected))
          div.find(".AddOne").addClass('ms2side__hide');
        else
          div.find(".AddOne").removeClass('ms2side__hide');

        // FIRST HIDE ALL
        div.find(".RemoveOne, .MoveUp, .MoveDown, .MoveTop, .MoveBottom, .SelSort").addClass('ms2side__hide');
        if (selectDx.size() > 1)
          div.find(".SelSort").removeClass('ms2side__hide');
        if (selectedDx.size() > 0) {
          div.find(".RemoveOne").removeClass('ms2side__hide');
          // ALL SELECTED - NO MOVE
          if (selectedDx.size() < selectDx.size()) {	// FOR NOW (JOE) && selectedDx.size() == 1
            if (selectedDx.val() != selectDx.val())	// FIRST OPTION, NO UP AND TOP BUTTON
              div.find(".MoveUp, .MoveTop").removeClass('ms2side__hide');
            if (selectedDx.last().val() != selectDx.last().val())	// LAST OPTION, NO DOWN AND BOTTOM BUTTON
              div.find(".MoveDown, .MoveBottom").removeClass('ms2side__hide');
          }
        }

        if (selectSx.size() == 0 ||
						(o.maxSelected >= 0 && selectSx.size() >= o.maxSelected))
          div.find(".AddAll").addClass('ms2side__hide');
        else
          div.find(".AddAll").removeClass('ms2side__hide');

        if (selectDx.size() == 0)
          div.find(".RemoveAll").addClass('ms2side__hide');
        else
          div.find(".RemoveAll").removeClass('ms2side__hide');
      });

      // DOUBLE CLICK ON LEFT SELECT OPTION
      leftSel.dblclick(function() {
        $(this).find("option:selected").each(function(i, selected) {

          if (o.maxSelected < 0 || rightSel.children().size() < o.maxSelected) {
            $(this).remove().appendTo(rightSel);
            el.find("[value=" + $(selected).val() + "]").attr("selected", true).remove().appendTo(el);
          }
        });
        $(this).trigger('change');
      });

      // DOUBLE CLICK ON RIGHT SELECT OPTION
      rightSel.dblclick(function() {
        $(this).find("option:selected").each(function(i, selected) {
          $(this).remove().appendTo(leftSel);
          el.find("[value=" + $(selected).val() + "]").attr("selected", false).remove().appendTo(el);
        });
        $(this).trigger('change');
      });

      // CLICK ON OPTION
      $(this).next().find('.ms2side__options').children().click(function() {
        if (!$(this).hasClass("ms2side__hide")) {
          if ($(this).hasClass("AddOne")) {
            leftSel.find("option:selected").each(function(i, selected) {
              $(this).remove().appendTo(rightSel);
              el.find("[value=" + $(selected).val() + "]").attr("selected", true).remove().appendTo(el);
            });
          }
          else if ($(this).hasClass("AddAll")) {	// ALL SELECTED
            leftSel.children().appendTo(rightSel);
            leftSel.children().remove();
            el.find('option').attr("selected", true);
            // el.children().attr("selected", true); -- PROBLEM WITH OPTGROUP
          }
          else if ($(this).hasClass("RemoveOne")) {
            rightSel.find("option:selected").each(function(i, selected) {
              $(this).remove().appendTo(leftSel);
              el.find("[value=" + $(selected).val() + "]").attr("selected", false).remove().appendTo(el);
            });
          }
          else if ($(this).hasClass("RemoveAll")) {	// ALL REMOVED
            rightSel.children().appendTo(leftSel);
            rightSel.children().remove();
            el.find('option').attr("selected", false);
            //el.children().attr("selected", false); -- PROBLEM WITH OPTGROUP
          }
        }

        leftSel.trigger('change');
      });

      // CLICK ON UP - DOWN
      $(this).next().find('.ms2side__updown').children().click(function() {
        var selectedDx = rightSel.find("option:selected");
        var selectDx = rightSel.find("option");

        if (!$(this).hasClass("ms2side__hide")) {
          if ($(this).hasClass("SelSort")) {
            // SORT SELECTED ELEMENT
            selectDx.sort(function(a, b) {
              var compA = $(a).text().toUpperCase();
              var compB = $(b).text().toUpperCase();
              return (compA < compB) ? -1 : (compA > compB) ? 1 : 0;
            })
            // FIRST REMOVE FROM ORIGINAL SELECT
            el.find("option:selected").remove();
            // AFTER ADD ON ORIGINAL AND RIGHT SELECT
            selectDx.each(function() {
              rightSel.append($(this).clone().attr("selected", true));
              el.append($(this).attr("selected", true));
            });
          }
          else if ($(this).hasClass("MoveUp")) {
            var prev = selectedDx.first().prev();
            var hPrev = el.find("[value=" + prev.val() + "]");

            selectedDx.each(function() {
              $(this).insertBefore(prev);
              el.find("[value=" + $(this).val() + "]").insertBefore(hPrev); // HIDDEN SELECT
            });
          }
          else if ($(this).hasClass("MoveDown")) {
            var next = selectedDx.last().next();
            var hNext = el.find("[value=" + next.val() + "]");

            selectedDx.each(function() {
              $(this).insertAfter(next);
              el.find("[value=" + $(this).val() + "]").insertAfter(hNext); // HIDDEN SELECT
            });
          }
          else if ($(this).hasClass("MoveTop")) {
            var first = selectDx.first();
            var hFirst = el.find("[value=" + first.val() + "]");

            selectedDx.each(function() {
              $(this).insertBefore(first);
              el.find("[value=" + $(this).val() + "]").insertBefore(hFirst); // HIDDEN SELECT
            });
          }
          else if ($(this).hasClass("MoveBottom")) {
            var last = selectDx.last();
            var hLast = el.find("[value=" + last.val() + "]");

            selectedDx.each(function() {
              last = $(this).insertAfter(last); // WITH last = SAME POSITION OF SELECTED OPTION AFTER MOVE
              hLast = el.find("[value=" + $(this).val() + "]").insertAfter(hLast); // HIDDEN SELECT
            });
          }
        }

        leftSel.trigger('change');
      });

      // HOVER ON OPTION
      $(this).next().find('.ms2side__options, .ms2side__updown').children().hover(
				function() {
				  $(this).addClass('ms2side_hover');
				},
				function() {
				  $(this).removeClass('ms2side_hover');
				}
			);

      // UPDATE BUTTON ON START
      leftSel.trigger('change');
      // SHOW WHEN ALL READY
      $(this).next().show();
    });
  };
})(jQuery);
$.postJSON = function (url, data, callback) {
	$.post(url, data, callback, "json");
};

/**
* SearchHighlight plugin for jQuery
* 
* Thanks to Scott Yang <http://scott.yang.id.au/>
* for the original idea and some code
*    
* @author Renato Formato <renatoformato@virgilio.it> 
*  
* @version 0.33
*
*  Options
*  - exact (string, default:"exact") 
*    "exact" : find and highlight the exact words.
*    "whole" : find partial matches but highlight whole words
*    "partial": find and highlight partial matches
*     
*  - style_name (string, default:'hilite')
*    The class given to the span wrapping the matched words.
*     
*  - style_name_suffix (boolean, default:true)
*    If true a different number is added to style_name for every different matched word.
*     
*  - debug_referrer (string, default:null)
*    Set a referrer for debugging purpose.
*     
*  - engines (array of regex, default:null)
*    Add a new search engine regex to highlight searches coming from new search engines.
*    The first element is the regex to match the domain.
*    The second element is the regex to match the query string. 
*    Ex: [/^http:\/\/my\.site\.net/i,/search=([^&]+)/i]        
*            
*  - highlight (string, default:null)
*    A jQuery selector or object to set the elements enabled for highlight.
*    If null or no elements are found, all the document is enabled for highlight.
*        
*  - nohighlight (string, default:null)  
*    A jQuery selector or object to set the elements not enabled for highlight.
*    This option has priority on highlight. 
*    
*  - keys (string, default:null)
*    Disable the analisys of the referrer and search for the words given as argument    
*    
*/

(function ($) {
  jQuery.fn.SearchHighlight = function (options) {
    var ref = options.debug_referrer || document.referrer;
    if (!ref && options.keys == undefined) return this;

    SearchHighlight.options = $.extend({ exact: "exact", style_name: 'hilite', style_name_suffix: true }, options);

    if (options.engines) SearchHighlight.engines.unshift(options.engines);
    var q = options.keys != undefined ? options.keys.toLowerCase().split(/[\s,\+\.]+/) : SearchHighlight.decodeURL(ref, SearchHighlight.engines);
    if (q && q.join("")) {
      SearchHighlight.buildReplaceTools(q);
      return this.each(function () {
        var el = this;
        if (el == document) el = $(".listItem-content")[0];
        SearchHighlight.hiliteElement(el, q);
      })
    } else return this;
  }

  var SearchHighlight = {
    options: {},
    regex: [],
    engines: [
    [/^http:\/\/(www\.)?google\./i, /q=([^&]+)/i],                            // Google
    [/^http:\/\/(www\.)?search\.yahoo\./i, /p=([^&]+)/i],                     // Yahoo
    [/^http:\/\/(www\.)?search\.msn\./i, /q=([^&]+)/i],                       // MSN
    [/^http:\/\/(www\.)?search\.live\./i, /query=([^&]+)/i],                  // MSN Live
    [/^http:\/\/(www\.)?search\.aol\./i, /userQuery=([^&]+)/i],               // AOL
    [/^http:\/\/(www\.)?ask\.com/i, /q=([^&]+)/i],                            // Ask.com
    [/^http:\/\/(www\.)?altavista\./i, /q=([^&]+)/i],                         // AltaVista
    [/^http:\/\/(www\.)?feedster\./i, /q=([^&]+)/i],                          // Feedster
    [/^http:\/\/(www\.)?search\.lycos\./i, /q=([^&]+)/i],                     // Lycos
    [/^http:\/\/(www\.)?alltheweb\./i, /q=([^&]+)/i],                         // AllTheWeb
    [/^http:\/\/(www\.)?technorati\.com/i, /([^\?\/]+)(?:\?.*)$/i],           // Technorati
    ],
    subs: {},
    decodeURL: function (URL, reg) {
      URL = decodeURIComponent(URL);
      var query = null;
      $.each(reg, function (i, n) {
        if (n[0].test(URL)) {
          var match = URL.match(n[1]);
          if (match) {
            query = match[1].toLowerCase();
            return false;
          }
        }
      })

      if (query) {
        query = query.replace(/(\'|")/, '\$1');
        query = query.split(/[\s,\+\.]+/);
      }

      return query;
    },
    regexAccent: [
      [/[\xC0-\xC5\u0100-\u0105]/ig, 'a'],
      [/[\xC7\u0106-\u010D]/ig, 'c'],
      [/[\xC8-\xCB]/ig, 'e'],
      [/[\xCC-\xCF]/ig, 'i'],
      [/\xD1/ig, 'n'],
      [/[\xD2-\xD6\xD8]/ig, 'o'],
      [/[\u015A-\u0161]/ig, 's'],
      [/[\u0162-\u0167]/ig, 't'],
      [/[\xD9-\xDC]/ig, 'u'],
      [/\xFF/ig, 'y'],
      [/[\x91\x92\u2018\u2019]/ig, '\'']
    ],
    matchAccent: /[\x91\x92\xC0-\xC5\xC7-\xCF\xD1-\xD6\xD8-\xDC\xFF\u0100-\u010D\u015A-\u0167\u2018\u2019]/ig,
    replaceAccent: function (q) {
      SearchHighlight.matchAccent.lastIndex = 0;
      if (SearchHighlight.matchAccent.test(q)) {
        for (var i = 0, l = SearchHighlight.regexAccent.length; i < l; i++)
          q = q.replace(SearchHighlight.regexAccent[i][0], SearchHighlight.regexAccent[i][1]);
      }
      return q;
    },
    escapeRegEx: /((?:\\{2})*)([[\]{}*?|])/g, //the special chars . and + are already gone at this point because they are considered split chars
    buildReplaceTools: function (query) {
      var re = [], regex;
      $.each(query, function (i, n) {
        if (n = SearchHighlight.replaceAccent(n).replace(SearchHighlight.escapeRegEx, "$1\\$2"))
          re.push(n);
      });

      regex = re.join("|");
      switch (SearchHighlight.options.exact) {
        case "exact":
          regex = '\\b(?:' + regex + ')\\b';
          break;
        case "whole":
          regex = '\\b\\w*(' + regex + ')\\w*\\b';
          break;
      }
      SearchHighlight.regex = new RegExp(regex, "gi");

      $.each(re, function (i, n) {
        SearchHighlight.subs[n] = SearchHighlight.options.style_name +
              (SearchHighlight.options.style_name_suffix ? i + 1 : '');
      });
    },
    nosearch: /s(?:cript|tyle)|textarea/i,
    hiliteElement: function (el, query) {
      var opt = SearchHighlight.options, elHighlight, noHighlight;
      elHighlight = opt.highlight ? $(opt.highlight) : $(".listItem-content");
      if (!elHighlight.length) elHighlight = $(".listItem-content");
      noHighlight = opt.nohighlight ? $(opt.nohighlight) : $([]);

      elHighlight.each(function () {
        SearchHighlight.hiliteTree(this, query, noHighlight);
      });
    },
    hiliteTree: function (el, query, noHighlight) {
      if (noHighlight.index(el) != -1) return;
      var matchIndex = SearchHighlight.options.exact == "whole" ? 1 : 0;
      for (var startIndex = 0, endIndex = el.childNodes.length; startIndex < endIndex; startIndex++) {
        var item = el.childNodes[startIndex];
        if (item.nodeType != 8) {//comment node
          //text node
          if (item.nodeType == 3) {
            var text = item.data, textNoAcc = SearchHighlight.replaceAccent(text);
            var newtext = "", match, index = 0;
            SearchHighlight.regex.lastIndex = 0;
            while (match = SearchHighlight.regex.exec(textNoAcc)) {
              newtext += text.substr(index, match.index - index) + '<span class="' +
                SearchHighlight.subs[match[matchIndex].toLowerCase()] + '">' + text.substr(match.index, match[0].length) + "</span>";
              index = match.index + match[0].length;
            }
            if (newtext) {
              //add the last part of the text
              newtext += text.substring(index);
              var repl = $.merge([], $("<span>" + newtext + "</span>")[0].childNodes);
              endIndex += repl.length - 1;
              startIndex += repl.length - 1;
              $(item).before(repl).remove();
            }
          } else {
            if (item.nodeType == 1 && item.nodeName.search(SearchHighlight.nosearch) == -1)
              SearchHighlight.hiliteTree(item, query, noHighlight);
          }
        }
      }
    }
  };
})(jQuery)
/*
 * jQuery selectbox plugin
 *
 * Copyright (c) 2007 Sadri Sahraoui (brainfault.com)
 * Licensed under the GPL license:
 *   http://www.gnu.org/licenses/gpl.html
 *
 * The code is inspired from Autocomplete plugin (http://www.dyve.net/jquery/?autocomplete)
 *
 * Revision: $Id$
 * Version: 0.3
 */
jQuery.fn.extend({
	selectbox: function(options) {
		return this.each(function() {
			new jQuery.SelectBox(this, options);
		});
	}
});

jQuery.SelectBox = function(selectobj, options) {
	
	var opt = options || {};
	opt.inputClass = opt.inputClass || "selectbox";
	opt.containerClass = opt.containerClass || "selectbox-wrapper";
	opt.hoverClass = opt.hoverClass || "selected";
	opt.debug = opt.debug || false;
	
	var elm_id = selectobj.id;
	var active = -1;
	var inFocus = false;
	var hasfocus = 0;
	//jquery object for select element
	var $select = $(selectobj);
	// jquery container object
	var $container = setupContainer(opt);
	//jquery input object 
	var $input = setupInput(opt);
	// hide select and append newly created elements
	$select.hide().before($input).before($container);
	
	init();
	
	$input
	.click(function(){
        if (!inFocus) {
		  $container.toggle();
		}
	})
	.focus(function(){
	   if ($container.not(':visible')) {
	       inFocus = true;
	       $container.show();
	   }
	})
	.keydown(function(event) {	   
		switch(event.keyCode) {
			case 38: // up
				event.preventDefault();
				moveSelect(-1);
				break;
			case 40: // down
				event.preventDefault();
				moveSelect(1);
				break;
			//case 9:  // tab 
			case 13: // return
				event.preventDefault(); // seems not working in mac !
				setCurrent();
				hideMe();
				break;
		}
	})
	.blur(function() {
		if ($container.is(':visible') && hasfocus > 0 ) {
			if(opt.debug) console.log('container visible and has focus')
		} else {
			hideMe();	
		}
	});


	function hideMe() { 
		hasfocus = 0;
		$container.hide(); 
	}
	
	function init() {
		$container.append(getSelectOptions()).hide();
		var width = $input.width()
		$container.width(width);
    }
	
	function setupContainer(options) {
		var container = document.createElement("div");
		$container = $(container);
		$container.attr('id', elm_id+'_container');
		$container.addClass(options.containerClass);
		
		return $container;
	}
	
	function setupInput(options) {
		var input = document.createElement("input");
		var $input = $(input);
		$input.attr("id", elm_id+"_input");
		$input.attr("type", "text");
		$input.addClass(options.inputClass);
		$input.attr("autocomplete", "off");
		$input.attr("readonly", "readonly");
		$input.attr("tabIndex", $select.attr("tabindex")); // "I" capital is important for ie
		
		return $input;	
	}
	
	function moveSelect(step) {
		var lis = $("li", $container);
		if (!lis) return;

		active += step;

		if (active < 0) {
			active = 0;
		} else if (active >= lis.size()) {
			active = lis.size() - 1;
		}

		lis.removeClass(opt.hoverClass);

		$(lis[active]).addClass(opt.hoverClass);
	}
	
	function setCurrent() {	
		var li = $("li."+opt.hoverClass, $container).get(0);
		var el = li.id
		$select.val(el);
		$input.val($(li).html());
		return true;
	}
	
	// select value
	function getCurrentSelected() {
		return $select.val();
	}
	
	// input value
	function getCurrentValue() {
		return $input.val();
	}
	
	function getSelectOptions() {
		var select_options = new Array();
		var ul = document.createElement('ul');
		$select.children('option').each(function() {
			var li = document.createElement('li');
			li.setAttribute('id', $(this).val());
			li.innerHTML = $(this).html();
			if ($(this).is(':selected')) {
				$input.val($(this).html());
				$(li).addClass(opt.hoverClass);
			}
			ul.appendChild(li);
			$(li)
			.mouseover(function(event) {
				hasfocus = 1;
				if (opt.debug) console.log('out on : '+this.id);
				jQuery(event.target, $container).addClass(opt.hoverClass);
			})
			.mouseout(function(event) {
				hasfocus = -1;
				if (opt.debug) console.log('out on : '+this.id);
				jQuery(event.target, $container).removeClass(opt.hoverClass);
			})
			.click(function(event) {
				if (opt.debug) console.log('click on :'+this.id);
				$(this).addClass(opt.hoverClass);
				setCurrent();
				hideMe();
			});
		});
		return ul;
	}
	
};
(function($) {
  var m = {
    '\b': '\\b',
    '\t': '\\t',
    '\n': '\\n',
    '\f': '\\f',
    '\r': '\\r',
    '"': '\\"',
    '\\': '\\\\'
  },
        s = {
          'array': function(x) {
            var a = ['['], b, f, i, l = x.length, v;
            for (i = 0; i < l; i += 1) {
              v = x[i];
              f = s[typeof v];
              if (f) {
                v = f(v);
                if (typeof v == 'string') {
                  if (b) {
                    a[a.length] = ',';
                  }
                  a[a.length] = v;
                  b = true;
                }
              }
            }
            a[a.length] = ']';
            return a.join('');
          },
          'boolean': function(x) {
            return String(x);
          },
          'null': function(x) {
            return "null";
          },
          'number': function(x) {
            return isFinite(x) ? String(x) : 'null';
          },
          'object': function(x) {
            if (x) {
              if (x instanceof Array) {
                return s.array(x);
              }
              var a = ['{'], b, f, i, v;
              for (i in x) {
                v = x[i];
                f = s[typeof v];
                if (f) {
                  v = f(v);
                  if (typeof v == 'string') {
                    if (b) {
                      a[a.length] = ',';
                    }
                    a.push(s.string(i), ':', v);
                    b = true;
                  }
                }
              }
              a[a.length] = '}';
              return a.join('');
            }
            return 'null';
          },
          'string': function(x) {
            if (/["\\\x00-\x1f]/.test(x)) {
              x = x.replace(/([\x00-\x1f\\"])/g, function(a, b) {
                var c = m[b];
                if (c) {
                  return c;
                }
                c = b.charCodeAt();
                return '\\u00' +
                            Math.floor(c / 16).toString(16) +
                            (c % 16).toString(16);
              });
            }
            return '"' + x + '"';
          }
        };

  $.toJSON = function(v) {
    log(typeof v);
    var f = isNaN(v) ? s[typeof v] : s['number'];
    if (f) return f(v);
  };

  $.parseJSON = function(v, safe) {
    if (safe === undefined) safe = $.parseJSON.safe;
    if (safe && !/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.test(v))
      return undefined;
    return eval('(' + v + ')');
  };

  $.parseJSON.safe = false;

})(jQuery);

/*
 * jQuery Tooltip plugin 1.3
 *
 * http://bassistance.de/jquery-plugins/jquery-plugin-tooltip/
 * http://docs.jquery.com/Plugins/Tooltip
 *
 * Copyright (c) 2006 - 2008 Jörn Zaefferer
 *
 * $Id: jquery.tooltip.js 5741 2008-06-21 15:22:16Z joern.zaefferer $
 * 
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 */
 
;(function($) {
	
		// the tooltip element
	var helper = {},
		// the current tooltipped element
		current,
		// the title of the current element, used for restoring
		title,
		// timeout id for delayed tooltips
		tID,
		// IE 5.5 or 6
		IE = $.browser.msie && /MSIE\s(5\.5|6\.)/.test(navigator.userAgent),
		// flag for mouse tracking
		track = false;
	
	$.tooltip = {
		blocked: false,
		defaults: {
			delay: 200,
			fade: false,
			showURL: true,
			extraClass: "",
			top: 15,
			left: 15,
			id: "tooltip"
		},
		block: function() {
			$.tooltip.blocked = !$.tooltip.blocked;
		}
	};
	
	$.fn.extend({
		tooltip: function(settings) {
			settings = $.extend({}, $.tooltip.defaults, settings);
			createHelper(settings);
			return this.each(function() {
					$.data(this, "tooltip", settings);
					this.tOpacity = helper.parent.css("opacity");
					// copy tooltip into its own expando and remove the title
					this.tooltipText = this.title;
					$(this).removeAttr("title");
					// also remove alt attribute to prevent default tooltip in IE
					this.alt = "";
				})
				.mouseover(save)
				.mouseout(hide)
				.click(hide);
		},
		fixPNG: IE ? function() {
			return this.each(function () {
				var image = $(this).css('backgroundImage');
				if (image.match(/^url\(["']?(.*\.png)["']?\)$/i)) {
					image = RegExp.$1;
					$(this).css({
						'backgroundImage': 'none',
						'filter': "progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=crop, src='" + image + "')"
					}).each(function () {
						var position = $(this).css('position');
						if (position != 'absolute' && position != 'relative')
							$(this).css('position', 'relative');
					});
				}
			});
		} : function() { return this; },
		unfixPNG: IE ? function() {
			return this.each(function () {
				$(this).css({'filter': '', backgroundImage: ''});
			});
		} : function() { return this; },
		hideWhenEmpty: function() {
			return this.each(function() {
				$(this)[ $(this).html() ? "show" : "hide" ]();
			});
		},
		url: function() {
			return this.attr('href') || this.attr('src');
		}
	});
	
	function createHelper(settings) {
		// there can be only one tooltip helper
		if( helper.parent )
			return;
		// create the helper, h3 for title, div for url
		helper.parent = $('<div id="' + settings.id + '"><h3></h3><div class="body"></div><div class="url"></div></div>')
			// add to document
			.appendTo(document.body)
			// hide it at first
			.hide();
			
		// apply bgiframe if available
		if ( $.fn.bgiframe )
			helper.parent.bgiframe();
		
		// save references to title and url elements
		helper.title = $('h3', helper.parent);
		helper.body = $('div.body', helper.parent);
		helper.url = $('div.url', helper.parent);
	}
	
	function settings(element) {
		return $.data(element, "tooltip");
	}
	
	// main event handler to start showing tooltips
	function handle(event) {
		// show helper, either with timeout or on instant
		if( settings(this).delay )
			tID = setTimeout(show, settings(this).delay);
		else
			show();
		
		// if selected, update the helper position when the mouse moves
		track = !!settings(this).track;
		$(document.body).bind('mousemove', update);
			
		// update at least once
		update(event);
	}
	
	// save elements title before the tooltip is displayed
	function save() {
		// if this is the current source, or it has no title (occurs with click event), stop
		if ( $.tooltip.blocked || this == current || (!this.tooltipText && !settings(this).bodyHandler) )
			return;

		// save current
		current = this;
		title = this.tooltipText;
		
		if ( settings(this).bodyHandler ) {
			helper.title.hide();
			var bodyContent = settings(this).bodyHandler.call(this);
			if (bodyContent.nodeType || bodyContent.jquery) {
				helper.body.empty().append(bodyContent)
			} else {
				helper.body.html( bodyContent );
			}
			helper.body.show();
		} else if ( settings(this).showBody ) {
			var parts = title.split(settings(this).showBody);
			helper.title.html(parts.shift()).show();
			helper.body.empty();
			for(var i = 0, part; (part = parts[i]); i++) {
				if(i > 0)
					helper.body.append("<br/>");
				helper.body.append(part);
			}
			helper.body.hideWhenEmpty();
		} else {
			helper.title.html(title).show();
			helper.body.hide();
		}
		
		// if element has href or src, add and show it, otherwise hide it
		if( settings(this).showURL && $(this).url() )
			helper.url.html( $(this).url().replace('http://', '') ).show();
		else 
			helper.url.hide();
		
		// add an optional class for this tip
		helper.parent.addClass(settings(this).extraClass);

		// fix PNG background for IE
		if (settings(this).fixPNG )
			helper.parent.fixPNG();
			
		handle.apply(this, arguments);
	}
	
	// delete timeout and show helper
	function show() {
		tID = null;
		if ((!IE || !$.fn.bgiframe) && settings(current).fade) {
			if (helper.parent.is(":animated"))
				helper.parent.stop().show().fadeTo(settings(current).fade, current.tOpacity);
			else
				helper.parent.is(':visible') ? helper.parent.fadeTo(settings(current).fade, current.tOpacity) : helper.parent.fadeIn(settings(current).fade);
		} else {
			helper.parent.show();
		}
		update();
	}
	
	/**
	 * callback for mousemove
	 * updates the helper position
	 * removes itself when no current element
	 */
	function update(event)	{
		if($.tooltip.blocked)
			return;
		
		if (event && event.target.tagName == "OPTION") {
			return;
		}
		
		// stop updating when tracking is disabled and the tooltip is visible
		if ( !track && helper.parent.is(":visible")) {
			$(document.body).unbind('mousemove', update)
		}
		
		// if no current element is available, remove this listener
		if( current == null ) {
			$(document.body).unbind('mousemove', update);
			return;	
		}
		
		// remove position helper classes
		helper.parent.removeClass("viewport-right").removeClass("viewport-bottom");
		
		var left = helper.parent[0].offsetLeft;
		var top = helper.parent[0].offsetTop;
		if (event) {
			// position the helper 15 pixel to bottom right, starting from mouse position
			left = event.pageX + settings(current).left;
			top = event.pageY + settings(current).top;
			var right='auto';
			if (settings(current).positionLeft) {
				right = $(window).width() - left;
				left = 'auto';
			}
			helper.parent.css({
				left: left,
				right: right,
				top: top
			});
		}
		
		var v = viewport(),
			h = helper.parent[0];
		// check horizontal position
		if (v.x + v.cx < h.offsetLeft + h.offsetWidth) {
			left -= h.offsetWidth + 20 + settings(current).left;
			helper.parent.css({left: left + 'px'}).addClass("viewport-right");
		}
		// check vertical position
		if (v.y + v.cy < h.offsetTop + h.offsetHeight) {
			top -= h.offsetHeight + 20 + settings(current).top;
			helper.parent.css({top: top + 'px'}).addClass("viewport-bottom");
		}
	}
	
	function viewport() {
		return {
			x: $(window).scrollLeft(),
			y: $(window).scrollTop(),
			cx: $(window).width(),
			cy: $(window).height()
		};
	}
	
	// hide helper and restore added classes and the title
	function hide(event) {
		if($.tooltip.blocked)
			return;
		// clear timeout if possible
		if(tID)
			clearTimeout(tID);
		// no more current element
		current = null;
		
		var tsettings = settings(this);
		function complete() {
			helper.parent.removeClass( tsettings.extraClass ).hide().css("opacity", "");
		}
		if ((!IE || !$.fn.bgiframe) && tsettings.fade) {
			if (helper.parent.is(':animated'))
				helper.parent.stop().fadeTo(tsettings.fade, 0, complete);
			else
				helper.parent.stop().fadeOut(tsettings.fade, complete);
		} else
			complete();
		
		if( settings(this).fixPNG )
			helper.parent.unfixPNG();
	}
	
})(jQuery);

/* French initialisation for the jQuery UI date picker plugin. */
/* Written by Keith Wood (kbwood@virginbroadband.com.au) and Stéphane Nahmani (sholby@sholby.net). */
jQuery(function ($) {
  //TODO:TRANSLATE
	$.datepicker.regional['fr'] = {
		closeText: 'Fermer',
		prevText: '&#x3c;Préc',
		nextText: 'Suiv&#x3e;',
		currentText: 'Courant',
		monthNames: ['Janvier','Février','Mars','Avril','Mai','Juin',
		'Juillet','Août','Septembre','Octobre','Novembre','Décembre'],
		monthNamesShort: ['Jan','Fév','Mar','Avr','Mai','Jun',
		'Jul','Aoû','Sep','Oct','Nov','Déc'],
		dayNames: ['Dimanche','Lundi','Mardi','Mercredi','Jeudi','Vendredi','Samedi'],
		dayNamesShort: ['Dim','Lun','Mar','Mer','Jeu','Ven','Sam'],
		dayNamesMin: ['Di','Lu','Ma','Me','Je','Ve','Sa'],
		dateFormat: 'dd/mm/yy', firstDay: 1,
		isRTL: false};
	$.datepicker.setDefaults($.datepicker.regional['fr']);
});
/*
 * jQuery UI Datepicker 1.7.2
 *
 * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 * http://docs.jquery.com/UI/Datepicker
 *
 * Depends:
 *	ui.core.js
 */

(function($) { // hide the namespace

$.extend($.ui, { datepicker: { version: "1.7.2" } });

var PROP_NAME = 'datepicker';

/* Date picker manager.
   Use the singleton instance of this class, $.datepicker, to interact with the date picker.
   Settings for (groups of) date pickers are maintained in an instance object,
   allowing multiple different settings on the same page. */

function Datepicker() {
	this.debug = false; // Change this to true to start debugging
	this._curInst = null; // The current instance in use
	this._keyEvent = false; // If the last event was a key event
	this._disabledInputs = []; // List of date picker inputs that have been disabled
	this._datepickerShowing = false; // True if the popup picker is showing , false if not
	this._inDialog = false; // True if showing within a "dialog", false if not
	this._mainDivId = 'ui-datepicker-div'; // The ID of the main datepicker division
	this._inlineClass = 'ui-datepicker-inline'; // The name of the inline marker class
	this._appendClass = 'ui-datepicker-append'; // The name of the append marker class
	this._triggerClass = 'ui-datepicker-trigger'; // The name of the trigger marker class
	this._dialogClass = 'ui-datepicker-dialog'; // The name of the dialog marker class
	this._disableClass = 'ui-datepicker-disabled'; // The name of the disabled covering marker class
	this._unselectableClass = 'ui-datepicker-unselectable'; // The name of the unselectable cell marker class
	this._currentClass = 'ui-datepicker-current-day'; // The name of the current day marker class
	this._dayOverClass = 'ui-datepicker-days-cell-over'; // The name of the day hover marker class
	this.regional = []; // Available regional settings, indexed by language code
	this.regional[''] = { // Default regional settings
		closeText: 'Done', // Display text for close link
		prevText: 'Prev', // Display text for previous month link
		nextText: 'Next', // Display text for next month link
		currentText: 'Today', // Display text for current month link
		monthNames: ['January','February','March','April','May','June',
			'July','August','September','October','November','December'], // Names of months for drop-down and formatting
		monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], // For formatting
		dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], // For formatting
		dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], // For formatting
		dayNamesMin: ['Su','Mo','Tu','We','Th','Fr','Sa'], // Column headings for days starting at Sunday
		dateFormat: 'mm/dd/yy', // See format options on parseDate
		firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ...
		isRTL: false // True if right-to-left language, false if left-to-right
	};
	this._defaults = { // Global defaults for all the date picker instances
		showOn: 'focus', // 'focus' for popup on focus,
			// 'button' for trigger button, or 'both' for either
		showAnim: 'show', // Name of jQuery animation for popup
		showOptions: {}, // Options for enhanced animations
		defaultDate: null, // Used when field is blank: actual date,
			// +/-number for offset from today, null for today
		appendText: '', // Display text following the input box, e.g. showing the format
		buttonText: '...', // Text for trigger button
		buttonImage: '', // URL for trigger button image
		buttonImageOnly: false, // True if the image appears alone, false if it appears on a button
		hideIfNoPrevNext: false, // True to hide next/previous month links
			// if not applicable, false to just disable them
		navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links
		gotoCurrent: false, // True if today link goes back to current selection instead
		changeMonth: false, // True if month can be selected directly, false if only prev/next
		changeYear: false, // True if year can be selected directly, false if only prev/next
		showMonthAfterYear: false, // True if the year select precedes month, false for month then year
		yearRange: '-10:+10', // Range of years to display in drop-down,
			// either relative to current year (-nn:+nn) or absolute (nnnn:nnnn)
		showOtherMonths: false, // True to show dates in other months, false to leave blank
		calculateWeek: this.iso8601Week, // How to calculate the week of the year,
			// takes a Date and returns the number of the week for it
		shortYearCutoff: '+10', // Short year values < this are in the current century,
			// > this are in the previous century,
			// string value starting with '+' for current year + value
		minDate: null, // The earliest selectable date, or null for no limit
		maxDate: null, // The latest selectable date, or null for no limit
		duration: 'normal', // Duration of display/closure
		beforeShowDay: null, // Function that takes a date and returns an array with
			// [0] = true if selectable, false if not, [1] = custom CSS class name(s) or '',
			// [2] = cell title (optional), e.g. $.datepicker.noWeekends
		beforeShow: null, // Function that takes an input field and
			// returns a set of custom settings for the date picker
		onSelect: null, // Define a callback function when a date is selected
		onChangeMonthYear: null, // Define a callback function when the month or year is changed
		onClose: null, // Define a callback function when the datepicker is closed
		numberOfMonths: 1, // Number of months to show at a time
		showCurrentAtPos: 0, // The position in multipe months at which to show the current month (starting at 0)
		stepMonths: 1, // Number of months to step back/forward
		stepBigMonths: 12, // Number of months to step back/forward for the big links
		altField: '', // Selector for an alternate field to store selected dates into
		altFormat: '', // The date format to use for the alternate field
		constrainInput: true, // The input is constrained by the current date format
		showButtonPanel: false // True to show button panel, false to not show it
	};
	$.extend(this._defaults, this.regional['']);
	this.dpDiv = $('<div id="' + this._mainDivId + '" class="ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all ui-helper-hidden-accessible"></div>');
}

$.extend(Datepicker.prototype, {
	/* Class name added to elements to indicate already configured with a date picker. */
	markerClassName: 'hasDatepicker',

	/* Debug logging (if enabled). */
	log: function () {
		if (this.debug)
			console.log.apply('', arguments);
	},

	/* Override the default settings for all instances of the date picker.
	   @param  settings  object - the new settings to use as defaults (anonymous object)
	   @return the manager object */
	setDefaults: function(settings) {
		extendRemove(this._defaults, settings || {});
		return this;
	},

	/* Attach the date picker to a jQuery selection.
	   @param  target    element - the target input field or division or span
	   @param  settings  object - the new settings to use for this date picker instance (anonymous) */
	_attachDatepicker: function(target, settings) {
		// check for settings on the control itself - in namespace 'date:'
		var inlineSettings = null;
		for (var attrName in this._defaults) {
			var attrValue = target.getAttribute('date:' + attrName);
			if (attrValue) {
				inlineSettings = inlineSettings || {};
				try {
					inlineSettings[attrName] = eval(attrValue);
				} catch (err) {
					inlineSettings[attrName] = attrValue;
				}
			}
		}
		var nodeName = target.nodeName.toLowerCase();
		var inline = (nodeName == 'div' || nodeName == 'span');
		if (!target.id)
			target.id = 'dp' + (++this.uuid);
		var inst = this._newInst($(target), inline);
		inst.settings = $.extend({}, settings || {}, inlineSettings || {});
		if (nodeName == 'input') {
			this._connectDatepicker(target, inst);
		} else if (inline) {
			this._inlineDatepicker(target, inst);
		}
	},

	/* Create a new instance object. */
	_newInst: function(target, inline) {
		var id = target[0].id.replace(/([:\[\]\.])/g, '\\\\$1'); // escape jQuery meta chars
		return {id: id, input: target, // associated target
			selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection
			drawMonth: 0, drawYear: 0, // month being drawn
			inline: inline, // is datepicker inline or not
			dpDiv: (!inline ? this.dpDiv : // presentation div
			$('<div class="' + this._inlineClass + ' ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>'))};
	},

	/* Attach the date picker to an input field. */
	_connectDatepicker: function(target, inst) {
		var input = $(target);
		inst.append = $([]);
		inst.trigger = $([]);
		if (input.hasClass(this.markerClassName))
			return;
		var appendText = this._get(inst, 'appendText');
		var isRTL = this._get(inst, 'isRTL');
		if (appendText) {
			inst.append = $('<span class="' + this._appendClass + '">' + appendText + '</span>');
			input[isRTL ? 'before' : 'after'](inst.append);
		}
		var showOn = this._get(inst, 'showOn');
		if (showOn == 'focus' || showOn == 'both') // pop-up date picker when in the marked field
			input.focus(this._showDatepicker);
		if (showOn == 'button' || showOn == 'both') { // pop-up date picker when button clicked
			var buttonText = this._get(inst, 'buttonText');
			var buttonImage = this._get(inst, 'buttonImage');
			inst.trigger = $(this._get(inst, 'buttonImageOnly') ?
				$('<img/>').addClass(this._triggerClass).
					attr({ src: buttonImage, alt: buttonText, title: buttonText }) :
				$('<button type="button"></button>').addClass(this._triggerClass).
					html(buttonImage == '' ? buttonText : $('<img/>').attr(
					{ src:buttonImage, alt:buttonText, title:buttonText })));
			input[isRTL ? 'before' : 'after'](inst.trigger);
			inst.trigger.click(function() {
				if ($.datepicker._datepickerShowing && $.datepicker._lastInput == target)
					$.datepicker._hideDatepicker();
				else
					$.datepicker._showDatepicker(target);
				return false;
			});
		}
		input.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).
			bind("setData.datepicker", function(event, key, value) {
				inst.settings[key] = value;
			}).bind("getData.datepicker", function(event, key) {
				return this._get(inst, key);
			});
		$.data(target, PROP_NAME, inst);
	},

	/* Attach an inline date picker to a div. */
	_inlineDatepicker: function(target, inst) {
		var divSpan = $(target);
		if (divSpan.hasClass(this.markerClassName))
			return;
		divSpan.addClass(this.markerClassName).append(inst.dpDiv).
			bind("setData.datepicker", function(event, key, value){
				inst.settings[key] = value;
			}).bind("getData.datepicker", function(event, key){
				return this._get(inst, key);
			});
		$.data(target, PROP_NAME, inst);
		this._setDate(inst, this._getDefaultDate(inst));
		this._updateDatepicker(inst);
		this._updateAlternate(inst);
	},

	/* Pop-up the date picker in a "dialog" box.
	   @param  input     element - ignored
	   @param  dateText  string - the initial date to display (in the current format)
	   @param  onSelect  function - the function(dateText) to call when a date is selected
	   @param  settings  object - update the dialog date picker instance's settings (anonymous object)
	   @param  pos       int[2] - coordinates for the dialog's position within the screen or
	                     event - with x/y coordinates or
	                     leave empty for default (screen centre)
	   @return the manager object */
	_dialogDatepicker: function(input, dateText, onSelect, settings, pos) {
		var inst = this._dialogInst; // internal instance
		if (!inst) {
			var id = 'dp' + (++this.uuid);
			this._dialogInput = $('<input type="text" id="' + id +
				'" size="1" style="position: absolute; top: -100px;"/>');
			this._dialogInput.keydown(this._doKeyDown);
			$('body').append(this._dialogInput);
			inst = this._dialogInst = this._newInst(this._dialogInput, false);
			inst.settings = {};
			$.data(this._dialogInput[0], PROP_NAME, inst);
		}
		extendRemove(inst.settings, settings || {});
		this._dialogInput.val(dateText);

		this._pos = (pos ? (pos.length ? pos : [pos.pageX, pos.pageY]) : null);
		if (!this._pos) {
			var browserWidth = window.innerWidth || document.documentElement.clientWidth ||	document.body.clientWidth;
			var browserHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
			var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
			var scrollY = document.documentElement.scrollTop || document.body.scrollTop;
			this._pos = // should use actual width/height below
				[(browserWidth / 2) - 100 + scrollX, (browserHeight / 2) - 150 + scrollY];
		}

		// move input on screen for focus, but hidden behind dialog
		this._dialogInput.css('left', this._pos[0] + 'px').css('top', this._pos[1] + 'px');
		inst.settings.onSelect = onSelect;
		this._inDialog = true;
		this.dpDiv.addClass(this._dialogClass);
		this._showDatepicker(this._dialogInput[0]);
		if ($.blockUI)
			$.blockUI(this.dpDiv);
		$.data(this._dialogInput[0], PROP_NAME, inst);
		return this;
	},

	/* Detach a datepicker from its control.
	   @param  target    element - the target input field or division or span */
	_destroyDatepicker: function(target) {
		var $target = $(target);
		var inst = $.data(target, PROP_NAME);
		if (!$target.hasClass(this.markerClassName)) {
			return;
		}
		var nodeName = target.nodeName.toLowerCase();
		$.removeData(target, PROP_NAME);
		if (nodeName == 'input') {
			inst.append.remove();
			inst.trigger.remove();
			$target.removeClass(this.markerClassName).
				unbind('focus', this._showDatepicker).
				unbind('keydown', this._doKeyDown).
				unbind('keypress', this._doKeyPress);
		} else if (nodeName == 'div' || nodeName == 'span')
			$target.removeClass(this.markerClassName).empty();
	},

	/* Enable the date picker to a jQuery selection.
	   @param  target    element - the target input field or division or span */
	_enableDatepicker: function(target) {
		var $target = $(target);
		var inst = $.data(target, PROP_NAME);
		if (!$target.hasClass(this.markerClassName)) {
			return;
		}
		var nodeName = target.nodeName.toLowerCase();
		if (nodeName == 'input') {
			target.disabled = false;
			inst.trigger.filter('button').
				each(function() { this.disabled = false; }).end().
				filter('img').css({opacity: '1.0', cursor: ''});
		}
		else if (nodeName == 'div' || nodeName == 'span') {
			var inline = $target.children('.' + this._inlineClass);
			inline.children().removeClass('ui-state-disabled');
		}
		this._disabledInputs = $.map(this._disabledInputs,
			function(value) { return (value == target ? null : value); }); // delete entry
	},

	/* Disable the date picker to a jQuery selection.
	   @param  target    element - the target input field or division or span */
	_disableDatepicker: function(target) {
		var $target = $(target);
		var inst = $.data(target, PROP_NAME);
		if (!$target.hasClass(this.markerClassName)) {
			return;
		}
		var nodeName = target.nodeName.toLowerCase();
		if (nodeName == 'input') {
			target.disabled = true;
			inst.trigger.filter('button').
				each(function() { this.disabled = true; }).end().
				filter('img').css({opacity: '0.5', cursor: 'default'});
		}
		else if (nodeName == 'div' || nodeName == 'span') {
			var inline = $target.children('.' + this._inlineClass);
			inline.children().addClass('ui-state-disabled');
		}
		this._disabledInputs = $.map(this._disabledInputs,
			function(value) { return (value == target ? null : value); }); // delete entry
		this._disabledInputs[this._disabledInputs.length] = target;
	},

	/* Is the first field in a jQuery collection disabled as a datepicker?
	   @param  target    element - the target input field or division or span
	   @return boolean - true if disabled, false if enabled */
	_isDisabledDatepicker: function(target) {
		if (!target) {
			return false;
		}
		for (var i = 0; i < this._disabledInputs.length; i++) {
			if (this._disabledInputs[i] == target)
				return true;
		}
		return false;
	},

	/* Retrieve the instance data for the target control.
	   @param  target  element - the target input field or division or span
	   @return  object - the associated instance data
	   @throws  error if a jQuery problem getting data */
	_getInst: function(target) {
		try {
			return $.data(target, PROP_NAME);
		}
		catch (err) {
			throw 'Missing instance data for this datepicker';
		}
	},

	/* Update or retrieve the settings for a date picker attached to an input field or division.
	   @param  target  element - the target input field or division or span
	   @param  name    object - the new settings to update or
	                   string - the name of the setting to change or retrieve,
	                   when retrieving also 'all' for all instance settings or
	                   'defaults' for all global defaults
	   @param  value   any - the new value for the setting
	                   (omit if above is an object or to retrieve a value) */
	_optionDatepicker: function(target, name, value) {
		var inst = this._getInst(target);
		if (arguments.length == 2 && typeof name == 'string') {
			return (name == 'defaults' ? $.extend({}, $.datepicker._defaults) :
				(inst ? (name == 'all' ? $.extend({}, inst.settings) :
				this._get(inst, name)) : null));
		}
		var settings = name || {};
		if (typeof name == 'string') {
			settings = {};
			settings[name] = value;
		}
		if (inst) {
			if (this._curInst == inst) {
				this._hideDatepicker(null);
			}
			var date = this._getDateDatepicker(target);
			extendRemove(inst.settings, settings);
			this._setDateDatepicker(target, date);
			this._updateDatepicker(inst);
		}
	},

	// change method deprecated
	_changeDatepicker: function(target, name, value) {
		this._optionDatepicker(target, name, value);
	},

	/* Redraw the date picker attached to an input field or division.
	   @param  target  element - the target input field or division or span */
	_refreshDatepicker: function(target) {
		var inst = this._getInst(target);
		if (inst) {
			this._updateDatepicker(inst);
		}
	},

	/* Set the dates for a jQuery selection.
	   @param  target   element - the target input field or division or span
	   @param  date     Date - the new date
	   @param  endDate  Date - the new end date for a range (optional) */
	_setDateDatepicker: function(target, date, endDate) {
		var inst = this._getInst(target);
		if (inst) {
			this._setDate(inst, date, endDate);
			this._updateDatepicker(inst);
			this._updateAlternate(inst);
		}
	},

	/* Get the date(s) for the first entry in a jQuery selection.
	   @param  target  element - the target input field or division or span
	   @return Date - the current date or
	           Date[2] - the current dates for a range */
	_getDateDatepicker: function(target) {
		var inst = this._getInst(target);
		if (inst && !inst.inline)
			this._setDateFromField(inst);
		return (inst ? this._getDate(inst) : null);
	},

	/* Handle keystrokes. */
	_doKeyDown: function(event) {
		var inst = $.datepicker._getInst(event.target);
		var handled = true;
		var isRTL = inst.dpDiv.is('.ui-datepicker-rtl');
		inst._keyEvent = true;
		if ($.datepicker._datepickerShowing)
			switch (event.keyCode) {
				case 9:  $.datepicker._hideDatepicker(null, '');
						break; // hide on tab out
				case 13: var sel = $('td.' + $.datepicker._dayOverClass +
							', td.' + $.datepicker._currentClass, inst.dpDiv);
						if (sel[0])
							$.datepicker._selectDay(event.target, inst.selectedMonth, inst.selectedYear, sel[0]);
						else
							$.datepicker._hideDatepicker(null, $.datepicker._get(inst, 'duration'));
						return false; // don't submit the form
						break; // select the value on enter
				case 27: $.datepicker._hideDatepicker(null, $.datepicker._get(inst, 'duration'));
						break; // hide on escape
				case 33: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
							-$.datepicker._get(inst, 'stepBigMonths') :
							-$.datepicker._get(inst, 'stepMonths')), 'M');
						break; // previous month/year on page up/+ ctrl
				case 34: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
							+$.datepicker._get(inst, 'stepBigMonths') :
							+$.datepicker._get(inst, 'stepMonths')), 'M');
						break; // next month/year on page down/+ ctrl
				case 35: if (event.ctrlKey || event.metaKey) $.datepicker._clearDate(event.target);
						handled = event.ctrlKey || event.metaKey;
						break; // clear on ctrl or command +end
				case 36: if (event.ctrlKey || event.metaKey) $.datepicker._gotoToday(event.target);
						handled = event.ctrlKey || event.metaKey;
						break; // current on ctrl or command +home
				case 37: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, (isRTL ? +1 : -1), 'D');
						handled = event.ctrlKey || event.metaKey;
						// -1 day on ctrl or command +left
						if (event.originalEvent.altKey) $.datepicker._adjustDate(event.target, (event.ctrlKey ?
									-$.datepicker._get(inst, 'stepBigMonths') :
									-$.datepicker._get(inst, 'stepMonths')), 'M');
						// next month/year on alt +left on Mac
						break;
				case 38: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, -7, 'D');
						handled = event.ctrlKey || event.metaKey;
						break; // -1 week on ctrl or command +up
				case 39: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, (isRTL ? -1 : +1), 'D');
						handled = event.ctrlKey || event.metaKey;
						// +1 day on ctrl or command +right
						if (event.originalEvent.altKey) $.datepicker._adjustDate(event.target, (event.ctrlKey ?
									+$.datepicker._get(inst, 'stepBigMonths') :
									+$.datepicker._get(inst, 'stepMonths')), 'M');
						// next month/year on alt +right
						break;
				case 40: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, +7, 'D');
						handled = event.ctrlKey || event.metaKey;
						break; // +1 week on ctrl or command +down
				default: handled = false;
			}
		else if (event.keyCode == 36 && event.ctrlKey) // display the date picker on ctrl+home
			$.datepicker._showDatepicker(this);
		else {
			handled = false;
		}
		if (handled) {
			event.preventDefault();
			event.stopPropagation();
		}
	},

	/* Filter entered characters - based on date format. */
	_doKeyPress: function(event) {
		var inst = $.datepicker._getInst(event.target);
		if ($.datepicker._get(inst, 'constrainInput')) {
			var chars = $.datepicker._possibleChars($.datepicker._get(inst, 'dateFormat'));
			var chr = String.fromCharCode(event.charCode == undefined ? event.keyCode : event.charCode);
			return event.ctrlKey || (chr < ' ' || !chars || chars.indexOf(chr) > -1);
		}
	},

	/* Pop-up the date picker for a given input field.
	   @param  input  element - the input field attached to the date picker or
	                  event - if triggered by focus */
	_showDatepicker: function(input) {
		input = input.target || input;
		if (input.nodeName.toLowerCase() != 'input') // find from button/image trigger
			input = $('input', input.parentNode)[0];
		if ($.datepicker._isDisabledDatepicker(input) || $.datepicker._lastInput == input) // already here
			return;
		var inst = $.datepicker._getInst(input);
		var beforeShow = $.datepicker._get(inst, 'beforeShow');
		extendRemove(inst.settings, (beforeShow ? beforeShow.apply(input, [input, inst]) : {}));
		$.datepicker._hideDatepicker(null, '');
		$.datepicker._lastInput = input;
		$.datepicker._setDateFromField(inst);
		if ($.datepicker._inDialog) // hide cursor
			input.value = '';
		if (!$.datepicker._pos) { // position below input
			$.datepicker._pos = $.datepicker._findPos(input);
			$.datepicker._pos[1] += input.offsetHeight; // add the height
		}
		var isFixed = false;
		$(input).parents().each(function() {
			isFixed |= $(this).css('position') == 'fixed';
			return !isFixed;
		});
		if (isFixed && $.browser.opera) { // correction for Opera when fixed and scrolled
			$.datepicker._pos[0] -= document.documentElement.scrollLeft;
			$.datepicker._pos[1] -= document.documentElement.scrollTop;
		}
		var offset = {left: $.datepicker._pos[0], top: $.datepicker._pos[1]};
		$.datepicker._pos = null;
		inst.rangeStart = null;
		// determine sizing offscreen
		inst.dpDiv.css({position: 'absolute', display: 'block', top: '-1000px'});
		$.datepicker._updateDatepicker(inst);
		// fix width for dynamic number of date pickers
		// and adjust position before showing
		offset = $.datepicker._checkOffset(inst, offset, isFixed);
		inst.dpDiv.css({position: ($.datepicker._inDialog && $.blockUI ?
			'static' : (isFixed ? 'fixed' : 'absolute')), display: 'none',
			left: offset.left + 'px', top: offset.top + 'px'});
		if (!inst.inline) {
			var showAnim = $.datepicker._get(inst, 'showAnim') || 'show';
			var duration = $.datepicker._get(inst, 'duration');
			var postProcess = function() {
				$.datepicker._datepickerShowing = true;
				if ($.browser.msie && parseInt($.browser.version,10) < 7) // fix IE < 7 select problems
					$('iframe.ui-datepicker-cover').css({width: inst.dpDiv.width() + 4,
						height: inst.dpDiv.height() + 4});
			};
			if ($.effects && $.effects[showAnim])
				inst.dpDiv.show(showAnim, $.datepicker._get(inst, 'showOptions'), duration, postProcess);
			else
				inst.dpDiv[showAnim](duration, postProcess);
			if (duration == '')
				postProcess();
			if (inst.input[0].type != 'hidden')
				inst.input[0].focus();
			$.datepicker._curInst = inst;
		}
	},

	/* Generate the date picker content. */
	_updateDatepicker: function(inst) {
		var dims = {width: inst.dpDiv.width() + 4,
			height: inst.dpDiv.height() + 4};
		var self = this;
		inst.dpDiv.empty().append(this._generateHTML(inst))
			.find('iframe.ui-datepicker-cover').
				css({width: dims.width, height: dims.height})
			.end()
			.find('button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a')
				.bind('mouseout', function(){
					$(this).removeClass('ui-state-hover');
					if(this.className.indexOf('ui-datepicker-prev') != -1) $(this).removeClass('ui-datepicker-prev-hover');
					if(this.className.indexOf('ui-datepicker-next') != -1) $(this).removeClass('ui-datepicker-next-hover');
				})
				.bind('mouseover', function(){
					if (!self._isDisabledDatepicker( inst.inline ? inst.dpDiv.parent()[0] : inst.input[0])) {
						$(this).parents('.ui-datepicker-calendar').find('a').removeClass('ui-state-hover');
						$(this).addClass('ui-state-hover');
						if(this.className.indexOf('ui-datepicker-prev') != -1) $(this).addClass('ui-datepicker-prev-hover');
						if(this.className.indexOf('ui-datepicker-next') != -1) $(this).addClass('ui-datepicker-next-hover');
					}
				})
			.end()
			.find('.' + this._dayOverClass + ' a')
				.trigger('mouseover')
			.end();
		var numMonths = this._getNumberOfMonths(inst);
		var cols = numMonths[1];
		var width = 17;
		if (cols > 1) {
			inst.dpDiv.addClass('ui-datepicker-multi-' + cols).css('width', (width * cols) + 'em');
		} else {
			inst.dpDiv.removeClass('ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4').width('');
		}
		inst.dpDiv[(numMonths[0] != 1 || numMonths[1] != 1 ? 'add' : 'remove') +
			'Class']('ui-datepicker-multi');
		inst.dpDiv[(this._get(inst, 'isRTL') ? 'add' : 'remove') +
			'Class']('ui-datepicker-rtl');
		if (inst.input && inst.input[0].type != 'hidden' && inst == $.datepicker._curInst)
			$(inst.input[0]).focus();
	},

	/* Check positioning to remain on screen. */
	_checkOffset: function(inst, offset, isFixed) {
		var dpWidth = inst.dpDiv.outerWidth();
		var dpHeight = inst.dpDiv.outerHeight();
		var inputWidth = inst.input ? inst.input.outerWidth() : 0;
		var inputHeight = inst.input ? inst.input.outerHeight() : 0;
		var viewWidth = (window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth) + $(document).scrollLeft();
		var viewHeight = (window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight) + $(document).scrollTop();

		offset.left -= (this._get(inst, 'isRTL') ? (dpWidth - inputWidth) : 0);
		offset.left -= (isFixed && offset.left == inst.input.offset().left) ? $(document).scrollLeft() : 0;
		offset.top -= (isFixed && offset.top == (inst.input.offset().top + inputHeight)) ? $(document).scrollTop() : 0;

		// now check if datepicker is showing outside window viewport - move to a better place if so.
		offset.left -= (offset.left + dpWidth > viewWidth && viewWidth > dpWidth) ? Math.abs(offset.left + dpWidth - viewWidth) : 0;
		offset.top -= (offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ? Math.abs(offset.top + dpHeight + inputHeight*2 - viewHeight) : 0;

		return offset;
	},

	/* Find an object's position on the screen. */
	_findPos: function(obj) {
        while (obj && (obj.type == 'hidden' || obj.nodeType != 1)) {
            obj = obj.nextSibling;
        }
        var position = $(obj).offset();
	    return [position.left, position.top];
	},

	/* Hide the date picker from view.
	   @param  input  element - the input field attached to the date picker
	   @param  duration  string - the duration over which to close the date picker */
	_hideDatepicker: function(input, duration) {
		var inst = this._curInst;
		if (!inst || (input && inst != $.data(input, PROP_NAME)))
			return;
		if (inst.stayOpen)
			this._selectDate('#' + inst.id, this._formatDate(inst,
				inst.currentDay, inst.currentMonth, inst.currentYear));
		inst.stayOpen = false;
		if (this._datepickerShowing) {
			duration = (duration != null ? duration : this._get(inst, 'duration'));
			var showAnim = this._get(inst, 'showAnim');
			var postProcess = function() {
				$.datepicker._tidyDialog(inst);
			};
			if (duration != '' && $.effects && $.effects[showAnim])
				inst.dpDiv.hide(showAnim, $.datepicker._get(inst, 'showOptions'),
					duration, postProcess);
			else
				inst.dpDiv[(duration == '' ? 'hide' : (showAnim == 'slideDown' ? 'slideUp' :
					(showAnim == 'fadeIn' ? 'fadeOut' : 'hide')))](duration, postProcess);
			if (duration == '')
				this._tidyDialog(inst);
			var onClose = this._get(inst, 'onClose');
			if (onClose)
				onClose.apply((inst.input ? inst.input[0] : null),
					[(inst.input ? inst.input.val() : ''), inst]);  // trigger custom callback
			this._datepickerShowing = false;
			this._lastInput = null;
			if (this._inDialog) {
				this._dialogInput.css({ position: 'absolute', left: '0', top: '-100px' });
				if ($.blockUI) {
					$.unblockUI();
					$('body').append(this.dpDiv);
				}
			}
			this._inDialog = false;
		}
		this._curInst = null;
	},

	/* Tidy up after a dialog display. */
	_tidyDialog: function(inst) {
		inst.dpDiv.removeClass(this._dialogClass).unbind('.ui-datepicker-calendar');
	},

	/* Close date picker if clicked elsewhere. */
	_checkExternalClick: function(event) {
		if (!$.datepicker._curInst)
			return;
		var $target = $(event.target);
		if (($target.parents('#' + $.datepicker._mainDivId).length == 0) &&
				!$target.hasClass($.datepicker.markerClassName) &&
				!$target.hasClass($.datepicker._triggerClass) &&
				$.datepicker._datepickerShowing && !($.datepicker._inDialog && $.blockUI))
			$.datepicker._hideDatepicker(null, '');
	},

	/* Adjust one of the date sub-fields. */
	_adjustDate: function(id, offset, period) {
		var target = $(id);
		var inst = this._getInst(target[0]);
		if (this._isDisabledDatepicker(target[0])) {
			return;
		}
		this._adjustInstDate(inst, offset +
			(period == 'M' ? this._get(inst, 'showCurrentAtPos') : 0), // undo positioning
			period);
		this._updateDatepicker(inst);
	},

	/* Action for current link. */
	_gotoToday: function(id) {
		var target = $(id);
		var inst = this._getInst(target[0]);
		if (this._get(inst, 'gotoCurrent') && inst.currentDay) {
			inst.selectedDay = inst.currentDay;
			inst.drawMonth = inst.selectedMonth = inst.currentMonth;
			inst.drawYear = inst.selectedYear = inst.currentYear;
		}
		else {
		var date = new Date();
		inst.selectedDay = date.getDate();
		inst.drawMonth = inst.selectedMonth = date.getMonth();
		inst.drawYear = inst.selectedYear = date.getFullYear();
		}
		this._notifyChange(inst);
		this._adjustDate(target);
	},

	/* Action for selecting a new month/year. */
	_selectMonthYear: function(id, select, period) {
		var target = $(id);
		var inst = this._getInst(target[0]);
		inst._selectingMonthYear = false;
		inst['selected' + (period == 'M' ? 'Month' : 'Year')] =
		inst['draw' + (period == 'M' ? 'Month' : 'Year')] =
			parseInt(select.options[select.selectedIndex].value,10);
		this._notifyChange(inst);
		this._adjustDate(target);
	},

	/* Restore input focus after not changing month/year. */
	_clickMonthYear: function(id) {
		var target = $(id);
		var inst = this._getInst(target[0]);
		if (inst.input && inst._selectingMonthYear && !$.browser.msie)
			inst.input[0].focus();
		inst._selectingMonthYear = !inst._selectingMonthYear;
	},

	/* Action for selecting a day. */
	_selectDay: function(id, month, year, td) {
		var target = $(id);
		if ($(td).hasClass(this._unselectableClass) || this._isDisabledDatepicker(target[0])) {
			return;
		}
		var inst = this._getInst(target[0]);
		inst.selectedDay = inst.currentDay = $('a', td).html();
		inst.selectedMonth = inst.currentMonth = month;
		inst.selectedYear = inst.currentYear = year;
		if (inst.stayOpen) {
			inst.endDay = inst.endMonth = inst.endYear = null;
		}
		this._selectDate(id, this._formatDate(inst,
			inst.currentDay, inst.currentMonth, inst.currentYear));
		if (inst.stayOpen) {
			inst.rangeStart = this._daylightSavingAdjust(
				new Date(inst.currentYear, inst.currentMonth, inst.currentDay));
			this._updateDatepicker(inst);
		}
	},

	/* Erase the input field and hide the date picker. */
	_clearDate: function(id) {
		var target = $(id);
		var inst = this._getInst(target[0]);
		inst.stayOpen = false;
		inst.endDay = inst.endMonth = inst.endYear = inst.rangeStart = null;
		this._selectDate(target, '');
	},

	/* Update the input field with the selected date. */
	_selectDate: function(id, dateStr) {
		var target = $(id);
		var inst = this._getInst(target[0]);
		dateStr = (dateStr != null ? dateStr : this._formatDate(inst));
		if (inst.input)
			inst.input.val(dateStr);
		this._updateAlternate(inst);
		var onSelect = this._get(inst, 'onSelect');
		if (onSelect)
			onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]);  // trigger custom callback
		else if (inst.input)
			inst.input.trigger('change'); // fire the change event
		if (inst.inline)
			this._updateDatepicker(inst);
		else if (!inst.stayOpen) {
			this._hideDatepicker(null, this._get(inst, 'duration'));
			this._lastInput = inst.input[0];
			if (typeof(inst.input[0]) != 'object')
				inst.input[0].focus(); // restore focus
			this._lastInput = null;
		}
	},

	/* Update any alternate field to synchronise with the main field. */
	_updateAlternate: function(inst) {
		var altField = this._get(inst, 'altField');
		if (altField) { // update alternate field too
			var altFormat = this._get(inst, 'altFormat') || this._get(inst, 'dateFormat');
			var date = this._getDate(inst);
			dateStr = this.formatDate(altFormat, date, this._getFormatConfig(inst));
			$(altField).each(function() { $(this).val(dateStr); });
		}
	},

	/* Set as beforeShowDay function to prevent selection of weekends.
	   @param  date  Date - the date to customise
	   @return [boolean, string] - is this date selectable?, what is its CSS class? */
	noWeekends: function(date) {
		var day = date.getDay();
		return [(day > 0 && day < 6), ''];
	},

	/* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition.
	   @param  date  Date - the date to get the week for
	   @return  number - the number of the week within the year that contains this date */
	iso8601Week: function(date) {
		var checkDate = new Date(date.getFullYear(), date.getMonth(), date.getDate());
		var firstMon = new Date(checkDate.getFullYear(), 1 - 1, 4); // First week always contains 4 Jan
		var firstDay = firstMon.getDay() || 7; // Day of week: Mon = 1, ..., Sun = 7
		firstMon.setDate(firstMon.getDate() + 1 - firstDay); // Preceding Monday
		if (firstDay < 4 && checkDate < firstMon) { // Adjust first three days in year if necessary
			checkDate.setDate(checkDate.getDate() - 3); // Generate for previous year
			return $.datepicker.iso8601Week(checkDate);
		} else if (checkDate > new Date(checkDate.getFullYear(), 12 - 1, 28)) { // Check last three days in year
			firstDay = new Date(checkDate.getFullYear() + 1, 1 - 1, 4).getDay() || 7;
			if (firstDay > 4 && (checkDate.getDay() || 7) < firstDay - 3) { // Adjust if necessary
				return 1;
			}
		}
		return Math.floor(((checkDate - firstMon) / 86400000) / 7) + 1; // Weeks to given date
	},

	/* Parse a string value into a date object.
	   See formatDate below for the possible formats.

	   @param  format    string - the expected format of the date
	   @param  value     string - the date in the above format
	   @param  settings  Object - attributes include:
	                     shortYearCutoff  number - the cutoff year for determining the century (optional)
	                     dayNamesShort    string[7] - abbreviated names of the days from Sunday (optional)
	                     dayNames         string[7] - names of the days from Sunday (optional)
	                     monthNamesShort  string[12] - abbreviated names of the months (optional)
	                     monthNames       string[12] - names of the months (optional)
	   @return  Date - the extracted date value or null if value is blank */
	parseDate: function (format, value, settings) {
		if (format == null || value == null)
			throw 'Invalid arguments';
		value = (typeof value == 'object' ? value.toString() : value + '');
		if (value == '')
			return null;
		var shortYearCutoff = (settings ? settings.shortYearCutoff : null) || this._defaults.shortYearCutoff;
		var dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort;
		var dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames;
		var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort;
		var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames;
		var year = -1;
		var month = -1;
		var day = -1;
		var doy = -1;
		var literal = false;
		// Check whether a format character is doubled
		var lookAhead = function(match) {
			var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
			if (matches)
				iFormat++;
			return matches;
		};
		// Extract a number from the string value
		var getNumber = function(match) {
			lookAhead(match);
			var origSize = (match == '@' ? 14 : (match == 'y' ? 4 : (match == 'o' ? 3 : 2)));
			var size = origSize;
			var num = 0;
			while (size > 0 && iValue < value.length &&
					value.charAt(iValue) >= '0' && value.charAt(iValue) <= '9') {
				num = num * 10 + parseInt(value.charAt(iValue++),10);
				size--;
			}
			if (size == origSize)
				throw 'Missing number at position ' + iValue;
			return num;
		};
		// Extract a name from the string value and convert to an index
		var getName = function(match, shortNames, longNames) {
			var names = (lookAhead(match) ? longNames : shortNames);
			var size = 0;
			for (var j = 0; j < names.length; j++)
				size = Math.max(size, names[j].length);
			var name = '';
			var iInit = iValue;
			while (size > 0 && iValue < value.length) {
				name += value.charAt(iValue++);
				for (var i = 0; i < names.length; i++)
					if (name == names[i])
						return i + 1;
				size--;
			}
			throw 'Unknown name at position ' + iInit;
		};
		// Confirm that a literal character matches the string value
		var checkLiteral = function() {
			if (value.charAt(iValue) != format.charAt(iFormat))
				throw 'Unexpected literal at position ' + iValue;
			iValue++;
		};
		var iValue = 0;
		for (var iFormat = 0; iFormat < format.length; iFormat++) {
			if (literal)
				if (format.charAt(iFormat) == "'" && !lookAhead("'"))
					literal = false;
				else
					checkLiteral();
			else
				switch (format.charAt(iFormat)) {
					case 'd':
						day = getNumber('d');
						break;
					case 'D':
						getName('D', dayNamesShort, dayNames);
						break;
					case 'o':
						doy = getNumber('o');
						break;
					case 'm':
						month = getNumber('m');
						break;
					case 'M':
						month = getName('M', monthNamesShort, monthNames);
						break;
					case 'y':
						year = getNumber('y');
						break;
					case '@':
						var date = new Date(getNumber('@'));
						year = date.getFullYear();
						month = date.getMonth() + 1;
						day = date.getDate();
						break;
					case "'":
						if (lookAhead("'"))
							checkLiteral();
						else
							literal = true;
						break;
					default:
						checkLiteral();
				}
		}
		if (year == -1)
			year = new Date().getFullYear();
		else if (year < 100)
			year += new Date().getFullYear() - new Date().getFullYear() % 100 +
				(year <= shortYearCutoff ? 0 : -100);
		if (doy > -1) {
			month = 1;
			day = doy;
			do {
				var dim = this._getDaysInMonth(year, month - 1);
				if (day <= dim)
					break;
				month++;
				day -= dim;
			} while (true);
		}
		var date = this._daylightSavingAdjust(new Date(year, month - 1, day));
		if (date.getFullYear() != year || date.getMonth() + 1 != month || date.getDate() != day)
			throw 'Invalid date'; // E.g. 31/02/*
		return date;
	},

	/* Standard date formats. */
	ATOM: 'yy-mm-dd', // RFC 3339 (ISO 8601)
	COOKIE: 'D, dd M yy',
	ISO_8601: 'yy-mm-dd',
	RFC_822: 'D, d M y',
	RFC_850: 'DD, dd-M-y',
	RFC_1036: 'D, d M y',
	RFC_1123: 'D, d M yy',
	RFC_2822: 'D, d M yy',
	RSS: 'D, d M y', // RFC 822
	TIMESTAMP: '@',
	W3C: 'yy-mm-dd', // ISO 8601

	/* Format a date object into a string value.
	   The format can be combinations of the following:
	   d  - day of month (no leading zero)
	   dd - day of month (two digit)
	   o  - day of year (no leading zeros)
	   oo - day of year (three digit)
	   D  - day name short
	   DD - day name long
	   m  - month of year (no leading zero)
	   mm - month of year (two digit)
	   M  - month name short
	   MM - month name long
	   y  - year (two digit)
	   yy - year (four digit)
	   @ - Unix timestamp (ms since 01/01/1970)
	   '...' - literal text
	   '' - single quote

	   @param  format    string - the desired format of the date
	   @param  date      Date - the date value to format
	   @param  settings  Object - attributes include:
	                     dayNamesShort    string[7] - abbreviated names of the days from Sunday (optional)
	                     dayNames         string[7] - names of the days from Sunday (optional)
	                     monthNamesShort  string[12] - abbreviated names of the months (optional)
	                     monthNames       string[12] - names of the months (optional)
	   @return  string - the date in the above format */
	formatDate: function (format, date, settings) {
		if (!date)
			return '';
		var dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort;
		var dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames;
		var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort;
		var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames;
		// Check whether a format character is doubled
		var lookAhead = function(match) {
			var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
			if (matches)
				iFormat++;
			return matches;
		};
		// Format a number, with leading zero if necessary
		var formatNumber = function(match, value, len) {
			var num = '' + value;
			if (lookAhead(match))
				while (num.length < len)
					num = '0' + num;
			return num;
		};
		// Format a name, short or long as requested
		var formatName = function(match, value, shortNames, longNames) {
			return (lookAhead(match) ? longNames[value] : shortNames[value]);
		};
		var output = '';
		var literal = false;
		if (date)
			for (var iFormat = 0; iFormat < format.length; iFormat++) {
				if (literal)
					if (format.charAt(iFormat) == "'" && !lookAhead("'"))
						literal = false;
					else
						output += format.charAt(iFormat);
				else
					switch (format.charAt(iFormat)) {
						case 'd':
							output += formatNumber('d', date.getDate(), 2);
							break;
						case 'D':
							output += formatName('D', date.getDay(), dayNamesShort, dayNames);
							break;
						case 'o':
							var doy = date.getDate();
							for (var m = date.getMonth() - 1; m >= 0; m--)
								doy += this._getDaysInMonth(date.getFullYear(), m);
							output += formatNumber('o', doy, 3);
							break;
						case 'm':
							output += formatNumber('m', date.getMonth() + 1, 2);
							break;
						case 'M':
							output += formatName('M', date.getMonth(), monthNamesShort, monthNames);
							break;
						case 'y':
							output += (lookAhead('y') ? date.getFullYear() :
								(date.getYear() % 100 < 10 ? '0' : '') + date.getYear() % 100);
							break;
						case '@':
							output += date.getTime();
							break;
						case "'":
							if (lookAhead("'"))
								output += "'";
							else
								literal = true;
							break;
						default:
							output += format.charAt(iFormat);
					}
			}
		return output;
	},

	/* Extract all possible characters from the date format. */
	_possibleChars: function (format) {
		var chars = '';
		var literal = false;
		for (var iFormat = 0; iFormat < format.length; iFormat++)
			if (literal)
				if (format.charAt(iFormat) == "'" && !lookAhead("'"))
					literal = false;
				else
					chars += format.charAt(iFormat);
			else
				switch (format.charAt(iFormat)) {
					case 'd': case 'm': case 'y': case '@':
						chars += '0123456789';
						break;
					case 'D': case 'M':
						return null; // Accept anything
					case "'":
						if (lookAhead("'"))
							chars += "'";
						else
							literal = true;
						break;
					default:
						chars += format.charAt(iFormat);
				}
		return chars;
	},

	/* Get a setting value, defaulting if necessary. */
	_get: function(inst, name) {
		return inst.settings[name] !== undefined ?
			inst.settings[name] : this._defaults[name];
	},

	/* Parse existing date and initialise date picker. */
	_setDateFromField: function(inst) {
		var dateFormat = this._get(inst, 'dateFormat');
		var dates = inst.input ? inst.input.val() : null;
		inst.endDay = inst.endMonth = inst.endYear = null;
		var date = defaultDate = this._getDefaultDate(inst);
		var settings = this._getFormatConfig(inst);
		try {
			date = this.parseDate(dateFormat, dates, settings) || defaultDate;
		} catch (event) {
			this.log(event);
			date = defaultDate;
		}
		inst.selectedDay = date.getDate();
		inst.drawMonth = inst.selectedMonth = date.getMonth();
		inst.drawYear = inst.selectedYear = date.getFullYear();
		inst.currentDay = (dates ? date.getDate() : 0);
		inst.currentMonth = (dates ? date.getMonth() : 0);
		inst.currentYear = (dates ? date.getFullYear() : 0);
		this._adjustInstDate(inst);
	},

	/* Retrieve the default date shown on opening. */
	_getDefaultDate: function(inst) {
		var date = this._determineDate(this._get(inst, 'defaultDate'), new Date());
		var minDate = this._getMinMaxDate(inst, 'min', true);
		var maxDate = this._getMinMaxDate(inst, 'max');
		date = (minDate && date < minDate ? minDate : date);
		date = (maxDate && date > maxDate ? maxDate : date);
		return date;
	},

	/* A date may be specified as an exact value or a relative one. */
	_determineDate: function(date, defaultDate) {
		var offsetNumeric = function(offset) {
			var date = new Date();
			date.setDate(date.getDate() + offset);
			return date;
		};
		var offsetString = function(offset, getDaysInMonth) {
			var date = new Date();
			var year = date.getFullYear();
			var month = date.getMonth();
			var day = date.getDate();
			var pattern = /([+-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g;
			var matches = pattern.exec(offset);
			while (matches) {
				switch (matches[2] || 'd') {
					case 'd' : case 'D' :
						day += parseInt(matches[1],10); break;
					case 'w' : case 'W' :
						day += parseInt(matches[1],10) * 7; break;
					case 'm' : case 'M' :
						month += parseInt(matches[1],10);
						day = Math.min(day, getDaysInMonth(year, month));
						break;
					case 'y': case 'Y' :
						year += parseInt(matches[1],10);
						day = Math.min(day, getDaysInMonth(year, month));
						break;
				}
				matches = pattern.exec(offset);
			}
			return new Date(year, month, day);
		};
		date = (date == null ? defaultDate :
			(typeof date == 'string' ? offsetString(date, this._getDaysInMonth) :
			(typeof date == 'number' ? (isNaN(date) ? defaultDate : offsetNumeric(date)) : date)));
		date = (date && date.toString() == 'Invalid Date' ? defaultDate : date);
		if (date) {
			date.setHours(0);
			date.setMinutes(0);
			date.setSeconds(0);
			date.setMilliseconds(0);
		}
		return this._daylightSavingAdjust(date);
	},

	/* Handle switch to/from daylight saving.
	   Hours may be non-zero on daylight saving cut-over:
	   > 12 when midnight changeover, but then cannot generate
	   midnight datetime, so jump to 1AM, otherwise reset.
	   @param  date  (Date) the date to check
	   @return  (Date) the corrected date */
	_daylightSavingAdjust: function(date) {
		if (!date) return null;
		date.setHours(date.getHours() > 12 ? date.getHours() + 2 : 0);
		return date;
	},

	/* Set the date(s) directly. */
	_setDate: function(inst, date, endDate) {
		var clear = !(date);
		var origMonth = inst.selectedMonth;
		var origYear = inst.selectedYear;
		date = this._determineDate(date, new Date());
		inst.selectedDay = inst.currentDay = date.getDate();
		inst.drawMonth = inst.selectedMonth = inst.currentMonth = date.getMonth();
		inst.drawYear = inst.selectedYear = inst.currentYear = date.getFullYear();
		if (origMonth != inst.selectedMonth || origYear != inst.selectedYear)
			this._notifyChange(inst);
		this._adjustInstDate(inst);
		if (inst.input) {
			inst.input.val(clear ? '' : this._formatDate(inst));
		}
	},

	/* Retrieve the date(s) directly. */
	_getDate: function(inst) {
		var startDate = (!inst.currentYear || (inst.input && inst.input.val() == '') ? null :
			this._daylightSavingAdjust(new Date(
			inst.currentYear, inst.currentMonth, inst.currentDay)));
			return startDate;
	},

	/* Generate the HTML for the current state of the date picker. */
	_generateHTML: function(inst) {
		var today = new Date();
		today = this._daylightSavingAdjust(
			new Date(today.getFullYear(), today.getMonth(), today.getDate())); // clear time
		var isRTL = this._get(inst, 'isRTL');
		var showButtonPanel = this._get(inst, 'showButtonPanel');
		var hideIfNoPrevNext = this._get(inst, 'hideIfNoPrevNext');
		var navigationAsDateFormat = this._get(inst, 'navigationAsDateFormat');
		var numMonths = this._getNumberOfMonths(inst);
		var showCurrentAtPos = this._get(inst, 'showCurrentAtPos');
		var stepMonths = this._get(inst, 'stepMonths');
		var stepBigMonths = this._get(inst, 'stepBigMonths');
		var isMultiMonth = (numMonths[0] != 1 || numMonths[1] != 1);
		var currentDate = this._daylightSavingAdjust((!inst.currentDay ? new Date(9999, 9, 9) :
			new Date(inst.currentYear, inst.currentMonth, inst.currentDay)));
		var minDate = this._getMinMaxDate(inst, 'min', true);
		var maxDate = this._getMinMaxDate(inst, 'max');
		var drawMonth = inst.drawMonth - showCurrentAtPos;
		var drawYear = inst.drawYear;
		if (drawMonth < 0) {
			drawMonth += 12;
			drawYear--;
		}
		if (maxDate) {
			var maxDraw = this._daylightSavingAdjust(new Date(maxDate.getFullYear(),
				maxDate.getMonth() - numMonths[1] + 1, maxDate.getDate()));
			maxDraw = (minDate && maxDraw < minDate ? minDate : maxDraw);
			while (this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1)) > maxDraw) {
				drawMonth--;
				if (drawMonth < 0) {
					drawMonth = 11;
					drawYear--;
				}
			}
		}
		inst.drawMonth = drawMonth;
		inst.drawYear = drawYear;
		var prevText = this._get(inst, 'prevText');
		prevText = (!navigationAsDateFormat ? prevText : this.formatDate(prevText,
			this._daylightSavingAdjust(new Date(drawYear, drawMonth - stepMonths, 1)),
			this._getFormatConfig(inst)));
		var prev = (this._canAdjustMonth(inst, -1, drawYear, drawMonth) ?
			'<a class="ui-datepicker-prev ui-corner-all" onclick="DP_jQuery.datepicker._adjustDate(\'#' + inst.id + '\', -' + stepMonths + ', \'M\');"' +
			' title="' + prevText + '"><span class="ui-icon ui-icon-circle-triangle-' + ( isRTL ? 'e' : 'w') + '">' + prevText + '</span></a>' :
			(hideIfNoPrevNext ? '' : '<a class="ui-datepicker-prev ui-corner-all ui-state-disabled" title="'+ prevText +'"><span class="ui-icon ui-icon-circle-triangle-' + ( isRTL ? 'e' : 'w') + '">' + prevText + '</span></a>'));
		var nextText = this._get(inst, 'nextText');
		nextText = (!navigationAsDateFormat ? nextText : this.formatDate(nextText,
			this._daylightSavingAdjust(new Date(drawYear, drawMonth + stepMonths, 1)),
			this._getFormatConfig(inst)));
		var next = (this._canAdjustMonth(inst, +1, drawYear, drawMonth) ?
			'<a class="ui-datepicker-next ui-corner-all" onclick="DP_jQuery.datepicker._adjustDate(\'#' + inst.id + '\', +' + stepMonths + ', \'M\');"' +
			' title="' + nextText + '"><span class="ui-icon ui-icon-circle-triangle-' + ( isRTL ? 'w' : 'e') + '">' + nextText + '</span></a>' :
			(hideIfNoPrevNext ? '' : '<a class="ui-datepicker-next ui-corner-all ui-state-disabled" title="'+ nextText + '"><span class="ui-icon ui-icon-circle-triangle-' + ( isRTL ? 'w' : 'e') + '">' + nextText + '</span></a>'));
		var currentText = this._get(inst, 'currentText');
		var gotoDate = (this._get(inst, 'gotoCurrent') && inst.currentDay ? currentDate : today);
		currentText = (!navigationAsDateFormat ? currentText :
			this.formatDate(currentText, gotoDate, this._getFormatConfig(inst)));
		var controls = (!inst.inline ? '<button type="button" class="ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all" onclick="DP_jQuery.datepicker._hideDatepicker();">' + this._get(inst, 'closeText') + '</button>' : '');
		var buttonPanel = (showButtonPanel) ? '<div class="ui-datepicker-buttonpane ui-widget-content">' + (isRTL ? controls : '') +
			(this._isInRange(inst, gotoDate) ? '<button type="button" class="ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all" onclick="DP_jQuery.datepicker._gotoToday(\'#' + inst.id + '\');"' +
			'>' + currentText + '</button>' : '') + (isRTL ? '' : controls) + '</div>' : '';
		var firstDay = parseInt(this._get(inst, 'firstDay'),10);
		firstDay = (isNaN(firstDay) ? 0 : firstDay);
		var dayNames = this._get(inst, 'dayNames');
		var dayNamesShort = this._get(inst, 'dayNamesShort');
		var dayNamesMin = this._get(inst, 'dayNamesMin');
		var monthNames = this._get(inst, 'monthNames');
		var monthNamesShort = this._get(inst, 'monthNamesShort');
		var beforeShowDay = this._get(inst, 'beforeShowDay');
		var showOtherMonths = this._get(inst, 'showOtherMonths');
		var calculateWeek = this._get(inst, 'calculateWeek') || this.iso8601Week;
		var endDate = inst.endDay ? this._daylightSavingAdjust(
			new Date(inst.endYear, inst.endMonth, inst.endDay)) : currentDate;
		var defaultDate = this._getDefaultDate(inst);
		var html = '';
		for (var row = 0; row < numMonths[0]; row++) {
			var group = '';
			for (var col = 0; col < numMonths[1]; col++) {
				var selectedDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, inst.selectedDay));
				var cornerClass = ' ui-corner-all';
				var calender = '';
				if (isMultiMonth) {
					calender += '<div class="ui-datepicker-group ui-datepicker-group-';
					switch (col) {
						case 0: calender += 'first'; cornerClass = ' ui-corner-' + (isRTL ? 'right' : 'left'); break;
						case numMonths[1]-1: calender += 'last'; cornerClass = ' ui-corner-' + (isRTL ? 'left' : 'right'); break;
						default: calender += 'middle'; cornerClass = ''; break;
					}
					calender += '">';
				}
				calender += '<div class="ui-datepicker-header ui-widget-header ui-helper-clearfix' + cornerClass + '">' +
					(/all|left/.test(cornerClass) && row == 0 ? (isRTL ? next : prev) : '') +
					(/all|right/.test(cornerClass) && row == 0 ? (isRTL ? prev : next) : '') +
					this._generateMonthYearHeader(inst, drawMonth, drawYear, minDate, maxDate,
					selectedDate, row > 0 || col > 0, monthNames, monthNamesShort) + // draw month headers
					'</div><table class="ui-datepicker-calendar"><thead>' +
					'<tr>';
				var thead = '';
				for (var dow = 0; dow < 7; dow++) { // days of the week
					var day = (dow + firstDay) % 7;
					thead += '<th' + ((dow + firstDay + 6) % 7 >= 5 ? ' class="ui-datepicker-week-end"' : '') + '>' +
						'<span title="' + dayNames[day] + '">' + dayNamesMin[day] + '</span></th>';
				}
				calender += thead + '</tr></thead><tbody>';
				var daysInMonth = this._getDaysInMonth(drawYear, drawMonth);
				if (drawYear == inst.selectedYear && drawMonth == inst.selectedMonth)
					inst.selectedDay = Math.min(inst.selectedDay, daysInMonth);
				var leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7;
				var numRows = (isMultiMonth ? 6 : Math.ceil((leadDays + daysInMonth) / 7)); // calculate the number of rows to generate
				var printDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1 - leadDays));
				for (var dRow = 0; dRow < numRows; dRow++) { // create date picker rows
					calender += '<tr>';
					var tbody = '';
					for (var dow = 0; dow < 7; dow++) { // create date picker days
						var daySettings = (beforeShowDay ?
							beforeShowDay.apply((inst.input ? inst.input[0] : null), [printDate]) : [true, '']);
						var otherMonth = (printDate.getMonth() != drawMonth);
						var unselectable = otherMonth || !daySettings[0] ||
							(minDate && printDate < minDate) || (maxDate && printDate > maxDate);
						tbody += '<td class="' +
							((dow + firstDay + 6) % 7 >= 5 ? ' ui-datepicker-week-end' : '') + // highlight weekends
							(otherMonth ? ' ui-datepicker-other-month' : '') + // highlight days from other months
							((printDate.getTime() == selectedDate.getTime() && drawMonth == inst.selectedMonth && inst._keyEvent) || // user pressed key
							(defaultDate.getTime() == printDate.getTime() && defaultDate.getTime() == selectedDate.getTime()) ?
							// or defaultDate is current printedDate and defaultDate is selectedDate
							' ' + this._dayOverClass : '') + // highlight selected day
							(unselectable ? ' ' + this._unselectableClass + ' ui-state-disabled': '') +  // highlight unselectable days
							(otherMonth && !showOtherMonths ? '' : ' ' + daySettings[1] + // highlight custom dates
							(printDate.getTime() >= currentDate.getTime() && printDate.getTime() <= endDate.getTime() ? // in current range
							' ' + this._currentClass : '') + // highlight selected day
							(printDate.getTime() == today.getTime() ? ' ui-datepicker-today' : '')) + '"' + // highlight today (if different)
							((!otherMonth || showOtherMonths) && daySettings[2] ? ' title="' + daySettings[2] + '"' : '') + // cell title
							(unselectable ? '' : ' onclick="DP_jQuery.datepicker._selectDay(\'#' +
							inst.id + '\',' + drawMonth + ',' + drawYear + ', this);return false;"') + '>' + // actions
							(otherMonth ? (showOtherMonths ? printDate.getDate() : '&#xa0;') : // display for other months
							(unselectable ? '<span class="ui-state-default">' + printDate.getDate() + '</span>' : '<a class="ui-state-default' +
							(printDate.getTime() == today.getTime() ? ' ui-state-highlight' : '') +
							(printDate.getTime() >= currentDate.getTime() && printDate.getTime() <= endDate.getTime() ? // in current range
							' ui-state-active' : '') + // highlight selected day
							'" href="#">' + printDate.getDate() + '</a>')) + '</td>'; // display for this month
						printDate.setDate(printDate.getDate() + 1);
						printDate = this._daylightSavingAdjust(printDate);
					}
					calender += tbody + '</tr>';
				}
				drawMonth++;
				if (drawMonth > 11) {
					drawMonth = 0;
					drawYear++;
				}
				calender += '</tbody></table>' + (isMultiMonth ? '</div>' + 
							((numMonths[0] > 0 && col == numMonths[1]-1) ? '<div class="ui-datepicker-row-break"></div>' : '') : '');
				group += calender;
			}
			html += group;
		}
		html += buttonPanel + ($.browser.msie && parseInt($.browser.version,10) < 7 && !inst.inline ?
			'<iframe src="javascript:false;" class="ui-datepicker-cover" frameborder="0"></iframe>' : '');
		inst._keyEvent = false;
		return html;
	},

	/* Generate the month and year header. */
	_generateMonthYearHeader: function(inst, drawMonth, drawYear, minDate, maxDate,
			selectedDate, secondary, monthNames, monthNamesShort) {
		minDate = (inst.rangeStart && minDate && selectedDate < minDate ? selectedDate : minDate);
		var changeMonth = this._get(inst, 'changeMonth');
		var changeYear = this._get(inst, 'changeYear');
		var showMonthAfterYear = this._get(inst, 'showMonthAfterYear');
		var html = '<div class="ui-datepicker-title">';
		var monthHtml = '';
		// month selection
		if (secondary || !changeMonth)
			monthHtml += '<span class="ui-datepicker-month">' + monthNames[drawMonth] + '</span> ';
		else {
			var inMinYear = (minDate && minDate.getFullYear() == drawYear);
			var inMaxYear = (maxDate && maxDate.getFullYear() == drawYear);
			monthHtml += '<select class="ui-datepicker-month" ' +
				'onchange="DP_jQuery.datepicker._selectMonthYear(\'#' + inst.id + '\', this, \'M\');" ' +
				'onclick="DP_jQuery.datepicker._clickMonthYear(\'#' + inst.id + '\');"' +
			 	'>';
			for (var month = 0; month < 12; month++) {
				if ((!inMinYear || month >= minDate.getMonth()) &&
						(!inMaxYear || month <= maxDate.getMonth()))
					monthHtml += '<option value="' + month + '"' +
						(month == drawMonth ? ' selected="selected"' : '') +
						'>' + monthNamesShort[month] + '</option>';
			}
			monthHtml += '</select>';
		}
		if (!showMonthAfterYear)
			html += monthHtml + ((secondary || changeMonth || changeYear) && (!(changeMonth && changeYear)) ? '&#xa0;' : '');
		// year selection
		if (secondary || !changeYear)
			html += '<span class="ui-datepicker-year">' + drawYear + '</span>';
		else {
			// determine range of years to display
			var years = this._get(inst, 'yearRange').split(':');
			var year = 0;
			var endYear = 0;
			if (years.length != 2) {
				year = drawYear - 10;
				endYear = drawYear + 10;
			} else if (years[0].charAt(0) == '+' || years[0].charAt(0) == '-') {
				year = drawYear + parseInt(years[0], 10);
				endYear = drawYear + parseInt(years[1], 10);
			} else {
				year = parseInt(years[0], 10);
				endYear = parseInt(years[1], 10);
			}
			year = (minDate ? Math.max(year, minDate.getFullYear()) : year);
			endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear);
			html += '<select class="ui-datepicker-year" ' +
				'onchange="DP_jQuery.datepicker._selectMonthYear(\'#' + inst.id + '\', this, \'Y\');" ' +
				'onclick="DP_jQuery.datepicker._clickMonthYear(\'#' + inst.id + '\');"' +
				'>';
			for (; year <= endYear; year++) {
				html += '<option value="' + year + '"' +
					(year == drawYear ? ' selected="selected"' : '') +
					'>' + year + '</option>';
			}
			html += '</select>';
		}
		if (showMonthAfterYear)
			html += (secondary || changeMonth || changeYear ? '&#xa0;' : '') + monthHtml;
		html += '</div>'; // Close datepicker_header
		return html;
	},

	/* Adjust one of the date sub-fields. */
	_adjustInstDate: function(inst, offset, period) {
		var year = inst.drawYear + (period == 'Y' ? offset : 0);
		var month = inst.drawMonth + (period == 'M' ? offset : 0);
		var day = Math.min(inst.selectedDay, this._getDaysInMonth(year, month)) +
			(period == 'D' ? offset : 0);
		var date = this._daylightSavingAdjust(new Date(year, month, day));
		// ensure it is within the bounds set
		var minDate = this._getMinMaxDate(inst, 'min', true);
		var maxDate = this._getMinMaxDate(inst, 'max');
		date = (minDate && date < minDate ? minDate : date);
		date = (maxDate && date > maxDate ? maxDate : date);
		inst.selectedDay = date.getDate();
		inst.drawMonth = inst.selectedMonth = date.getMonth();
		inst.drawYear = inst.selectedYear = date.getFullYear();
		if (period == 'M' || period == 'Y')
			this._notifyChange(inst);
	},

	/* Notify change of month/year. */
	_notifyChange: function(inst) {
		var onChange = this._get(inst, 'onChangeMonthYear');
		if (onChange)
			onChange.apply((inst.input ? inst.input[0] : null),
				[inst.selectedYear, inst.selectedMonth + 1, inst]);
	},

	/* Determine the number of months to show. */
	_getNumberOfMonths: function(inst) {
		var numMonths = this._get(inst, 'numberOfMonths');
		return (numMonths == null ? [1, 1] : (typeof numMonths == 'number' ? [1, numMonths] : numMonths));
	},

	/* Determine the current maximum date - ensure no time components are set - may be overridden for a range. */
	_getMinMaxDate: function(inst, minMax, checkRange) {
		var date = this._determineDate(this._get(inst, minMax + 'Date'), null);
		return (!checkRange || !inst.rangeStart ? date :
			(!date || inst.rangeStart > date ? inst.rangeStart : date));
	},

	/* Find the number of days in a given month. */
	_getDaysInMonth: function(year, month) {
		return 32 - new Date(year, month, 32).getDate();
	},

	/* Find the day of the week of the first of a month. */
	_getFirstDayOfMonth: function(year, month) {
		return new Date(year, month, 1).getDay();
	},

	/* Determines if we should allow a "next/prev" month display change. */
	_canAdjustMonth: function(inst, offset, curYear, curMonth) {
		var numMonths = this._getNumberOfMonths(inst);
		var date = this._daylightSavingAdjust(new Date(
			curYear, curMonth + (offset < 0 ? offset : numMonths[1]), 1));
		if (offset < 0)
			date.setDate(this._getDaysInMonth(date.getFullYear(), date.getMonth()));
		return this._isInRange(inst, date);
	},

	/* Is the given date in the accepted range? */
	_isInRange: function(inst, date) {
		// during range selection, use minimum of selected date and range start
		var newMinDate = (!inst.rangeStart ? null : this._daylightSavingAdjust(
			new Date(inst.selectedYear, inst.selectedMonth, inst.selectedDay)));
		newMinDate = (newMinDate && inst.rangeStart < newMinDate ? inst.rangeStart : newMinDate);
		var minDate = newMinDate || this._getMinMaxDate(inst, 'min');
		var maxDate = this._getMinMaxDate(inst, 'max');
		return ((!minDate || date >= minDate) && (!maxDate || date <= maxDate));
	},

	/* Provide the configuration settings for formatting/parsing. */
	_getFormatConfig: function(inst) {
		var shortYearCutoff = this._get(inst, 'shortYearCutoff');
		shortYearCutoff = (typeof shortYearCutoff != 'string' ? shortYearCutoff :
			new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10));
		return {shortYearCutoff: shortYearCutoff,
			dayNamesShort: this._get(inst, 'dayNamesShort'), dayNames: this._get(inst, 'dayNames'),
			monthNamesShort: this._get(inst, 'monthNamesShort'), monthNames: this._get(inst, 'monthNames')};
	},

	/* Format the given date for display. */
	_formatDate: function(inst, day, month, year) {
		if (!day) {
			inst.currentDay = inst.selectedDay;
			inst.currentMonth = inst.selectedMonth;
			inst.currentYear = inst.selectedYear;
		}
		var date = (day ? (typeof day == 'object' ? day :
			this._daylightSavingAdjust(new Date(year, month, day))) :
			this._daylightSavingAdjust(new Date(inst.currentYear, inst.currentMonth, inst.currentDay)));
		return this.formatDate(this._get(inst, 'dateFormat'), date, this._getFormatConfig(inst));
	}
});

/* jQuery extend now ignores nulls! */
function extendRemove(target, props) {
	$.extend(target, props);
	for (var name in props)
		if (props[name] == null || props[name] == undefined)
			target[name] = props[name];
	return target;
};

/* Determine whether an object is an array. */
function isArray(a) {
	return (a && (($.browser.safari && typeof a == 'object' && a.length) ||
		(a.constructor && a.constructor.toString().match(/\Array\(\)/))));
};

/* Invoke the datepicker functionality.
   @param  options  string - a command, optionally followed by additional parameters or
                    Object - settings for attaching new datepicker functionality
   @return  jQuery object */
$.fn.datepicker = function(options){

	/* Initialise the date picker. */
	if (!$.datepicker.initialized) {
		$(document).mousedown($.datepicker._checkExternalClick).
			find('body').append($.datepicker.dpDiv);
		$.datepicker.initialized = true;
	}

	var otherArgs = Array.prototype.slice.call(arguments, 1);
	if (typeof options == 'string' && (options == 'isDisabled' || options == 'getDate'))
		return $.datepicker['_' + options + 'Datepicker'].
			apply($.datepicker, [this[0]].concat(otherArgs));
	if (options == 'option' && arguments.length == 2 && typeof arguments[1] == 'string')
		return $.datepicker['_' + options + 'Datepicker'].
			apply($.datepicker, [this[0]].concat(otherArgs));
	return this.each(function() {
		typeof options == 'string' ?
			$.datepicker['_' + options + 'Datepicker'].
				apply($.datepicker, [this].concat(otherArgs)) :
			$.datepicker._attachDatepicker(this, options);
	});
};

$.datepicker = new Datepicker(); // singleton instance
$.datepicker.initialized = false;
$.datepicker.uuid = new Date().getTime();
$.datepicker.version = "1.7.2";

// Workaround for #4055
// Add another global to avoid noConflict issues with inline event handlers
window.DP_jQuery = $;

})(jQuery);


function uiAutocomplete(url, target, d, max, t, lim, callback){
  $t = $(target);
  var input = null;
  var button = null;
  $.widget("ui.combobox", {
    _create: function() {
      var self = this;
      var select = this.element.hide(),
      selected = select.children(":selected"),
      value = selected.val() ? selected.text() : "";
      input = $("<input />")
      .insertAfter(select)
      .val(value)
      .autocomplete({
        delay: d,
        minLength: max,
        source: function(request, response) {
          var values = [];
          $.ajaxSetup({ cache: false, async: false });
          $.getJSON(url, { q: request.term, limit: lim }, function(data) {
            if (data.IsOk) {
              $.each(data.list, function(i, val) {
                values.push({ value: val.Second, label: val.First, option: this });
              });
            }
          });
          response(values);
        },
        select: function(event, ui) {
          ui.item.option.selected = true;
          self._trigger("selected", event, {
            item: ui.item.option
          });
          callback.call(this, ui.item.label, ui.item.value);
        },
        change: function(event, ui) {
          if (!ui.item) {
            var matcher = new RegExp("^" + $.ui.autocomplete.escapeRegex($(this).val()) + "$", "i"),
          valid = false;
            select.children("option").each(function() {
              if ($(this).text().match(matcher)) {
                this.selected = valid = true;
                return false;
              }
            });
            if (!valid) {
              $(this).val("");
              select.val("");
              input.data("autocomplete").term = "";
              return false;
            }
          }
        },
        close: function(event, ui) {
        }
      }).addClass("ui-widget ui-corner-all");

      input.data("autocomplete")._renderItem = function(ul, item) {
        return $("<li></li>")
      .data("item.autocomplete", item)
      .append("<a>" + item.value + "</a>")
      .appendTo(ul);
      };

      var currentOrg = input.val();
      input.focus(function() {
        $(this).val("");
      }).blur(function() {
        $(this).val(currentOrg);
      });

      button = $("<button></button>")
    .attr("tabIndex", -1)
    .attr("title", t)
    .insertAfter(input)
    .button({
      icons: {
        primary: "ui-icon-triangle-1-s"
      },
      text: false
    })
      //.removeClass( "ui-corner-all" )
      //.addClass("ui-corner-right ui-button-icon" )
    .addClass("ui-button-icon")
    .css("margin-left", "2px")
    .click(function() {
      // close if already visible
      if (input.autocomplete("widget").is(":visible")) {
        input.autocomplete("close");
        return;
      }
      // pass empty string as value to search for, displaying all results
      input.autocomplete("search", "");
      input.focus();
    });
    }
    ,
    destroy: function() {
      this.input.remove();
      this.button.remove();
      this.element.show();
      $.Widget.prototype.destroy.call(this);
    }
  });
  
  jQuery(function(){jQuery($t).combobox();});
}
// requires jQuery

if(!('tangane' in this)) this.tangane = {};
/*** Constructeur de la classe ***
 * - param : delay avant que le sous menu disparaisse au rollout
 */
tangane.menu = function(delay){
	var self = this;
	$('.tabsmenucontent').hide();
	$('#mytabsmenu li').mouseover(function(){
		var index = $(this).prevAll().length;
		$('.tabsmenucontent:not(:eq('+index+'))').hide();
		$('.tabsmenucontent:eq('+index+')').show();
	});
	$('#navig').mouseleave(function(){
	});
}
/*** Methode qui vide le timer ***
 * - prend en param le timer id
 */
tangane.menu.prototype.clearhidetimer = function(timerid){
	if (timerid);
		clearTimeout(timerid);
}

function modal(){
	//When you click on a link with class of poplight and the href starts with a # 
	$('a.poplight').click(function() {
		var popID = $(this).attr('name'); //Get Popup Name
		var popURL = $(this).attr('rel'); //Get Popup href to define size

		//Pull Query & Variables from href URL
		var query= popURL.split('?');
		var dim= query[1].split('&');
		var popWidth = dim[0].split('=')[1]; //Gets the first query string value

		//Fade in the Popup and add close button
		$('#' + popID).fadeIn().css({ 'width': Number( popWidth ) }).prepend(/*'<div id="action"><a href="#" class="close button button-gray">\
					<span class="button-inner"><span class="button-glyph"></span>Fermer</span>\
                  </a></div>'*/);
		
		//Define margin for center alignment (vertical + horizontal) - we add 80 to the height/width to accomodate for the padding + border width defined in the css
		var popMargTop = ($('#' + popID).height() + 80) / 2;
		var popMargLeft = ($('#' + popID).width() + 80) / 2;
		
		//Apply Margin to Popup
		$('#' + popID).css({ 
			'margin-top' : -popMargTop,
			'margin-left' : -popMargLeft
		});
		
		//Fade in Background
		$('body').append('<div id="fade"></div>'); //Add the fade layer to bottom of the body tag.
		$('#fade').css({'filter' : 'alpha(opacity=80)'}).fadeIn(); //Fade in the fade layer 
		
		return false;
	});
	
	//Close Popups and Fade Layer
	$('a.close').live('click', function() { //When clicking on the close or fade layer...
	  	$('#fade , .popup_block').fadeOut(); //fade them both out
		$('#fade').remove();
		//$('a.close').remove();
		return false;
	});

}

// JavaScript Document

// Correctif de l'affichage des hover dans IE5.5 et IE6.


sfHover = function() {
	var navig = document.getElementById("navig");
	if (!navig) return;
	var sfEls = navig.getElementsByTagName("li");
	for (var i=0; i<sfEls.length; i++) {
		sfEls[i].onmouseover=function() {
			this.className+=" sfhover";
		}
		sfEls[i].onmouseout=function() {
			this.className=this.className.replace(new RegExp(" sfhover\\b"), "");
		}
	}
}
if (window.attachEvent) window.attachEvent("onload", sfHover);
// JavaScript Document

$(document).ready(function(){
	
	$('.ong_orange ').mouseover(function(){ 
		$('.tabsmenucontentclass').css ('background', 'url(content/img/bgSub2.png) repeat-x  0 0'); 
	});
	$('.ong').mouseover(function(){
	$('.tabsmenucontentclass').css('background', 'url(content/img/bgSub.png) repeat-x  0 0'); 
	});
	$('.last').mouseover(function(){
	$('.tabsmenucontentclass').css('background', 'url(content/img/bgSub3.png) repeat-x  0 0'); 
	});
});

	var tooltip = false;
	var tooltipShadow = false;
	var shadowSize = 0;
	var tooltipMaxWidth = 200;
	var tooltipMinWidth = 100;
	var iframe = false;
	var myElem = null;
	//var tooltip_is_msie = (navigator.userAgent.indexOf('MSIE')>=0 && navigator.userAgent.indexOf('opera')==-1 && document.all)?true:false;

	function showTooltip(e,tooltipTxt)	{
		
		var bodyWidth = Math.max(document.body.clientWidth,document.documentElement.clientWidth) - 20;
	
		if(!tooltip){
			tooltip = document.createElement('DIV');
			tooltip.id = 'tooltip';
			tooltipShadow = document.createElement('DIV');
			tooltipShadow.id = 'tooltipShadow';
			
			document.body.appendChild(tooltip);
			document.body.appendChild(tooltipShadow);	
			/*
			if(tooltip_is_msie){
				iframe = document.createElement('IFRAME');
				iframe.frameborder='5';
				iframe.style.backgroundColor='#FFFFFF';
				iframe.src = '#'; 	
				iframe.style.zIndex = 100;
				iframe.style.position = 'absolute';
				document.body.appendChild(iframe);
			}*/
			
		}
		
		tooltip.style.display='block';
		tooltipShadow.style.display='block';
		//if(tooltip_is_msie)iframe.style.display='block';
		
		var st = Math.max(document.body.scrollTop,document.documentElement.scrollTop);
		if(navigator.userAgent.toLowerCase().indexOf('safari')>=0)st=0; 
		var leftPos = e.clientX + 10;
		
		tooltip.style.width = null;	// Reset style width if it's set 
		tooltip.innerHTML = tooltipTxt;
		tooltip.style.left = leftPos + 'px';
		tooltip.style.top = e.clientY + 10 + st + 'px';

		
		tooltipShadow.style.left =  leftPos + shadowSize + 'px';
		tooltipShadow.style.top = e.clientY + 10 + st + shadowSize + 'px';
		
		if(tooltip.offsetWidth>tooltipMaxWidth){	/* Exceeding max width of tooltip ? */
			tooltip.style.width = tooltipMaxWidth + 'px';
		}
		
		var tooltipWidth =  tooltip.offsetWidth;		
		if(tooltipWidth< tooltipMinWidth)tooltipWidth =  tooltipMinWidth;


		//tooltip.style.width = tooltipWidth + 'px';
		tooltip.style.width = 'auto';
		 //tooltipShadow.style.width =  tooltip.offsetWidth + 'px';
		 //tooltipShadow.style.height =  tooltip.offsetHeight + 'px';		
		
		if((leftPos + tooltipWidth)>bodyWidth){
			 tooltip.style.left = ( tooltipShadow.style.left.replace('px','') - ((leftPos + tooltipWidth)-bodyWidth)) + 'px';
			 tooltipShadow.style.left = ( tooltipShadow.style.left.replace('px','') - ((leftPos + tooltipWidth)-bodyWidth) +  shadowSize) + 'px';
		}
		
		/*if(tooltip_is_msie){
			 iframe.style.left =  tooltip.style.left;
			 iframe.style.top =  tooltip.style.top;
			 iframe.style.width =  tooltip.offsetWidth + 'px';
			 iframe.style.height =  tooltip.offsetHeight + 'px';
		
		}*/
				
	}
	function hideTooltip()	{
		 tooltip.style.display='none';
		 tooltipShadow.style.display='none';		
		//if(tooltip_is_msie) iframe.style.display='none';		
	}

// ------------------ Variables
var input_ht = 'input[rel="ht"]';
var input_ttc = 'input[rel="ttc"]';
var select_tva = 'select[rel="tva"]';
var select_tva_selected = 'select[rel="tva"] option:selected';
var input_tva = 'input[rel="tvavalue"]';

var delTvaLink = '.delTvaLink';
var addTvaLink = '.addTvaLink';

var select_tva_option = select_tva + ' option';
var select_tva_first = select_tva + ':first';
var onBlur = input_ttc + ', ' + input_ht + ', ' + input_tva;
// ------------------ Fin variables

function tva() {

	// Si on a une seule ligne de TVA et que le taux n'est pas vide, on affiche le lien.
    if ($(select_tva).size() < $("#nbtva").html() && $(select_tva_first).val() != 0)
        showTvaLink();
    else
        hideTvaLink();

	// Fonctionnement identique lors du changement d'une selectBox.
      $(select_tva).change(function() {
      if ($(this).val() != 0 && $(select_tva).size() < $("#nbtva").html()) {
          showTvaLink();
          calculTVA();
        } else {
          // Si une TVA est positionn�e sur "vide" on supprime les autres champs.
          if ($(select_tva).size() > 1 && $(this).val() == 0) {
            $(select_tva).each(function(i, item) {
              if (item.value != 0) {
                $(this).parent().parent().parent().remove();
                $(delTvaLink).hide();
              }
            });
          }
          hideTvaLink();
          goodTTC();
        }
      });

	// On v�rifie le montant TTC en quittant les champs TTC, TVA ou HT
	$(onBlur).keyup(function() {
		verifTTC(getTTC());
	});

	$(delTvaLink).click(function() {
	  $(this).parent().parent().parent().remove();
	  showTvaLink();
	  if ($(select_tva).size() == 1)
	    $(delTvaLink).hide();
	  verifTTC(getTTC());
	});

	verifTTC(getTTC());
}
//TODO:TRANSLATE : V�rifier compatibilit� entre les diff�rents notations de TVA
function calculTVA() {
    var ht = $(input_ht).val();
    if (ht != "" && $(select_tva_selected).val() != 0 && $(select_tva).size() == 1) {
		ht = textToNumber(ht);

		var tva = $(select_tva_selected).text();
		tva = tva.substring(0, tva.length - 1);
		tva = textToNumber(tva);

		var res = myRound(ht * tva / 100);
		$(input_tva).val(res.toString().replace(".", ","));

		verifTTC(getTTC());
	}
}

function verifTTC(val) {
  var ttc = $(input_ttc).val();
  var ht = $(input_ht).val();
	if (ttc != "" && val != null && ht != "") {
		ttc = textToNumber(ttc);
		if (ttc != val)
			errorTTC();
		else
			goodTTC();
	}
}

// Fonction appel�e lors d'un click sur la petite calculatrice.
//
function calcul() {
    if ($(input_ht).val() != 0 && $(input_ht).val() != null)
        calculTTC();
    else if (($(input_ht).val() == 0 || $(input_ht).val() == null)
            && $(input_ttc).val() != 0
            && $(input_ttc).val() != null
            && $(select_tva).size() == 1)
      calculHT();

    if ($(input_ttc).val() != 0 && $(input_ttc).val() != null && $(select_tva).size() == 1)
      getTauxTva();     
}

function calculHT() {
  var ht = getHT();
  var ttc = $(input_ttc).val();
  ttc = textToNumber(ttc);
  if (ht != null) {
    $(input_ht).val(ht.toString().replace(".", ","));
    res = myRound(ttc - ht);
    $(input_tva).val(res.toString().replace(".", ","));
    verifTTC(ttc);
  }
}

function calculTTC() {
  if ($(select_tva_selected).val() != 0 && $(select_tva).size() == 1) {
      calculTVA();
  }
  
	var ttc = getTTC();
	$(input_ttc).val(ttc.toString().replace(".", ","));
	verifTTC(ttc);
}

function getTauxTva() {
  var ht = $(input_ht).val();
  ht = textToNumber(ht);

  var previousTva = $(input_tva).val();
  previousTva = textToNumber(previousTva);
  
  var tva = myRound((previousTva / ht) * 100);

  $(select_tva_option).each(function(i, item) {
    if ($(item).text() == tva.toString().replace(".", ",") + "%") {
      $(item).attr("selected", "selected");
      showTvaLink();
    }
  });
}

// Convertion de la valeur d'un champ en float.
function textToNumber(value) {
	if (value != null) {
		var val = value.replace(/ /g, "");
		val = parseFloat(val.replace(",", "."));
		return val;
	}
}

// Calcule et retourne la valeur TTC � partir des TVA et du HT
//
function getTTC() {
    var ht = $(input_ht).val();
	var tvaSum = 0;
	if (ht != "") {
		ht = textToNumber(ht);
		$(input_tva).each(function(i, item) {
			if (item.value != "")
				tvaSum += textToNumber(item.value);
		});
		var res = myRound(ht + tvaSum);
		return res;
	}
}

// Calcule et retourne la valeur HT � partir de la TVA et du TTC
//
function getHT() {
  var res = null;
  var ttc = $(input_ttc).val();
  var tva_value = $(input_tva).val();
  var tva = $(select_tva_selected).text();
  ttc = textToNumber(ttc);
  
  if (ttc != "" && $(select_tva_selected).val() != 0) {
    tva = tva.substring(0, tva.length - 1);
    tva = textToNumber(tva);
    res = myRound(100 * ttc / (100 + tva));
  } else if (ttc != "" && $(select_tva_selected).val() == 0 && tva_value != "") {
    tva_value = textToNumber(tva_value);
    res = myRound(ttc - tva_value);
  }
  return res;
}

function myRound(val) {
    var myVal = Math.round(val * 100) / 100;
    return myVal;
}

function hideTvaLink() {
    $(addTvaLink).hide();
}

function showTvaLink() {
  showTvaLinkLast();
}
function showTvaLinkLast() {
  hideTvaLink();
  $(addTvaLink+":last").show();
}

function errorTTC() {
    $(input_ttc).css("font-weight", "bold");
    $(input_ttc).css("color", "red");
}

function goodTTC() {
    $(input_ttc).css("font-weight", "normal");
    $(input_ttc).css("color", "black");
}


Ext.override(Ext.data.GroupingStore, {

  clearGrouping: function (noReload) {
    this.groupField = false;
    if (this.remoteGroup) {
      if (this.baseParams) {
        delete this.baseParams.groupBy;
      }
      var lo = this.lastOptions;
      if (lo && lo.params) {
        delete lo.params.groupBy;
      }

      console.debug(this.groupField);
      console.debug(this.baseParams);
      console.debug(this.lastOptions);

      if (!noReload) this.reload();
    } else {
      this.applySort();
      this.fireEvent('datachanged', this);
    }
  },

  groupBy: function (field, forceRegroup, direction, noReload) {
    direction = direction ? (String(direction).toUpperCase() == 'DESC' ? 'DESC' : 'ASC') : this.groupDir;
    if (this.groupField == field && this.groupDir == direction && !forceRegroup) {
      return;
    }

    console.debug(this.groupDir);

    this.groupField = field;
    this.groupDir = direction;
    this.applyGroupField();
    if (this.groupOnSort) {
      this.sort(field, direction);
      return;
    }
    if (this.remoteGroup) {
      if (!noReload) this.reload();
    } else {
      var si = this.sortInfo || {};
      if (si.field != field || si.direction != direction) {
        this.applySort();
      } else {
        this.sortData(field, direction);
      }
      this.fireEvent('datachanged', this);
    }
  }
});
Ext.override(Ext.grid.GridPanel, {
  applyState: function (state) {
    var cm = this.colModel,
            cs = state.columns,
            store = this.store,
            s,
            c,
            colIndex;

    if (cs) {
      for (var i = 0, len = cs.length; i < len; i++) {
        s = cs[i];
        c = cm.getColumnById(s.id);
        if (c) {
          colIndex = cm.getIndexById(s.id);
          cm.setState(colIndex, {
            hidden: s.hidden,
            width: s.width,
            sortable: s.sortable
          });
          if (colIndex != i) {
            cm.moveColumn(colIndex, i);
          }
        }
      }
    }
    if (store) {
      s = state.sort;
      if (s) {
        store[store.remoteSort ? 'setDefaultSort' : 'sort'](s.field, s.direction);
      }
      s = state.group;
      if (store.groupBy) {
        if (s) {
          store.groupBy(s, null, null, true);
        } else {
          store.clearGrouping(true);
        }
      }
    }
    var o = Ext.apply({}, state);
    delete o.columns;
    delete o.sort;
    Ext.grid.GridPanel.superclass.applyState.call(this, o);
  }
});
Ext.namespace('Ext.ux.plugins');

Ext.ux.plugins.currency = function(config) {
  Ext.apply(this, config);
};

Ext.extend(Ext.ux.plugins.currency, Ext.util.Observable, {

  init: function(textField) {
    Ext.apply(textField, {
      onRender: textField.onRender.createSequence(function() {
        this.currencyConfig = Ext.apply(
                    {
                      symbolBeforeAmount: false,
                      decimalPrecision: 2,
                      currencySymbol: '\u20AC', //  use '\u20AC' for euro sign (= unicode)
                      decimalSeparator: ',',
                      thousandsSeparator: '\u00A0',
                      negativeAmountClass: ''
                    }, this.currencyConfig);



        this.maskRe = new RegExp('[\\-\\d\\' + this.currencyConfig.decimalSeparator + (this.currencyConfig.thousandsSeparator.trim() !== "" ? "\\" + this.currencyConfig.thousandsSeparator : "") + ']', 'i');
        var name = this.name || this.el.dom.name;
        this.validator = function(theVal) {
          if (isNaN(parseFloat(this.formatHiddenValue(theVal))) && theVal != "") {
            return this.currencyConfig.invalidAmountText || false;
          }
          return true;
        };
        this.hiddenField = this.el.insertSibling({
          tag: 'input'
                    , type: 'hidden'
                    , name: name
                    , value: this.formatHiddenValue(this.value)
        });
        this.hiddenName = name; // otherwise field is not found by BasicForm::findField
        this.el.dom.removeAttribute('name');
        this.el.on({
          keyup: { scope: this, fn: this.updateHidden },
          focus: { scope: this, fn: this.cleanForEdit },
          blur: { scope: this, fn: this.updateShown },
          render: { scope: this, fn: this.updateShown }
        }, Ext.isIE ? 'after' : 'before');

        this.setValue = this.setValue.createSequence(this.updateHidden);
        this.setValue(this.getDisplay(this.value));
      }),
      updateShown: function() {
        if (this.isValid() && this.getValue().trim() !== "") {
          var newValue = "";
          var currentValue = parseFloat(this.formatHiddenValue(this.getValue()));

          newValue = this.getDisplay(currentValue);

          if (newValue < 0) {
            this.el.addClass(this.currencyConfig.negativeAmountClass);
          }
          else {
            this.el.removeClass(this.currencyConfig.negativeAmountClass);
          }
          this.setValue(newValue);
          this.updateHidden();
        }
      },
      updateHidden: function() {
        this.hiddenField.dom.value = (this.isValid() ? this.formatHiddenValue(this.getValue()) : "");
      },
      formatHiddenValue: function(rawAmount) {
        if (!rawAmount) {
          return null;
        }
        rawAmount = String(rawAmount).replace(this.currencyConfig.currencySymbol, '');
        //CORRECTION DU PLUGIN : sur les grand nombres, le s�parateur apparait dans la value de l'input hiden
        //rawAmount = String(rawAmount).replace(this.currencyConfig.thousandsSeparator, '');
        //Autheur : M Jusserand --> cf. http://www.sencha.com/forum/showthread.php?105951-Form-field-currency-plugin/page2
        var replacer = RegExp(this.currencyConfig.thousandsSeparator, 'g');
        rawAmount = String(rawAmount).replace(replacer, '');
        //fin CORRECTION
        rawAmount = String(rawAmount).replace(this.currencyConfig.decimalSeparator, '.');
        return rawAmount.trim();
      },
      cleanForEdit: function(textField) {
        this.setValue(String(this.getValue()).replace(this.currencyConfig.currencySymbol, '').trim());
        this.setValue(String(this.getValue()).replace(this.currencyConfig.thousandsSeparator, '').trim());
      },
      getDisplay: function(value) {
        return Ext.util.Format.money(value, true,
                                     this.currencyConfig.decimalPrecision,
                                     this.currencyConfig.decimalSeparator,
                                     this.currencyConfig.thousandsSeparator,
                                     this.currencyConfig.currencySymbol,
                                     !this.currencyConfig.symbolBeforeAmount);
      }
    });
  }


});
Ext.preg('currency', Ext.ux.plugins.currency);


if (Ext.util.Format) {
  if (!Ext.util.Format.money) {
    Ext.util.Format.money = function(n, hasSpace, dp, dSeparator, tSeparator, symbol, rightPosition) {
      if (!n)
        return "";
      var spaceText = hasSpace ? " " : "";
      dp = Math.abs(dp) + 1 ? dp : 2;
      dSeparator = dSeparator || ".";
      tSeparator = tSeparator || ",";
      symbol = symbol || "$";
      rightPosition = rightPosition || false;

      var m = /(\d+)(?:(\.\d+)|)/.exec(n + ""),
      x = m[1].length > 3 ? m[1].length % 3 : 0;

      var v = (n < 0 ? '-' : '') // preserve minus sign
      + (x ? m[1].substr(0, x) + tSeparator : "")
      + m[1].substr(x).replace(/(\d{3})(?=\d)/g, "$1" + tSeparator)
      + (dp ? dSeparator + (+m[2] || 0).toFixed(dp).substr(2) : "");

      return rightPosition ? v + spaceText + symbol : symbol + spaceText + v;
    };
  }
}
/**********************************************************************************************
 * JAFFA - Java Application Framework For All - Copyright (C) 2008 JAFFA Development Group
 *
 * This library is free software; you can redistribute it and/or modify it under the terms
 * of the GNU Lesser General Public License (version 2.1 any later).
 *
 * See http://jaffa.sourceforge.net/site/legal.html for more details.
 *********************************************************************************************/

/** Based on Original Work found at http://extjs.com/forum/showthread.php?p=203828#post203828
 *
 * @author chander, PaulE
 */
Ext.namespace("Ext.ux.grid");

Ext.ux.grid.MultiGroupingPanel = function(config) {
    config = config||{};
    config.tbar = new Ext.Toolbar({id:'grid-tbr'}); //FIXME is this hardcoded id needed?
    Ext.ux.grid.MultiGroupingPanel.superclass.constructor.call(this, config);
    //console.debug("Create MultiGroupingPanel",config);
};
Ext.extend(Ext.ux.grid.MultiGroupingPanel, Ext.grid.GridPanel, {

   initComponent : function(){
     //console.debug("MultiGroupingPanel.initComponent",this);
     Ext.ux.grid.MultiGroupingPanel.superclass.initComponent.call(this);
     
     // Initialise DragZone
     this.on("render", this.setUpDragging, this);
   }
    
  ,setUpDragging: function() {
     //console.debug("SetUpDragging", this);
     this.dragZone = new Ext.dd.DragZone(this.getTopToolbar().getEl(), {
       ddGroup:"grid-body" + this.getGridEl().id //FIXME - does this need to be unique to support multiple independant panels on the same page
      ,panel:this 
      ,scroll:false
       // @todo - docs
      ,onInitDrag : function(e) {
         // alert('init');
         var clone = this.dragData.ddel;
         clone.id = Ext.id('ven'); //FIXME??
         // clone.class='x-btn button';
         this.proxy.update(clone);
         return true;
       }

       // @todo - docs
      ,getDragData: function(e) {
         var target = Ext.get(e.getTarget().id);
         if(target.hasClass('x-toolbar x-small-editor')) {
           return false;
         }
           
         d = e.getTarget().cloneNode(true);
         d.id = Ext.id();
         console.debug("getDragData",this, target);
           
         this.dragData = {
           repairXY: Ext.fly(target).getXY(),
           ddel: d,
           btn:e.getTarget()
         };
         return this.dragData;
       }

       //Provide coordinates for the proxy to slide back to on failed drag.
       //This is the original XY coordinates of the draggable element.
      ,getRepairXY: function() {
         return this.dragData.repairXY;
       }
     });
     
     // This is the target when columns are dropped onto the toolbar (ie added to the group)
     this.dropTarget2s = new Ext.dd.DropTarget(this.getTopToolbar().getEl(), {
       ddGroup: "gridHeader" + this.getGridEl().id
      ,panel:this 
      ,notifyDrop: function(dd, e, data) {
         console.debug("Adding Filter", data);
         var btname= this.panel.getColumnModel().getDataIndex( this.panel.getView().getCellIndex(data.header));
         this.panel.store.groupBy(btname);
         return true;
       }
     });

     // This is the target when columns are dropped onto the grid (ie removed from the group)
     this.dropTarget22s = new Ext.dd.DropTarget(this.getView().el.dom.childNodes[0].childNodes[1], {
       ddGroup: "grid-body" + this.getGridEl().id  //FIXME - does this need to be unique to support multiple independant panels on the same page
      ,panel:this 
      ,notifyDrop: function(dd, e, data) {
         var txt = Ext.get(data.btn).dom.innerHTML;
         var tb = this.panel.getTopToolbar();
         console.debug("Removing Filter", txt);
         var bidx = tb.items.findIndexBy(function(b) {
           console.debug("Match button ",b.text);
           return b.text==txt;
         },this);
         console.debug("Found matching button", bidx);
         if(bidx<0) return; // Error!
         var fld = tb.items.get(bidx).fieldName;
         
         // Remove from toolbar
         Ext.removeNode(Ext.getDom(tb.items.get(bidx).id));
         if(bidx>0) Ext.removeNode(Ext.getDom(tb.items.get(bidx-1).id));;

         console.debug("Remove button", fld);
         //console.dir(button);
         var cidx=this.panel.view.cm.findColumnIndex(fld);
         
         if(cidx<0)
           console.error("Can't find column for field ", fld);
         
         this.panel.view.cm.setHidden(cidx, false);

         //Ext.removeNode(Ext.getDom(data.btn.id));

         // Remove this group from the groupField array
         // @todo - replace with method on store
         // this.panel.store.removeGroupField(fld);
         var temp=[];
         for(var i=this.panel.store.groupField.length-1;i>=0;i--) {
           if(this.panel.store.groupField[i]==fld) {
             this.panel.store.groupField.pop();
             break;
           }
           temp.push(this.panel.store.groupField[i]);
           this.panel.store.groupField.pop();
         }

         for(var i=temp.length-1;i>=0;i--) {
           this.panel.store.groupField.push(temp[i]);
         }

         if(this.panel.store.groupField.length==0)
           this.panel.store.groupField=false;

         this.panel.store.fireEvent('datachanged', this);
         return true;
       }
     }); 
    }
});


/**********************************************************************************************
 * JAFFA - Java Application Framework For All - Copyright (C) 2008 JAFFA Development Group
 *
 * This library is free software; you can redistribute it and/or modify it under the terms
 * of the GNU Lesser General Public License (version 2.1 any later).
 *
 * See http://jaffa.sourceforge.net/site/legal.html for more details.
 *********************************************************************************************/

/** Based on Original Work found at http://extjs.com/forum/showthread.php?p=203828#post203828
 *
 * @author chander, PaulE
 */
Ext.namespace("Ext.ux.grid");

Ext.ux.grid.MultiGroupingStore = Ext.extend(Ext.data.GroupingStore, {

    constructor: function(config){
        Ext.ux.grid.MultiGroupingStore.superclass.constructor.apply(this, arguments);
    }

   ,sortInfo: []
    
   ,sort: function(field, dir){
        //  alert('sort '+ field);
        var f = [];
        if (Ext.isArray(field)) {
            for (var i = 0, len = field.length; i < len; ++i) {
                f.push(this.fields.get(field[i]));
            }
        } else {
            f.push(this.fields.get(field));
        }
        
        if (f.length < 1) {
            return false;
        }
        
        if (!dir) {
            if (this.sortInfo && this.sortInfo.length > 0 && this.sortInfo[0].field == f[0].name) { // toggle sort dir
                dir = (this.sortToggle[f[0].name] || "ASC").toggle("ASC", "DESC");
            } else {
                dir = f[0].sortDir;
            }
        }
        
        var st = (this.sortToggle) ? this.sortToggle[f[0].name] : null;
        var si = (this.sortInfo) ? this.sortInfo : null;
        
        this.sortToggle[f[0].name] = dir;
        this.sortInfo = [];
        for (var i = 0, len = f.length; i < len; ++i) {
            this.sortInfo.push({
                field: f[i].name,
                direction: dir
            });
        }
        
        if (!this.remoteSort) {
            this.applySort();
            this.fireEvent("datachanged", this);
        } else {
            if (!this.load(this.lastOptions)) {
                if (st) {
                    this.sortToggle[f[0].name] = st;
                }
                if (si) {
                    this.sortInfo = si;
                }
            }
        }
        
    }
    
   ,setDefaultSort: function(field, dir){
        // alert('setDefaultSort '+ field);
        dir = dir ? dir.toUpperCase() : "ASC";
        this.sortInfo = [];
        
        if (!Ext.isArray(field)) 
            this.sortInfo.push({
                field: field,
                direction: dir
            });
        else {
            for (var i = 0, len = field.length; i < len; ++i) {
                this.sortInfo.push({
                    field: field[i].field,
                    direction: dir
                });
                this.sortToggle[field[i]] = dir;
            }
        }
    }
    
    // @todo: if field passed in is an array, assume this is a complete replacement for the 'groupField'
    // @todo: if field passed in is empty/null, assume this means group by nothing, ie remove all groups
   ,groupBy: function(field, forceRegroup){
      //  alert("groupBy   " + field + "   " + forceRegroup);
        if (!forceRegroup && this.groupField == field) {
            return; // already grouped by this field
        }
        
        //if field passed in is an array, assume this is a complete replacement for the 'groupField'
        if(Ext.isArray(field)) {
          if(field.length==0)
            // @todo: field passed in is empty/null, assume this means group by nothing, ie remove all groups
            this.groupField=false;
          else
            this.groupField=field;
        } else {
          // Add the field passed as as an additional group
          if (this.groupField) {
            // If there is already some grouping, make sure this field is not already in here
            if(this.groupField.indexOf(field)==-1)
              this.groupField.push(field);
            else
              return; // Already grouped by this field  
          } else 
            // If there is no grouping already use this field
            this.groupField = [field];
        }
        if (this.remoteGroup) {
            if (!this.baseParams) {
                this.baseParams = {};
            }
            this.baseParams['groupBy'] = field;
        }
        if (this.groupOnSort) {
            this.sort(field);
            return;
        }
        if (this.remoteGroup) {
            this.reload();
        }
        else {
            var si = this.sortInfo || [];
            if (si.field != field) {
                this.applySort();
            }
            else {
              //  alert(field);
                this.sortData(field);
            }
            this.fireEvent('datachanged', this);
        }
    }
    
   ,applySort: function(){
        //alert('applySort ');
        
        var si = this.sortInfo;
        
        if (si && si.length > 0 && !this.remoteSort) {
            this.sortData(si, si[0].direction);
        }
        
        if (!this.groupOnSort && !this.remoteGroup) {
            var gs = this.getGroupState();
            if (gs && gs != this.sortInfo) {
            
                this.sortData(this.groupField);
            }
        }
    }
    
   ,getGroupState: function(){
        // alert('getGroupState '+ this.groupField);
        return this.groupOnSort && this.groupField !== false ? (this.sortInfo ? this.sortInfo : undefined) : this.groupField;
    }
    
   ,sortData: function(flist, direction) {
      //alert('sortData '+ direction);
      direction = direction || 'ASC';
      var st = [];
      var o;
      for (var i = 0, len = flist.length; i < len; ++i) {
        o = flist[i];
        st.push(this.fields.get(o.field ? o.field : o).sortType);
      }
      
      var fn = function(r1, r2){
        var v1 = [];
        var v2 = [];
        var len = flist.length;
        var o;
        var name;
        
        for (var i = 0; i < len; ++i) {
            o = flist[i];
            name = o.field ? o.field : o;
            
            v1.push(st[i](r1.data[name]));
            v2.push(st[i](r2.data[name]));
        }
        
        var result;
        for (var i = 0; i < len; ++i) {
            result = v1[i] > v2[i] ? 1 : (v1[i] < v2[i] ? -1 : 0);
            if (result != 0) 
                return result;
        }
        
        return result; //if it gets here, that means all fields are equal
      };
      
      this.data.sort(direction, fn);
      if (this.snapshot && this.snapshot != this.data) {
          this.snapshot.sort(direction, fn);
      }
    }
    
   ,removeGroupField: function(fld) {
    // @todo
      if(this.groupField) {
        var i=this.groupField.length;
        this.groupField.remove(fld);
        // See if anything was really removed?
        if(this.groupField.length < i) {
           if(this.groupField.length==0)
             this.groupField=false;
           // Fire event so grid can be re-drawn  
           this.fireEvent('datachanged', this);
        }
      }  
    }
});


/**********************************************************************************************
 * JAFFA - Java Application Framework For All - Copyright (C) 2008 JAFFA Development Group
 *
 * This library is free software; you can redistribute it and/or modify it under the terms
 * of the GNU Lesser General Public License (version 2.1 any later).
 *
 * See http://jaffa.sourceforge.net/site/legal.html for more details.
 *********************************************************************************************/

/** Based on Original Work found at http://extjs.com/forum/showthread.php?p=203828#post203828
 *
 * @author chander, PaulE
 */
Ext.namespace("Ext.ux.grid");

Ext.ux.grid.MultiGroupingView = Ext.extend(Ext.grid.GroupingView, {
  constructor: function(config) {
    Ext.ux.grid.MultiGroupingView.superclass.constructor.apply(this, arguments);
    // Added so we can clear cached rows each time the view is refreshed
    this.on("beforerefresh", function() {
      console.debug("Cleared Row Cache");
      if (this.rowsCache) delete rowsCache;
    }, this);
  }

  , displayEmptyFields: false

  , displayFieldSeperator: ', '

  , renderRows: function() {
    //alert('renderRows');
    var groupField = this.getGroupField();
    var eg = !!groupField;
    // if they turned off grouping and the last grouped field is hidden
    if (this.hideGroupedColumn) {
      var colIndexes = [];
      for (var i = 0, len = groupField.length; i < len; ++i) {
        var cidx = this.cm.findColumnIndex(groupField[i]);
        if (cidx >= 0)
          colIndexes.push(cidx);
        else
          console.debug("Ignore unknown column : ", groupField[i]);
      }
      if (!eg && this.lastGroupField !== undefined) {
        this.mainBody.update('');
        for (var i = 0, len = this.lastGroupField.length; i < len; ++i) {
          var cidx = this.cm.findColumnIndex(this.lastGroupField[i]);
          if (cidx >= 0)
            this.cm.setHidden(cidx, false);
          else
            console.debug("Error unhiding column " + cidx);
        }
        delete this.lastGroupField;
        delete this.lgflen;
      }

      else if (eg && colIndexes.length > 0 && this.lastGroupField === undefined) {
        this.lastGroupField = groupField;
        this.lgflen = groupField.length;
        for (var i = 0, len = colIndexes.length; i < len; ++i) {
          this.cm.setHidden(colIndexes[i], true);
        }
      }

      else if (eg && this.lastGroupField !== undefined && (groupField !== this.lastGroupField || this.lgflen != this.lastGroupField.length)) {
        this.mainBody.update('');
        for (var i = 0, len = this.lastGroupField.length; i < len; ++i) {
          var cidx = this.cm.findColumnIndex(this.lastGroupField[i]);
          if (cidx >= 0)
            this.cm.setHidden(cidx, false);
          else
            console.debug("Error unhiding column " + cidx);
        }
        this.lastGroupField = groupField;
        this.lgflen = groupField.length;
        for (var i = 0, len = colIndexes.length; i < len; ++i) {
          this.cm.setHidden(colIndexes[i], true);
        }
      }
    }
    return Ext.ux.grid.MultiGroupingView.superclass.renderRows.apply(this, arguments);
  }



  /** This sets up the toolbar for the grid based on what is grouped
  * It also iterates over all the rows and figures out where each group should appeaer
  * The store at this point is already stored based on the groups.
  */
  , doRender: function(cs, rs, ds, startRow, colCount, stripe) {
    //console.debug ("doRender: ",cs, rs, ds, startRow, colCount, stripe);
    var ss = this.grid.getTopToolbar();
    if (rs.length < 1) {
      return '';
    }

    var groupField = this.getGroupField();
    var gfLen = 0; 
    if (groupField)
      gfLen = groupField.length;


    // Remove all entries alreay in the toolbar
    for (var hh = 0; hh < ss.items.length; hh++) {
      Ext.removeNode(Ext.getDom(ss.items.itemAt(hh).id));
    }

    if (gfLen == 0) {
      //       this.cm.setHidden(0,true);
      ss.addItem(new Ext.Toolbar.TextItem("Drop Columns Here To Group"));
      console.debug("No Groups");
    } else {
      //       this.cm.setHidden(0,false);
      //       this.cm.setColumnWidth(0,gfLen*12);
      console.debug("Set width to", gfLen, " Groups");

      // Add back all entries to toolbar from GroupField[]    
      ss.addItem(new Ext.Toolbar.TextItem("Grouped By:"));
      for (var gfi = 0; gfi < gfLen; gfi++) {
        var t = groupField[gfi];
        if (gfi > 0)
          ss.addItem(new Ext.Toolbar.Separator());
        var b = new Ext.Toolbar.Button({
          text: this.cm.getColumnHeader(this.cm.findColumnIndex(t))
        });
        b.fieldName = t;
        ss.addItem(b);
        console.debug("Added Group to Toolbar :", this, t, '=', b.text);
      }
    }

    this.enableGrouping = !!groupField;

    if (!this.enableGrouping || this.isUpdating) {
      return Ext.grid.GroupingView.superclass.doRender.apply(this, arguments);
    }

    var gstyle = 'width:' + this.getTotalWidth() + ';';
    var gidPrefix = this.grid.getGridEl().id;
    var groups = [], curGroup, i, len, gid;
    var lastvalues = [];
    var added = 0;
    var currGroups = [];

    // Create a specific style to indent rows
    //@TODO come up with a better way to do this with a dummy column
    /*
    var st = Ext.get(gidPrefix+"-style");
    if(st) st.remove();
    Ext.getDoc().child("head").createChild({
    tag:'style',
    id:gidPrefix+"-style",
    html:"div#"+gidPrefix+" div.x-grid3-row {padding-left:"+(gfLen*12)+"px}"+
    "div#"+gidPrefix+" div.x-grid3-header {padding-left:"+(gfLen*12)+"px}"
    });
    */

    // Loop through all rows in srecord set
    for (var i = 0, len = rs.length; i < len; i++) {
      added = 0;
      var rowIndex = startRow + i;
      var r = rs[i];
      var differ = 0;
      var gvalue = [];
      var fieldName;
      var fieldLabel;
      var grpFieldNames = [];
      var grpFieldLabels = [];
      var v;
      var changed = 0;
      var addGroup = [];

      for (var j = 0; j < gfLen; j++) {
        fieldName = groupField[j];
        fieldLabel = this.cm.getColumnHeader(this.cm.findColumnIndex(fieldName));
        v = r.data[fieldName];
        if (v) {
          if (i == 0) {
            // First record always starts a new group
            addGroup.push({ idx: j, dataIndex: fieldName, header: fieldLabel, value: v });
            lastvalues[j] = v;
          } else {
            if ((typeof (v) == "object" && (lastvalues[j].toString() != v.toString())) || (typeof (v) != "object" && (lastvalues[j] != v))) {
              // This record is not in same group as previous one
              //console.debug("Row ",i," added group. Values differ: prev=",lastvalues[j]," curr=",v);
              addGroup.push({ idx: j, dataIndex: fieldName, header: fieldLabel, value: v });
              lastvalues[j] = v;
              changed = 1;
            } else {
              if (gfLen - 1 == j && changed != 1) {
                // This row is in all the same groups to the previous group
                curGroup.rs.push(r);
                //console.debug("Row ",i," added to current group");
              } else if (changed == 1) {
                // This group has changed because an earlier group changed.
                addGroup.push({ idx: j, dataIndex: fieldName, header: fieldLabel, value: v });
                //console.debug("Row ",i," added group. Higher level group change");
              } else if (j < gfLen - 1) {
                // This is a parent group, and this record is part of this parent so add it
                if (currGroups[fieldName])
                  currGroups[fieldName].rs.push(r);
                //else
                //    console.error("Missing on row ",i," current group for ",fieldName);
              }
            }
          }
        } else {
          if (this.displayEmptyFields) {
            addGroup.push({ idx: j, dataIndex: fieldName, header: fieldLabel, value: this.emptyGroupText || '(none)' });
          }
        }
      } //for j
      //if(addGroup.length>0) console.debug("Added groups for row=",i,", Groups=",addGroup);

      for (var k = 0; k < addGroup.length; k++) {
        var grp = addGroup[k];
        gid = gidPrefix + '-gp-' + grp.dataIndex + '-' + Ext.util.Format.htmlEncode(grp.value);

        // if state is defined use it, however state is in terms of expanded
        // so negate it, otherwise use the default.
        var isCollapsed = typeof this.state[gid] !== 'undefined' ? !this.state[gid] : this.startCollapsed;
        var gcls = isCollapsed ? 'x-grid-group-collapsed' : '';
        var rndr = this.cm.config[this.cm.findColumnIndex(grp.dataIndex)].renderer;
        curGroup = {
          group: rndr ? rndr(grp.value) : grp.value
           , groupName: grp.dataIndex
           , gvalue: grp.value
           , text: grp.header
           , groupId: gid
           , startRow: rowIndex
           , rs: [r]
           , cls: gcls
           , style: gstyle + 'padding-left:' + (grp.idx * 12) + 'px;'
        };
        currGroups[grp.dataIndex] = curGroup;
        groups.push(curGroup);

        r._groupId = gid; // Associate this row to a group
      } //for k
    } //for i

    var buf = [];
    var toEnd = 0;
    for (var ilen = 0, len = groups.length; ilen < len; ilen++) {
      toEnd++;
      var g = groups[ilen];
      var leaf = g.groupName == groupField[gfLen - 1]
      this.doGroupStart(buf, g, cs, ds, colCount);
      if (g.rs.length != 0 && leaf)
        buf[buf.length] = Ext.grid.GroupingView.superclass.doRender.call(this, cs, g.rs, ds, g.startRow, colCount, stripe);

      if (leaf) {
        var jj;
        var gg = groups[ilen + 1];
        if (gg != null) {
          for (jj = 0; jj < groupField.length; jj++) {
            if (gg.groupName == groupField[jj])
              break;
          }
          toEnd = groupField.length - jj;
        }
        for (var k = 0; k < toEnd; k++) {
          this.doGroupEnd(buf, g, cs, ds, colCount);
        }
        toEnd = jj;
      }
    }

    return buf.join('');
  }



  /** Should return an array of all elements that represent a row, it should bypass
  *  all grouping sections
  */
  , getRows: function() {

    // This function is called may times, so use a cache if it is available
    if (this.rowsCache)
      r = this.rowsCache.slice(0);
    else {
      //alert('getRows');
      if (!this.enableGrouping) {
        return Ext.grid.GroupingView.superclass.getRows.call(this);
      }
      var groupField = this.getGroupField();
      var r = [];
      var g, gs = this.getGroups();
      // this.getGroups() contains an array of DIVS for the top level groups
      //console.debug("Get Rows", groupField, gs);

      if (groupField) {
        r = this.getRowsFromGroup(r, gs, groupField[groupField.length - 1]);

        // Clone the array, but not the objects in it
        //@TODO uncomment this for caching to work
        this.rowsCache = r.slice(0);
      }
    }
    //console.debug("Found ", r.length, " rows");
    return r;
  }


  /** Return array of records under a given group
  * @param r Record array to append to in the returned object
  * @param gs Grouping Sections, an array of DIV element that represent a set of grouped records
  * @param lsField The name of the grouping section we want to count
  */
  , getRowsFromGroup: function(r, gs, lsField) {
    var rx = new RegExp(".*-gp-" + lsField + "-.*");

    // Loop over each section
    for (var i = 0, len = gs.length; i < len; i++) {

      // Get group name for this section
      var groupName = gs[i].id;
      if (rx.test(groupName)) {
        //console.debug(groupName, " matched ", lsField);
        g = gs[i].childNodes[1].childNodes;
        for (var j = 0, jlen = g.length; j < jlen; j++) {
          r[r.length] = g[j];
        }
        //console.debug("Found " + g.length + " rows for group " + lsField);
      } else {
        if (!gs[i].childNodes[1]) {
          console.error("Can't get rowcount for field ", lsField, " from ", gs, i);
        } else
        // if its an interim level, each group needs to be traversed as well
          r = this.getRowsFromGroup(r, gs[i].childNodes[1].childNodes, lsField);
      }
    }
    return r;
  }
});

function datepicker(target){
	Ext.onReady(function() {
	var textBoxes = Ext.DomQuery.select("input[rel=" + target + "]");
		Ext.each(textBoxes, function(item, id, all) {
			var cl = new Ext.form.DateField({
			  allowBlank: true,
			  //TODO:TRANSLATE
				format: 'd/m/Y',
				applyTo: item
			});
		});
	});
}

/*Ext.override(Ext.data.GroupingStore, {
  groupBy: function(field, forceRegroup) {
    //alert(field);
    //if (this.groupField == field && !forceRegroup) {
    //  return; // already grouped by this field
    //}
    this.groupField = field;
    if (this.remoteGroup) {
      if (!this.baseParams) {
        this.baseParams = {};
      }
      this.baseParams['groupBy'] = field;
      var lo = this.lastOptions; // dan
      if (lo && lo.params) {
        lo.params.groupBy = field;
      }
    }
    if (this.groupOnSort) {
      this.sort(field);
      return;
    }
    if (this.remoteGroup) {
      this.reload();
    } else {
      var si = this.sortInfo || {};
      if (si.field != field) {
        this.applySort();
      } else {
        this.sortData(field);
      }
      this.fireEvent('datachanged', this);
    }
  }
});*/
Tangane = {};

/*
window.log = 'console' in window && 'log' in window.console && $.isFunction(window.console.log) 
  ? function _log() { window.console.log.apply(console, arguments); }
  : function _log() { }
;
*/
window.log = function () { };

function throwError(str)
{
  if ($('#error').length == 0) {
      //TODO:TRANSLATE
      $('<div id="error"><span class="1">one error</span> &mdash; <a href="javascript:void(0)">Close</a></div>').css({
        position:'fixed',
        width:'100%',
        top:'0px',
        left:'0px',
        zIndex:'9999',
        backgroundColor:'#FCC',
        textAlign:"center",
        color:"#A66",
        padding:"6px 0px",
        borderBottom:'1px #A66 solid'
      })
      .hide()
      .appendTo('body')
      .fadeIn('slow')
      .find('a')
      .css({
        color:"#A66"
      })
      .click(function(){
        $('#error')
        .attr('id','old-error')
        .css({zIndex:'9998'})
        .fadeOut('fast',function(){$(this).remove();});
      });      
    } else {
      var $span = $('#error span');
      var count = 1 + parseInt($span.attr('class'));
      $span.attr('class',count).text(count+' errors');
    }    
}

/*[*/
  window.logError = log;
  window.logWarning = log;
/*]*/
  /*<*/
  window.logNotice = log;
/*>*/

/*[*/
Assert = {
  
  /**
   * Test d'assertion classique, renvoie une fonction avec la signature de log().
   * Si la condition est fausse, la fonction renvoyée affiche en log ses 
   * arguments puis lance une exception "Assertion failure!"
   */
  test : function _assert_isTrue(condition) 
  {
    if (condition) return function() {};
    return function() {
      logError.apply(this,arguments);
      throwError("Assertion failure!");
    };
  },
    
  /**
   * S'assure que deux objets sont ?gaux (==).
   */
  areEqual : function _assert_areEqual(a,b) 
  {
    Assert.test(a == b)("Assert : expected %o == %o", a, b);
  },
  
  /**
   * S'assure que a est de type t. Rappel des types javascript: 
   *  - undefined
   *  - null
   *  - boolean
   *  - number
   *  - string
   *  - object
   *  - function
   */
  isType : function _assert_isType(a,t) 
  {
    Assert.test((typeof a) == t)("Assert : expected typeof %o == %s", a, t);
  },
  
  /**
   * S'assure que a a un membre m.
   */
  hasMember : function _assert_hasMember(a,m) 
  {
    Assert.test(m in a)('Assert : expected %o[%s]', a, m);
  },
  
  /**
   * D?termine si une fonction est appelée un jour, ou non. Attend 10 secondes avant
   * de pr?venir que l'appel n'a pas lieu. Renvoie une nouvelle version de la
   * fonction configurée pour faire le travail. La description est un tableau au
   * format compatible avec log() pour d?crire l'erreur.   
   */
  willBeCalled : function _assert_hasMember(f,desc)
  {
    var hasBeenCalled = false;
    
    function assert_willBeCalled_timer() {          
      desc[0] = 'Assert : '+desc[0];
      Assert.test(hasBeenCalled).apply(Assert,desc);
    }
    
    setTimeout(assert_willBeCalled_timer,10000);
    
    return function assert_willBeCalled_wrapper() {
      hasBeenCalled = true;
      f.apply(this,arguments);
    }        
  }
};
/*]*/

/**
 * Construction de packages ? la vol?e.
 */
function pack(tngpackage) 
{
  var p = this, segs = tngpackage.split('.');
  $.each(segs,function(k,seg)
  {
    if (! (seg in p)) p[seg] = {};
    p = p[seg]; 
  });
  return p;
}

/* ------------ Listener ------------ */

/**
 * Un listener: une fonction qui propage les appels qu'elle re?oit vers des
 * fonctions enregistrées auprès d'elle, en utilisant "self" comme valeur
 * de this lors de l'appel, avec les m?mes arguments.
 *
 * var l = new tangane.listener(foobar);
 * 
 * var r = l.bind(hello); 
 *
 * l(1,2,3); // hello.apply(foobar,[1,2,3]);
 *
 * l.unbind(r); 
 *
 * Si fourni, bind(init) avant de renvoyer une valeur.
 */
Tangane.Listener = function _tangane_listener(self, init) {
  var h = {}, i = 0;
  /*function s() { 
  for (var k in h)
  h[k].apply(self, arguments);
  }
  */
  function s() {
    var t = $.extend({}, h);
    for (var k in t) {
      t[k].apply(this, arguments);
    }
  }


  s.bind = function(f) {
    /*[*/
    if (!$.isFunction(f)) {
      logError("ERROR: f = %o is not a function !", f);
      throwError("Expected function in .bind()");
    }
    /*]*/
    h[i] = f;
    return i++;
  };

  s.unbind = function(k) {
    /*[*/
    if (!(k in h)) {
      logError("ERROR: removal key %d unknown, maybe already removed ?", k);
      throwError("Expected valid removal key in .unbind()");
    }
    /*]*/

    delete h[k];
  };

  s._listeners = h;

  if (init) {
    /*[*/
    if (!$.isFunction(init)) {
      logError("ERROR: f = %o is not a function !", init);
      throw "Expected function in Tangane.Listener()"
    }
    /*]*/

    s.bind(init);
  }

  return s;
};

/* ------------ Contexte ------------ */ 

/**
 * Contexte d'exécution. Permet d'éviter tout recours à des variables globales
 * en fournissant à chaque classe un objet représentant tous les canaux globaux
 * suivant lesquels elle peut communiquer vers l'extérieur (ou recevoir des
 * communications). 
 */
Tangane.Context = function _tangane_context() 
{ 
  this._c = Tangane.Context; 
};

/**
 * Crée un nouveau contexte à partir de l'ancien. Ce contexte dispose d'un 
 * canal de communication appel? 'c', qui devient disponible comme un membre
 * de cette fonction. Il permet trois op?rations: 
 * 
 *  - ctx.c.Bind(f) : attache le callback f au canal.
 *  
 *  - ctx.c(...) : lance un message sur le canal, appelle tous les f(...) 
 *                 avec this = ctx et les m?mes arguments
 *                 
 *  - ctx.c.Unbind(k) : d?tache un callback du canal, où k est la valeur de 
 *                      retour de l'appel à bind()
 * 
 * L'ancien contexte n'est pas modifi?. N?anmoins, les canaux du pr?c?dent
 * contexte sont partag?s: un bind() sur un canal dans le nouveau contexte
 * sera visible depuis l'ancien contexte (sauf si ce canal est c). 
 * 
 * @param c Le canal a cr?er.
 * @return Nouveau contexte
 */
Tangane.Context.prototype.channel = function _tangane_context_channel(c) 
{
  /*[*/
  if (typeof c != "string")
  {
    logError("Channel name %o is not a string", c);
    throwError("Expected string name in Tangane.Context.channel()");
  }
  /*]*/
  
  var self = this;    
  return this.set(c,new Tangane.Listener(self));
};
  
/**
 * Ajoute une valeur au contexte. Cr?e un nouveau contexte, ne
 * modifie pas l'ancien.
 *  
 * @param n La cl?
 * @param v La valeur
 * @return Nouveau contexte.
 */
Tangane.Context.prototype.set = function _tangane_context_set(n,v)
{ 
  /*[*/
  if (typeof n != "string")
  {
    logError("Member name %o is not a string", n);
    throw "Expected string name in Tangane.Controller.set()"
  }
  /*]*/
  
  function i() 
  { 
    this._c = i; 
  };
    
  i.prototype = new this._c();
  i.prototype[n] = v;
    
  return new i();
};

/**
 * Enveloppe une fonction existante. Fonctionne comme set(n,func)
 * ? l'exception du fait que lorsque ctx.n(a,b,c) est appel?e, 
 * l'appel est transf?r? ? func(old,a,b,c) (avec ctx = this), 
 * o? old est l'ancienne valeur. Cela permet donc de travailler avec
 * des couches de valeurs.   
 */
Tangane.Context.prototype.wrap = function _tangane_context_wrap(n,f) 
{  
  var original = this[n];
  
  function outer() 
  { 
    var a = arguments, self = this; 
    a.unshift(function inner(){ original.apply(self,arguments); }); 
    f.apply(this,a); 
  }
  
  return this.set(n,outer);
};

/**
 * Le contexte par d?faut, pouvant ?tre ?tendu pendant la phase d'initialisation
 * du site pour y inclure d'autres comportements.
 */
Tangane.Context.root = (new Tangane.Context())

/**
 * Canal 'dynamique', permet d'envoyer des données sans nom défini préalablement.
 * Dangereux car non typé et peu performant, mais parfois indispensable.
 */
  .channel('trigger');

/* ------------ Box ------------ */ 

/**
 * Cr?e une nouvelle box, associ?e ? un ?l?ment. Une box est un conteneur
 * permettant de mettre ? jour son contenu de fa?on autonome et 
 * ind?pendante.
 * @param elt Un ?l?ment du DOM, un s?lecteur CSS, un objet jQuery.
 * @param ctx Un tangane.context
 * @param _next Callback, optionnel, appel? sur la box lorsque le 
 *              chargement de celle-ci est fini.
 */
Tangane.Box = function _tangane_box(elt, ctx, _next) 
{  
  var self = this;
  this._ctx = ctx;
  this._unbind = {};
  this._inner = [];
  this._loading = false;
  $(function() {
    self.$self = $(elt);
    if (_next) _next(self);
  });
};

/**
 * Efface tout le contenu d'une box, vide tous les ?l?ments qui ont pu
 * y ?tre ajout?s et annule tous les bind() qu'ils ont fait ? travers
 * leurs contextes.
 * 
 * Effet synchrone et instantan?.
 * @return this
 */
Tangane.Box.prototype.clear = function _tangane_box_clear() {
  /*<*/logNotice('Box %o : clear', this.$self); /*>*/

  this.$self.html('');

  var self = this;

  $.each(this._inner, function(i, key) {
    delete self[key];
  });

  this._inner = [];

  for (var c in this._unbind) {
    this._ctx[c].unbind(this._unbind[c]);
  }

  this._unbind = {};

  return this;
};

/**
 * Remplit une box à partir des données fournies. 
 * 
 * Les données comprennent du HTML. Celui-ci est inséré dans la box dès le départ. 
 * Ensuite, si un membre 'code' existe, les clés sont prises dans leur ordre
 * d'apparition et pour chacun on ex?cute l'expression 'do', dont on s'attend
 * qu'elle s'évalue en une fonction, et on appelle cette fonction.
 * 
 * Les arguments de l'appel sont: 
 *   do(box, ctx, data, _next)
 *   
 * box : la box elle-même (permet d'accéder à tous ses membres, donc)
 * ctx : le contexte dans lequel vit la box. Il s'agit d'une copie du contexte
 *       de la box, spécialement conçu pour que le contenu de la box puisse y 
 *       mettre des bind() sans craindre de fuites de m?moire.
 * data : le champ data du JSON
 * _next : un callback qui doit obligatoirement être appelé par do lorsque tout
 *         son traitement est fini (donc ?ventuel décalage s'il faut aller 
 *         chercher des données sur le serveur). 
 *         
 * Si un argument est fourni à _next --- par exemple, _next(foo) --- alors la box
 * va faire box.key = foo avant de passer à l'exécution du prochain do pour
 * la clé suivante.
 * 
 * Par définition, le comportement de cette fonction est potentiellement 
 * asynchrone, ce qui veut dire qu'elle peut retourner avant ou après que
 * l'initialisation soit finie. Pour se caler sur la fin de l'initialisation,
 * utiliser _next. 
 * 
 * @param data Les donn?es. JSON : {html:"",code:{key:{action:"expr",data:object}}} 
 * @param _next Callback appel? lorsque toute l'initialisation est finie.
 * @return this
 */
Tangane.Box.prototype.fill = function _tangane_box_fill(data, _next) {
  /*[*/
  if (this.loading) {
    logError("Box %o is already loading, attempting to load again...", this.$self);
    throwError("Loading box requests another load of itself.");
  }
  /*]*/

  this._loading = true;
  this.clear();
  this.$self.addClass('loading');

  Tangane.Box._todo = [];

  if ('html' in data && data.html) {
    /*[*/
    if (typeof data.html != "string") {
      logError("ERROR: In %o, expected string for HTML data: %o", box.$self, data.html);
      throwError("HTML data for box should be a string");
    }
    /*]*/

    this.$self.html(data.html);
  }

  // Création du nouveau contexte
  var ctx = this._ctx, self = this;
  $.each(this._ctx, function(c, chan) {
    if (!('bind' in chan)) return;
    if (!('unbind' in chan)) return;

    var h = {}, i = 0;

    // Créer un nouveau canal, qui se contente de forwarder
    // l'appel au channel au-dessus...
    function s() {
      chan.apply(this, arguments);
    }

    s.bind = function(f) { h[i] = f; return i++; };
    s.unbind = function(k) { delete h[k]; };

    ctx = ctx.set(c, s);

    // Faire que le channel au-dessus appelle également nos 
    // propres callbacks...
    /*function d() {
    for (var k in h)
    h[k].apply(this, arguments);
    }*/

    function d() {
      var t = $.extend({}, h);
      for (var k in t) {
        t[k].apply(this, arguments);
      }
    }

    // Se souvenir de quel canal on doit détacher
    self._unbind[c] = chan.bind(d);

  });

  if (!('code' in data) || !data.code)
    data.code = {};

  /*[*/
  if (typeof data.code != "object") {
    logError('ERROR: In %o, Inner contents %o are not an array or dictionary', self.$self, data.code);
    throwError("Box inner contents expected to be array or dictionary");
  }
  /*]*/

  var actions = Tangane.Box._todo;
  Tangane.Box._todo = [];

  for (var k in data.code) {
    var boxAction = data.code[k];

    /*[*/
    if (typeof boxAction.action != "string") {
      logError(
        "ERROR: In %o, constructor name for action %s is %o",
        self.$self,
        k,
        boxAction.action
      );
      throwError("Expected box action constructor name as string");
    }

    if (!$.isFunction(pack(boxAction.action))) {
      logError(
        "ERROR: In %o, constructor for action %s is %o",
        self.$self,
        k,
        pack(boxAction.action)
      );
      throwError("Expected box action constructor to be a function");
    }
    /*]*/

    actions.push({ n: k, f: pack(boxAction.action), d: boxAction.data });
  }

  // Initialisation de la i-ème valeur présente
  function init(i) {
    if (i == actions.length) finish();
    else {
      var action = actions[i];
      var assign = function _assign(actionResult) {
        /*[*/
        if (action.n in self) {
          logError("ERROR: In %o, box['%s'] is already defined as %o", self.$self, action.n, self[action.n]);
          throwError("Trying to assign inner-content to existing member");
        }
        /*]*/

        // console.log("%s : %o", action.n, actionResult);

        self[action.n] = actionResult;
        self._inner.push(action.n);
        init(i + 1);
      };

      /*[*/
      assign = Assert.willBeCalled(assign,
        ['ERROR: In %o, constructor %o for action %s does not call _next!',
        self.$self,
        action.f,
        action.n]
      );
      /*]*/

      /*<*/
      logNotice('Box %o : action %s : %o(%o)',
        self.$self,
        action.n,
        action.f,
        action.d);
      /*>*/

      action.f(
        self,
        ctx,
        action.d,
        assign
      );
    }
  }

  init(0);

  // Fini : nettoyer et appeler le callback
  function finish() {
    self.$self.removeClass('loading');
    self._loading = false;
    if (_next) _next();
  }
};

/**
 * Lance le chargement d'une box en GET à une URL fournie.
 * Se contente de récupérer du JSON à l'URL fournie en utilisant
 * le canal ctx.ajaxGet(), puis de donner le résultat à la fonction
 * fill(). Ne garde que le canal "box" du JSON renvoyé.
 * 
 * En cas d'échec, ne fait rien (il faut donc compter sur 
 * ctx.ajaxGetFailure() pour réagir.
 * 
 * Il s'agit, intrinsèquement, d'une fonction asynchrone. 
 * 
 * @param url Une url à laquelle aller chercher des données.
 * @param args Les arguments GET à envoyer.
 * @param _next Callback appelé lorsque l'initialisation s'est finie
 *              avec succès.
 *              
 * @return this
 */
Tangane.Box.prototype.fromUrl = function _tangane_box_fromUrl(url, args, _next) 
{
  var self = this;
  
  /*<*/
  logNotice('Box %o : query : %o ? %o', self.$self, url, args );
  /*>*/
  
  this._ctx.ajaxGet(url, args, onSuccess, _next);
  
  function onSuccess(data) { 
    /*<*/
    logNotice('Box %o : got : %o', self.$self, data.box);
    /*>*/
    self.fill(data.box, _next); 
  }  
};

/* ------------ HTML-time box action adding ------------ */

/**
 * Les actions à prendre en compte lors de la prochaine exécution d'actions ,
 * format {:nn:nom,f:fonction}
 */
Tangane.Box._todo = null;

/**
 * Enregistre une action comme "à faire", avec un nom et une fonction à 
 * exécuter. Ces actions sont réalisées avant celles envoyées par le serveur.
 * @param name Le nom de l'action.
 * @param func La fonction à exécuter.
 */
Tangane.Box.addAction = function _tangane_box_addAction(name,func)
{
  /*[*/
  if (!$.isArray(Tangane.Box._todo)) 
  {
    logError("Attempting to use Tangane.Box.addAction when no box is constructing!");
  }  
  /*]*/
  Tangane.Box._todo.push({n:name,f:func,d:null});
};

/* ------------ Box Contents ------------ */

/**
 * Wrapper, transforme une fonction box-unaware en une fonction box-aware.
 * @param func Une fonction. Arguments : elem (un objet jQuery), config (les 
 *             données envoyées par le serveur).
 * @return Une fonction compatible avec une box, qui renvoie la valeur de retour
 *         de func suivant la convention _next(result).
 */
Tangane.Box.wrap = function _tangane_box_wrap(func) 
{  
  function cls(box, ctx, config, _next) 
  {
    $(function(){
      var result = func(box.$self, config);
      if (_next) _next(result);
    });
  }
  
  return cls;
};

/**
 * Wrapper, crée une classe à partir d'un constructeur. Initialise
 *   this.box, this.ctx et this.config
 * à partir des paramètres, puis appelle ctor(_next) et exige que ce
 * dernier fasse un if(_next) _next(this) à la toute fin. 
 * 
 * @param ctor Un constructeur. 
 * @return Une classe.
 */
Tangane.Box.contents = function _tangane_box_contents(ctor) 
{  
  function cls(box, ctx, config, _next) 
  {
    this.box = box;
    this.ctx = ctx;   
    ctor.call(this,config);
    if (_next) _next(this);
  };
  
  return cls;
};

/* ------------ AJAX ------------ */

/**
 * Classe de requêtage AJAX. En fait un singleton (tangane.ajax.instance) qui propose
 * des fonctions en GET et POST. Il est conseillé d'utiliser plutôt:
 * 
 * ctx.ajaxGet(url, args, onSuccess, onFailure) 
 * ctx.ajaxPost(url, args, onSuccess, onFailure)
 */
Tangane.Ajax = function() {};

/**
 * Fonction interne, ne pas appeler directement.
 */
Tangane.Ajax.prototype._run = function _tangane_ajax__run(ctx, method, url, data, onSuccess, onFail) {

  $.ajax({
    async: true,
    type: method,
    data: data,
    dataType: 'json',
    url: url,
    beforeSend: function (xhr) {
      xhr.setRequestHeader('X-Requested-Client', "X-Tangane-Mvc");
    },
    success: function (data) {
      if ('error' in data) fail(data.error);
      else success(data);
    },
    error: function (xhr, textStatus) {
      fail(textStatus);
    },
    complete: function () {
    }
  });

  function fail(error) {
    /*[*/logError('ERROR: %s request to %s failed, error: %s', method, url, error); /*]*/
    if (ctx) ctx.onAjaxFailure(error);
    if (onFail) onFail(error);
  }

  function success(data) {
    /*<*/logNotice('Ajax: %s request to %s, result: %o', method, url, data); /*>*/
    if (ctx) ctx.onAjaxSuccess(data);
    if (onSuccess) onSuccess(data);
  }
};

/**
 * Requête GET sur une url.
 * 
 * Si les données n'arrivent pas, ou si on récupère un JSON avec un membre "error", 
 * appelle ctx.onAjaxFailure(data.error) et onFail(data.error) dans cet ordre.
 * 
 * Si les données arrivent sans erreur, appelle ctx.onAjaxSuccess(data.error) et
 * onFail(data.error) dans cet ordre.
 * 
 * @param url La cible.
 * @param data Les arguments GET.
 * @param onSuccess callback (optionnel)
 * @param onFail callback (optionnel)
 * @param ctx contexte (optionnel)
 * @return void
 */
Tangane.Ajax.prototype.get = function _tangane_ajax_get(url, data, onSuccess, onFail, ctx) 
{
  this._run(ctx, 'get', url, data, onSuccess, onFail);
};
  
/**
 * Comme get(), mais avec un POST.
 */
Tangane.Ajax.prototype.post = function _tangane_ajax_post(url, data, onSuccess, onFail, ctx) 
{
  this._run(ctx, 'post', url, data, onSuccess, onFail);
};

/**
 * Instance du singleton. Utiliser plut?t ctx.ajaxGet et ctx.ajaxPost.
 */
Tangane.Ajax.instance = new Tangane.Ajax();

// Initialiser le contexte avec les canaux li?s ? l'AJAX...
Tangane.Context.root = Tangane.Context.root

/**
 * onAjaxSuccess : canal de communication, appel? ? chaque fois qu'une requ?te
 * AJAX r?ussit sur le serveur, avec en argument la donn?e.
 */
  .channel('onAjaxSuccess')
/** 
 * onAjaxFailure : canal de communication, appel? ? chaque fois qu'une requ?te
 * AJAX rate sur le serveur, avec en argument l'erreur (ou rien si erreur de
 * connexion).
 */
  .channel('onAjaxFailure')
/**
 * onNewPage : canal appelé lorsqu'une page vient d'être chargée ou rechargée.
 */
 .channel('onNewPage')
/**
 * onAjaxGet(url,args,s,f) : requ?te GET.
 */
  .set('ajaxGet', function _tangane_context_root_ajaxGet(url,args,s,f){ Tangane.Ajax.instance.get(url,args,s,f,this); })
/**
 * onAjaxPost(url,args,s,f) : requ?te POST.
 */
  .set('ajaxPost', function _tangane_context_root_ajaxPost(url,args,s,f){ Tangane.Ajax.instance.post(url,args,s,f,this); });

Tangane.Context.root.onAjaxSuccess.bind(function(data) {
  if ('login' in data && data.login)
    document.location = data.login;
});

Tangane.Context.root.onAjaxFailure.bind(function(error) {
  if ('url' in error && error.url) {
    document.location = error.url;
  }
});

/**
 * nous sera utile pour déclencher les tags Google Analytics
 */
Tangane.Context.root.onNewPage.bind(function(url) { /*alert('/Home#' + url);*/ });


/* ------------ Url Handling ------------ */

/**
 * Gestion de l'URL : enregistre dans le hash-fragment des informations 
 * issues de l'arborescence des box/composants sur la page et permet
 * de les redistribuer ensuite.
 */
Tangane.Url = function _tangane_url() {
  this._root = { get: function() { return []; }, set: function() { } };
  var self = this;
  $.address.change(function() {
    /*<*/logNotice('Url : changed, notify root'); /*>*/
    self._root.set(self.decode(self.url()));
    Tangane.Context.root.onNewPage(self.url());
  });
};

Tangane.Url.prototype.url = function _tangane_url_url()
{
  var url = $.address.value();
  if (url.length >= 2 && (url.charAt(1) == '=' || url.charAt(1) == '{'))
    url = url.substr(1);
  return url;
};

/**
 * Encode un objet d'une façon proche du JSON, mais formaté pour ressembler
 * à une URL. L'approche est:
 * 
 *   encode({key:val}) = '{' + [esc_k(key) + encode(val)].join('/') + '}'
 *   
 *   encode(number) = '=' + number
 *   encode(bool)   = '=' + bool ? 'true' : 'false'
 *   encode(string) = '/' + esc_k(key)
 *   encode(null)   = '='
 *   
 *   esc_k(number) = number
 *   esc_k(string) = encodeURIComponent(string)
 * 
 * @param data Les données
 * @return Représentation string de la chose.
 */
/* TODO: SLI --> encode les id ? */
Tangane.Url.prototype.encode = function _tangane_url_encode(state) {
  /*<*/logNotice('Url : encode : %o', state); /*>*/

  function aux(o) {

    if (o === null) return '=';
    if (o === true) return '=true';
    if (o === false) return '=false';

    if (typeof o == 'number') return '=' + o;
    if (typeof o == 'string') return '/' + encodeURIComponent(o);

    if (typeof o == 'object') {
      var out = [];
      for (var k in o) {
        var inner = aux(o[k]);
        if (inner != '') out.push(encodeURIComponent(k) + inner);
      }
      if (out.length == 0) return '';
      else return '{' + out.join('/') + '}';
    }

    return '=';
  }

  return aux(state);
};

/**
 * Inverse le traitement fait par encode. ;-)
 * @param arg
 * @return
 */
/* TODO: SLI --> decode les id ? */
Tangane.Url.prototype.decode = function _tangane_url_decode(uri) 
{
  /*<*/ logNotice('Url : decode : %o', uri); /*>*/
  
  if (!uri || typeof uri != "string") return null;
  
  // Renvoie : [ result, unparsed string ]
  function aux(str) 
  {
	var match;
	if (!str) return [null,''];
	
	// Segment entier/bool/null
	match = str.match(/^=[^\/}]*/);
	if (match) 
	{
	  var n = match[0].substr(1);
	  str = str.substr(1+n.length);
	  if (!n) return [null,str];
	  if (n == 'true') return [true,str];
	  if (n == 'false') return [false,str];
	  return [parseFloat(n),str];
	}
	
	// Segment string
	match = str.match(/^\/([^\/}]*)/);
	if (match) 
	{
	  var n = match[0].substr(1);
	  return [decodeURIComponent(n),str.substr(1+n.length)];	  
	}
	
	// Segment objet
	if (str.charAt(0) == '{') 
	{
	  str = str.substr(1);
	  var obj = {};
	  while (str && str.charAt(0) != '}') 
	  {		
		match = str.match(/^([^\/{}=]*)/);
		if (match) {
		  var n = match[0], k = decodeURIComponent(n);
		  if (/^[0-9]+$/.test(k)) k = parseInt(k);
		  str = str.substr(n.length);
		  var r = aux(str);
		  obj[k] = r[0];
		  str = r[1];
		} else return [null,''];
		if (str && str.charAt(0) == '/') str = str.substr(1);
	  }
	  if (str) str = str.substr(1);
	  return [obj,str];
	}
	
	return [null,''];
  }
  
  var result = aux(uri);
  return result[0];
};

/**
 * Modifie la racine de la page (l'objet responsable de fournir l'URL
 * actuelle et de propager les changements d'URL.
 * 
 * root.get renvoie un tableau (utilisé dans encode)
 * 
 * root.set récupère le résultat (renvoyé par decode) lors d'un changement
 * d'URL.
 *
 * Appelle directement this
 * 
 * @param root un objet { get : func, set : func }
 * @return
 */
Tangane.Url.prototype.setRoot = function _tangane_url_setRoot(root) 
{
  /*<*/ logNotice('Url : using root class : %o', root); /*>*/
  this._root = root;  
};

/**
 * Appelle root.get, qui renvoie un tableau. Ce tableau est encodé et
 * mis dans l'URL (ce qui provoque donc un root.set automatique).
 * 
 * Pour changer de page, le principe est de modifier une de ses valeurs
 * internes puis d'appeler update. En principe, on appellera plutôt
 * ctx.updateUrl...
 * 
 * Ne fait rien si l'URL n'a pas changé.
 *  
 * @return
 */
Tangane.Url.prototype.update = function _tangane_url_update(force) 
{
  /*<*/ logNotice('Url : %supdate requested',force?'forced ':''); /*>*/
  var url = this.encode(this._root.get());  
  
  /*<*/ logNotice('Url : new state : %s : old state : %s', url, this.url()); /*>*/
  if (url != this.url())
    $.address.value(url);
  else if (force)
  {  
    /*<*/ logNotice('Url : not changed, forced, notify root'); /*>*/
    this._root.set(this.decode(url));
  }    
};

/**
 * Instance du singleton.
 */
Tangane.Url.instance = new Tangane.Url();

// Equipement...

Tangane.Context.root = Tangane.Context.root
  .set('updateUrl', function _tangane_context_root_updateUrl(force) { Tangane.Url.instance.update(force); });

//Tangane.Context.root.updateUrl.bind(function(force) { alert( 'test' /*Tangane.Url.instance.get()*/ ); });

/* ------------ Url Boxes ------------*/

/**
 * Les Url-Box sont les outils qui permettent de dire, sur le serveur, "voici du HTML, 
 * dont les portions X, Y et Z seront récupérées en AJAX et dont l'état sera mémorisé dans
 * l'URL de la page". Autrement dit, c'est le moteur de base du site full AJAX.
 * 
 * La page est constitué d'une arborescence de Url-Box, enracinées en une unique Url-Box
 * spécifique.
 * 
 * Exemple èd'arborescence:
 * 
 * +- [root]
 *     |
 *     +- [contents]
 *     |   |
 *     |   +- [elements]
 *     |   |
 *     |   +- [controls]
 *     |
 *     +- [header]
 *         
 * Chaque noeud de l'arborescence a un état qui lui est propre. Par exemple, le header
 * peut contenir une liste d'accés rapide au choix parmi "utilisateurs", "dossiers" ou 
 * "tâches", donc il doit se souvenir de laquelle il s'agit pour afficher la même
 * lorsqu'on change d'écran. 
 */
Tangane.Url.Box = function _tangane_url_box(src, ctx, config, _next) {
  var innerBox = null, element = src;

  $(function() {

    if (typeof element != 'string' && '$self' in element) element = element.$self;

    if ('select' in config) element = $(element).find(config.select);

    element = $(element);

    /*[*/
    if (!element || !element.length) {
      logError('ERROR: Url-Box, selector (%o).find(%s) matched nothing', src, config.select);
      throwError("Url-Box created without a corresponding target on the page");
    }
    /*]*/

    /*<*/logNotice('Url-Box %o : create : %o', element, config); /*>*/

    new Tangane.Box(element, ctx, postConstruct);
  });

  function postConstruct(created) {
    /*<*/logNotice('Url-Box %o : inner box done : %o', element, created); /*>*/

    innerBox = created;

    innerBox._urlBox = {
      urlFormatter: pack(config.urlFormatter),
      urlRoot: config.urlRoot,
      catchState: config.catchState ? true : false,
      parentInfo: [],
      state: [],
      oldUrl: null,
      forcedState: null,
      modified: false
    };

    /**
    * Si un ancêtre de cette box porte le nom fourni, cet ancêtre est
    * renvoyé. 
    * 
    * On utilise en général les ancêtres nommés pour définir des ancêtres
    * communs accessibles à deux noeuds.
    * 
    * @param name
    * @return Tangane.Url.Box
    */
    innerBox.getParent = function _tangane_url_box_getParent(name) {
      /*[*/
      if (!(name in this._urlBox.parentInfo)) {
        logError("ERROR: In %o, no parent %s, parents are: %o", element, name, this._urlBox.parentInfo);
        throwError("Could not find requested parent for Url-Box");
      }
      /*]*/

      /*<*/logNotice('Url-Box %o : parent : %s -> %o', name, innerBox._urlBox.parentInfo[name]); /*>*/

      return innerBox._urlBox.parentInfo[name];
    };

    /**
    * Fournit à la box la liste de ses parents nommés, avec leur nom. Par
    * convention, on décide que si deux parents ont le même nom, le plus
    * proche est fourni. 
    * 
    * @param info Hash de Tangane.Url.Box
    * @return void
    */
    innerBox.applyParentInfo = function _tangane_url_box_applyParentInfo(info) {
      /*<*/logNotice('Url-Box %o : receive parent info : %o', element, info); /*>*/
      this._urlBox.parentInfo = info;
    };

    /**
    * Invalidation: la prochaine requête obligera la box à rafraîchir son 
    * contenu depuis le serveur, même si c'est le même état en réalité.
    */
    innerBox.invalidate = function _tangane_url_box_invalidate() {
      this._urlBox.oldUrl = "";
      return this;
    };

    /**
    * Modifie l'état de la box. La modification n'est pas instantanée: elle
    * est propagée jusqu'à la racine, puis revient en mettant à jour tout
    * l'arbre. Cette opération est donc asynchrone. 
    * 
    * Il n'y a pas de callback pour indiquer la fin de l'opération.
    * 
    * @param data
    * @return void
    */
    innerBox.setState = function _tangane_url_box_setState(data) {
      /*[*/
      if (typeof data != "object" || !data) {
        logError("Error: in %o, invalid state %o.", element, data);
        throwError("State for Url-Box should be an array or dictionary");
      }
      /*]*/

      /*<*/logNotice('Url-Box %o : set url : %o', element, data); /*>*/

      this._urlBox.forcedState = data ? data : [];
      this._urlBox.modified = true;
      ctx.updateUrl(this._urlBox.oldUrl == "" || this._urlBox.catchState);
    };

    /**
    * Récupère les informations sur la branche aboutissant à la box. 
    * Il s'agit plus exactement du parentInfo qui sera fourni à tous
    * les enfants de cette box.
    * 
    * On le construit à partir du parentInfo de cette Box, ainsi que
    * de son config.name (s'il existe).
    * 
    * @return Hash de Tangane.Url.Box
    */
    innerBox.branchInfo = function _tangane_url_box_branchInfo() {
      var info = $.extend({}, this._urlBox.parentInfo);

      if ('name' in config) {
        info[config.name] = this;
      }

      /*<*/logNotice('Url-Box %o : get branch info : %o', element, info); /*>*/
      return info;
    };

    /**
    * Application directe de l'état. Ne pas utiliser directement (ni 
    * applyState, qui est le listener correspondant), même si on peut s'y 
    * abonner pour réagier. Cette fonction met à jour l'état, ce qui provoque
    * un rechargement du contenu de la box.
    * 
    * Fonction asynchrone à durée indéterminée, fait une requête sur le serveur
    * si les données n'étaient pas déjà disponibles.
    * 
    * @param data
    * @return void
    */
    innerBox.rawApplyState = function _tangane_url_box_rawApplyState(data) {
      var self = this;

      /*<*/logNotice('Url-Box %o : apply url : %o', element, data); /*>*/

      data = data || {};

      if (this._urlBox.catchState) data = this._urlBox.forcedState;

      this._urlBox.state = {};
      var childState = {};

      for (var k in data) {
        var v = data[k];
        if (k.charAt(0) != '_') self._urlBox.state[k] = v;
        else childState[k.substr(1)] = v;
      };

      var url = this._urlBox.urlFormatter(this._urlBox.urlRoot, this._urlBox.parentInfo, this._urlBox.state);

      /*<*/
      logNotice('-- apply : seek at %s : old is %s', url, this._urlBox.oldUrl);
      /*>*/

      var shouldReload = this._urlBox.oldUrl != url;
      this._urlBox.oldUrl = url;

      if (shouldReload) {
        ctx.onUrlBoxLoadStart(this, url);
        this.fromUrl(url, {}, afterLoad);
      } else afterLoad();

      function afterLoad() {

        /*<*/
        logNotice('Url-Box %o : inner loaded : apply %o : %o',
             element, childState, self._inner);
        /*>*/

        if (shouldReload) ctx.onUrlBoxLoadFinish(self, url);

        $.each(self._inner, function(i, child) {

          if ('applyParentInfo' in self[child])
            self[child].applyParentInfo(self.branchInfo());

          if ('applyState' in self[child]) {
            self[child].applyState(child in childState ? childState[child] : {});
          }
        });

        self._urlBox.modified = false;
      };
    };

    innerBox.applyState = new Tangane.Listener(innerBox, innerBox.rawApplyState);

    /**
    * Récupère l'état (récursif) de la box.
    * 
    * @return data
    */
    innerBox.getState = function _tangane_url_box_getState() {

      if (this._urlBox.catchState) return {};

      var returned = this._urlBox.forcedState;

      if (!this._urlBox.modified) {
        var self = this;

        returned = {};

        $.each(this._inner, function(i, child) {
          if ('getState' in self[child])
            returned['_' + child] = self[child].getState();
        });

        for (var k in this._urlBox.state) {
          returned[k] = this._urlBox.state[k];
        }
      }
      else {
        this._urlBox.modified = false;
      }

      /*<*/logNotice('Url-Box %o : get url : %o', element, $.extend(true, returned, {})); /*>*/

      return $.extend(true, returned, {});
    }

    if (_next) _next(innerBox);
  }
};

/**
 * Utilise une box comme racine du système.
 */
Tangane.Url.Box.makeRoot = function _tangane_url_box_makeRoot(box, dflt) {
  /*<*/logNotice('Root box : %o', box); /*>*/

  Tangane.Url.Box.root = box;

  Tangane.Url.instance.setRoot({
    get: function() { return box.getState(); },
    set: function(e) {
      if (!e) e = dflt || {};
      box.applyState(e);
    }
  });

  Tangane.Context.root.onRootBoxRegister();
};

// Initialiser le contexte avec les canaux de réflection sur les url-box
Tangane.Context.root = Tangane.Context.root

/**
 * Appelé à chaque fois qu'une Url Box commence son chargement
 */
  .channel('onUrlBoxLoadStart')

/**
 * Appelé à chaque fois qu'une Url Box finit son chargement
 */
  .channel('onUrlBoxLoadFinish')
  
/**
 * Appelé après qu'une nouvelle root url box soit enregistrée.
 */
  .channel('onRootBoxRegister');
  
/**
 * Change le contenu d'une box à partir de la box actuelle.
 * 
 * Remonte jusqu'à l'ancêtre cfg.parent (si fourni, sinon reste en place), 
 * puis redescend suivant les membres du tableau cfg.sub (si fourni), 
 * met à jour l'état de la box ainsi obtenue en utilisant config.state.
 * 
 * Si cfg.refresh est fourni et vaut 'true', invalide le contenu de la box
 * (autrement dit, il y aura forcément rechargement depuis le serveur).
 *
 * Si config.select est fourni, alors l'élément box.$self.find(config.select) est
 * branché pour exécuter le changement en cas de click.
 * 
 * En cas contraire, on peut utiliser this.apply() pour exécuter le changement.
 *  
 * @param box La box depuis laquelle on fait le changement.
 * @param ctx Un contexte, ignoré.
 * @param config La configuration de l'objet.
 * @param _next Callback asynchrone, appelé sur l'objet.
 */
Tangane.Url.Box.Changer = function _tangane_url_box_changer(box, ctx, config, _next) {

  /*<*/logNotice("Url Changer : create : %o", config); /*>*/

  /*[*/
  Assert.isType(config, "object");
  Assert.hasMember(config, 'state');
  Assert.isType(config.state, 'object');
  /*]*/

  var self = this;

  self.apply = function _tangane_url_box_changer_apply() 
  {
    /*<*/logNotice("Url Changer : apply : %s : %o : %o", config.parent || "", config.sub || [], config.state); /*>*/

    var target = box;

    if ('parent' in config && config.parent) 
    {
      target = target.getParent(config.parent);
    }

    if ('sub' in config && config.sub) 
    {
      $.each(config.sub, function(i, seg) 
      {
        /*[*/
        if (!target || typeof target != "object" || !seg in target )
        {
          logError("ERROR: attempting to access member %s of %o", seg, target);
          throwError("Invalid Url-Box selection in Url Changer");
        }
        /*]*/
        
        target = target[seg];
      });
    }
    
    if ('refresh' in config && config.refresh)
    {
        target.invalidate();
    }

    target.setState(config.state);
  };

  if ('trigger' in config && config.trigger) 
  {
    /*<*/
    logNotice("Url Changer : bind trigger : %s", config.trigger); 
    /*>*/
    
    ctx.trigger.bind(function _tangane_url_box_changer_trigger(name,state) 
    {
      /*<*/
      logNotice("Url Changer : received trigger : %s, %s", name, config.trigger); 
      /*>*/
      
      if (name === config.trigger)
      {
        var oldState = config.state;
        
        if (state) 
            config.state = state;
            
        self.apply();
        
        config.state = oldState;
      }
    });
  }

  if ('select' in config && config.select) 
  {
    if (typeof config.select === 'string')
    {
      self.$self = box.$self.find(config.select);

      /*[*/    
      if (!self.$self || !self.$self.length) 
      {
        logError('ERROR: Url-Box Changer, selector (%o).find(%s) matched nothing', box.$self, config.select); 
        throwError("Url-Box Changer created without a corresponding target on the page");
      }
      /*]*/
    }
    else
      self.$self = config.select;


    self.$self.click(self.apply);
    
    /*<*/
    logNotice("Url Changer : bind : %s -> %o", config.select, self.$self); 
    /*>*/
  }

  if (_next) _next(self);
  
  return self;
};

/**
 * Attache d'un formulaire à un trigger. L'argument doit contenir un champ 
 * 'trigger' donnant le nom du trigger, sinon rien ne se passe.
 */
Tangane.Url.Box.formTrigger = function _tangane_url_box_formTrigger(context)
{
  var jsonData = context.get_data();
  
  if (jsonData && jsonData.charAt(0) == '{') 
  {
    var data = eval('('+jsonData+')'), state = {};
        
    if ('state' in data && data.state)
    {
        state = data.state;
    }
    
    if ('trigger' in data && data.trigger) 
    {
      /*<*/
      logNotice('Trigger %s executed by form', data.trigger);
      /*>*/
      
      Tangane.Context.root.trigger(data.trigger, state);
    }
  }
};

/**
 * Formatage configurable des URLs. La racine fournie est de la forme:
 *
 *   /segment/{variable}?valeur={variable} 
 *
 * Les {variable} sont remplacées par la valeur de la variable correspondante
 * suivant la règle:
 *
 *   x : valeur de la variable x de l'état de la box.
 *   x.y : valeur de la variable y de l'état du parent nommé x.
 *   x.y.z : valeur de la variable z de l'état du fils nommé y du parent nommé x.
 * 
 * On peut descendre sur n'importe quelle profondeur dans des fils, mais ils doivent
 * tous exister (si un fils n'existe pas ou un parent nommé n'existe pas, il y a une
 * assertion pour le signaler).
 * 
 * Une variable d'état non définie renvoie une chaîne vide. On applique sur les valeurs
 * un escaping standard pour ne pas en casser la structure.
 */
Tangane.Url.Box.format = function _tangane_url_box_format(root, parent, state) {
  /*<*/
  logNotice("Url formatter: %s : %o : %o", root, parent, state);
  /*>*/

  // Variables : {foo}
  var matches = root.match(/{[^}]*}/g);

  if (matches) {
    $.each(matches, function(k, variable) {

      // Option : foo:bar
      var option = variable.match(/:[^}]*/);
      option = option && option.length > 0 ? option[0].substr(1) : '';

      // Segment : foo.bar.qux
      var segments = variable.replace(/:.*$/, '').match(/[^{}.]+/g), data = state;

      /*[*/
      Assert.test(segments)("ERROR: Url Construction : variable %s is invalid", variable);
      /*]*/

      if (segments.length > 1) {
        // Plus d'un segment : on vise un parent, puis on descend
        var mem = segments.shift();

        /*[*/
        Assert.test(mem in parent && parent[mem])("ERROR: Url Construction : variable %s : no root %s in %o", variable, mem, parent);
        /*]*/

        var current = parent[mem];

        while (segments.length > 1) {
          var mem = segments.shift();

          /*[*/
          Assert.test(mem in current)("ERROR: Url Construction : variable %s : no member %s in %o", variable, mem, current);
          /*]*/

          current = current[mem];
        }

        data = current._urlBox.state;
      }

      var mem = segments.shift();
      var result = encodeURIComponent(mem in data ? data[mem] : option);

      // Remplacement de la variable par sa valeur
      root = root.replace(variable, result);
    });
  }
  
  return root;
};

pack('Tangane.Reflection.Url.Box').tree = function()
{
  var id = '#--url-box-tree';
  if( $(id).length > 0 ) return;
    
  var $root = $('<table id="'+id.substr(1)+'"><tr></tr></table>').css({
    position: 'fixed',
    right: '5px',
    bottom: '5px',
    zIndex: '9999'
  });
    
  var colors = ['#FFF','#EEE','#DDD','#CCC','#BBB','#AAA'];
    
  Tangane.Url.Box.root._reflection = {
    $node :
        $('<td></td>').appendTo($root.find('tr')),
    depth : 0
  };    
      
  Tangane.Context.root.onUrlBoxLoadStart.bind(
    renderLoading
  );
  
  Tangane.Context.root.onUrlBoxLoadFinish.bind(
    render
  );
      
  render(Tangane.Url.Box.root);
  
  function renderLoading(box,url)
  {
    $root.appendTo('body');
    
    box._reflection.startTime = +(new Date());
    box._reflection.$node
      .html('<strong><code>'+url+'</code></strong><br/>'+
            '<img style="border:1px solid black" src="http://www.sanbaldo.com/wordpress/wp-content/gray_busy.gif"/>')
      .css({textAlign:'center'})     
  }
  
  function render(box)
  {  
    $root.appendTo('body');
    
    var $node = box._reflection.$node;
    $node.html('').css({
      textAlign : 'center',
      color: 'black',
      border: '1px solid black',
      backgroundColor : colors[box._reflection.depth]
    });
    
    $node.append('<strong><code>'+box._urlBox.oldUrl+'</code></strong>');
    
    if (box._reflection.startTime) 
      $node.append('<br/>' +  (+(new Date()) - box._reflection.startTime) + 'ms');
            
    var $table = $('<table><thead><tr></tr></thead><tbody><tr></tr></tbody></table>');
    $tr = $table.find('tbody tr');
    $th = $table.find('thead tr');       
              
    var found = false;              
              
    $.each(box._inner,function(ign,k){
      if ('_urlBox' in box[k]) {
        box[k]._reflection = {
          $node : $('<td/>').appendTo($tr),
          depth : box._reflection.depth+1
        };        
        $('<td>'+k+'</td>').css({backgroundColor:colors[box._reflection.depth+1]}).appendTo($th);
        render(box[k]);
        found = true;
      }
    });
        
    if (found) {
      $table.css({margin:'2px'}).appendTo($node);
    }
  }
};

//$(function(){
//  Tangane.Context.root.onRootBoxRegister.bind(
//    function(){
//        Tangane.Reflection.Url.Box.tree();
//    }
//  );
//});
pack('LivePME.Site');

LivePME.Site.controller = function(ig1, parents, ig2) {
  var data = LivePME.Site.getAction(parents['page']._urlBox.state);
  if (!data) {
    logError('Impossible de traduire %o', parents['page']._urlBox.state);
  }
  return data[0];
};

LivePME.Site.getTab = function(state) {
  var data = LivePME.Site.getAction(state);
  return data[1];
};

LivePME.Site.getAction = function(state) {
  switch (state.mode || '') {
    case 'create':
      return LivePME.Site.create(state.type || '', state.filter || '', state.org || '', state.lang || '');
    case 'edit':
      return LivePME.Site.edit(state.type || '', state.filter || '0', state.org || '', state.lang || '', state.id || '');
    case 'view':
      return LivePME.Site.view(state.type || '', state.filter || '0', state.org || '', state.lang || '', state.id || '');
    case 'list':
      return LivePME.Site.list(state.type || '', state.filter || '0', state.org || '', state.lang || '');
    case 'custom':
    default:
      return LivePME.Site.custom(state.type || '', state.filter || '0', state.org || '', state.lang || '');
  }
};

function nullFilterValues(filter) {
  var car = "_";
  if (!filter.id) filter.id = car;
  /*if (!filter.tsp) filter.tsp = car;
  if (!filter.tsc) filter.tsc = car;
  if (!filter.tss) filter.tss = car;
  if (!filter.tsa) filter.tsa = car;
  if (!filter.tst) filter.tst = car;
  if (!filter.mis) filter.mis = car;
  if (!filter.t) filter.t = car;
  if (!filter.accounted) filter.accounted = car;
  if (!filter.state) filter.state = car;
  if (!filter.doctype) filter.doctype = car;
  if (!filter.nature) filter.nature = car;
  if (!filter.viewUI) filter.viewUI = car;
  if (!filter.q) filter.q = car;*/
  return filter;
}

function setCurrentOrganization(org) {
  var orgOld = getCurrentOrganization();
  if (org != orgOld) {
    //dans le cas ou la société courante est différente de la société que l'on souhaite charger, il faut aussi mettre le header à jour
    loadHeaderBox(org);
  }
}

// Onglet et sous onglet du menu de navigation
LivePME.Site.tab = {
  //TODO:TRANSLATE
  accueil: ['Accueil',
    ['Dashboard', 'ShoesBox', 'Cabinet', 'Client', 'TaskGlobal', 'ExtJS4']
  ],
  compta: ['Comptabilite',
    ['General', 'DocComptaCustomer', 'DocComptaSupplier', 'DocComptaBank', 'DocComptaFiscal', 'Discussions', 'Export']
  ],
  social: ['Social',
    ['Documents', 'Discussions', 'Employees']
  ],
  juridique: ['Juridique',
    ['Documents', 'Discussions']
  ],
  document: ['Document',
    ['PlanFolder', 'UploadedDocs', 'Recherche']
  ],
  espace: ['MonEspace',
    ['Docsmysite', 'Tasksmysite']
  ],
  news: ['Actualite',
    ['Accueil']
  ]
}

LivePME.Site.custom = function(type, filter, org, lang) {
  if (!lang) lang = 'fr-Fr';
  setCurrentOrganization(org);
  var orgLang = org + '/' + lang;
  var tabs = LivePME.Site.tab;

  switch (type) {
    case 'search':
      var id = getCurrentTextSearch();
      return ['/Search/ShowList/' + orgLang + '/' + id, [tabs.document[0], tabs.document[1][2]]];
    case 'employee':
      return ['/Employee/ShowDetails/' + orgLang, []];
    case 'profile':
      return ['/Home/GotoUser/' + orgLang, []];
    case 'termsofuse':
      return ['/SubMenu/TermsOfUse/' + orgLang, []];
    case 'rss':
      return ['/SubMenu/RssStream/' + orgLang, []];
    case 'parameters':
      return ['/SubMenu/Parametres/' + orgLang, []];
    case 'support':
      return ['/SubMenu/Support/' + orgLang, []];
    case 'help':
      return ['/SubMenu/Help/' + orgLang, []];
    case 'legal':
      return ['/SubMenu/LegalMention/' + orgLang, []];
      /*case 'dashboard-admin':
      return ['/SubMenu/Administration/' + orgLang + '/DashBoard/', ['Administration', 'Dashboard']];*/
    case 'upload-compta':
      return ['/DocumentCompta/UploadView/' + orgLang, [tabs.compta[0], tabs.compta[1][0]]];
    case 'upload-juridique':
      return ['/DocumentJuridique/UploadView/' + orgLang, [tabs.juridique[0], tabs.juridique[1][0]]];
    case 'upload-social':
      return ['/DocumentSocial/UploadView/' + orgLang, [tabs.social[0], tabs.social[1][0]]];
    case 'plan-folder':
      return ['/PlanFolder/Index/' + orgLang, [tabs.document[0], tabs.document[1][0]]];
    case 'export-compta':
      return ['/Export/Index/' + orgLang + '/' + filter.id + '?exportList=' + filter.exportList + '&details=' + filter.details, [tabs.compta[0], tabs.compta[1][6]]];
    case 'dashboard':
    default:
      return ['/SubMenu/Accueil/' + orgLang + '/Dashboard/', [tabs.accueil[0], tabs.accueil[1][0]]];
  }
};


LivePME.Site.list = function(type, filter, org, lang) {
  if (!lang) lang = 'fr-Fr';
  var orgLang = org + '/' + lang;
  setCurrentOrganization(org);

  filter = nullFilterValues(filter);
  var tabs = LivePME.Site.tab;

  switch (type) {
    case 'results-search':
      var textSearch = filter.id;  //getCurrentTextSearch();
      if (!textSearch) textSearch = "_";
      return ['/Search/ShowList/' + orgLang + '/' + textSearch + '/' + filter.page + '?useFiscalFilter=' + filter.useFiscalFilter, [tabs.document[0], tabs.document[1][2]]];

    case 'compta-disc':
      return ['/DiscussionCompta/ShowList/' + orgLang + '/' + filter.id + '/' + filter.page + '?discstate=' + filter.discstate, [tabs.compta[0], tabs.compta[1][5]]];
    case 'docs-compta':
      return ['/DocumentCompta/ShowList/' + orgLang + '/' + filter.id + '/' + filter.page + '?doctype=' + filter.doctype + '&viewUI=' + filter.viewUI + '&useFiscalFilter=' + filter.useFiscalFilter, [tabs.compta[0], tabs.compta[1][0]]];

    case 'docs-compta-c':
      return ['/DocComptaCustomer/ShowList/' + orgLang + '/' + filter.id + '/' + filter.page + '?accounted=' + filter.accounted + '&nature=' + filter.nature + '&state=' + filter.state + '&doctype=' + filter.doctype + '&days=' + filter.days + '&viewUI=' + filter.viewUI + '&q=' + filter.q, [tabs.compta[0], tabs.compta[1][1]]];
    case 'docs-compta-s':
      return ['/DocComptaSupplier/ShowList/' + orgLang + '/' + filter.id + '/' + filter.page + '?accounted=' + filter.accounted + '&nature=' + filter.nature + '&state=' + filter.state + '&doctype=' + filter.doctype + '&days=' + filter.days + '&viewUI=' + filter.viewUI + '&q=' + filter.q, [tabs.compta[0], tabs.compta[1][2]]];
    case 'docs-compta-b':
      return ['/DocComptaBank/ShowList/' + orgLang + '/' + filter.id + '/' + filter.page + '?doctype=' + filter.doctype + '&viewUI=' + filter.viewUI, [tabs.compta[0], tabs.compta[1][3]]];
    case 'docs-compta-f':
      return ['/DocComptaFiscal/ShowList/' + orgLang + '/' + filter.id + '/' + filter.page + '?doctype=' + filter.doctype + '&viewUI=' + filter.viewUI, [tabs.compta[0], tabs.compta[1][4]]];

    case 'collection':
      return ['/Collection/ShowList/' + orgLang + '/' + filter.id + '/' + filter.page, [tabs.document[0], tabs.document[1][1]]];
    case 'docs-collection':
      return ['/Collection/ShowDocUploadList/' + orgLang + '/' + filter.id + '/' + filter.page + '?entId=' + filter.entId, [tabs.document[0], tabs.document[1][1]]];
    case 'docs-collection-pf':
      return ['/Collection/ShowDocUploadListPF/' + orgLang + '/' + filter.id + '/' + filter.page + '?elmId=' + filter.elmId , [tabs.document[0], tabs.document[1][1]]];

    case 'docs-mysite':
      return ['/MySite/ShowList/' + orgLang + '/' + filter.id + '/' + filter.page + '?eln=' + filter.eln + '&doctype=' + filter.doctype, [tabs.espace[0], tabs.espace[1][0]]];
    case 'tasks-mysite':
      return ['/Task/ShowListMySite/' + orgLang + '/' + filter.id + '/' + filter.page + '?tsp=' + filter.tsp + '&tsc=' + filter.tsc + '&tss=' + filter.tss + '&tsa=' + filter.tsa + '&tst=' + filter.tst + '&mis=' + filter.mis + '&t=' + filter.t, [tabs.espace[0], tabs.espace[1][1]]];
    case 'tasks-global':
      return ['/Task/ShowList/' + orgLang + '/' + filter.id + '/' + filter.page + '?tsp=' + filter.tsp + '&tsc=' + filter.tsc + '&tss=' + filter.tss + '&tsa=' + filter.tsa + '&tst=' + filter.tst + '&mis=' + filter.mis + '&t=' + filter.t + '&q=' + filter.q, [tabs.accueil[0], tabs.accueil[1][4]]];
    case 'docs-employee':
      return ['/EmployeeDocument/ShowList/' + orgLang + '/' + filter.userId + '/' + filter.page + '?eln=' + filter.eln + '&from=' + filter.from + "&doctype=" + filter.doctype, [tabs.social[0], tabs.social[1][2]]];

      /*case 'compta-pieces':
      return ['/PiecesCompta/ShowList/' + orgLang + '/' + filter.id + '/' + filter.page + '?accounted=' + filter.accounted + '&nature=' + filter.nature + '&state=' + filter.state, [tabs.compta[0], 'AccountingElements']];*/

    case 'social-disc':
      return ['/DiscussionSocial/ShowList/' + orgLang + '/' + filter.id + '/' + filter.page + '?discstate=' + filter.discstate, [tabs.social[0], tabs.social[1][1]]];
    case 'employees':
      return ['/Employee/ShowList/' + orgLang + '/' + filter + '?useSessionPosition=' + filter.useSessionPosition, [tabs.social[0], tabs.social[1][2]]];
    case 'payroll':
      return ['/Employee/PayrollShowList/' + orgLang + '/' + filter.id + '/' + filter.page, [tabs.social[0], tabs.social[1][2]]];
    case 'docs-social':
      return ['/DocumentSocial/ShowList/' + orgLang + '/' + filter.id + '/' + filter.page + '?doctype=' + filter.doctype + '&viewUI=' + filter.viewUI + '&useFiscalFilter=' + filter.useFiscalFilter, [tabs.social[0], tabs.social[1][0]]];
    case 'docs-profile':
      return ['/Home/PostedDocList/' + orgLang + '/' + filter.id + '/' + filter.page + '?doctype=' + filter.doctype, ['', '']];
    case 'docs-profile-ca':
      return ['/Cabinet/PostedDocList/' + orgLang + '/' + filter.id + '/' + filter.page + '?doctype=' + filter.doctype, [tabs.accueil[0], tabs.accueil[1][2]]];
    case 'docs-profile-cl':
      return ['/Client/PostedDocList/' + orgLang + '/' + filter.id + '/' + filter.page + '?doctype=' + filter.doctype, [tabs.accueil[0], tabs.accueil[1][3]]];
    case 'docs-juridique':
      return ['/DocumentJuridique/ShowList/' + orgLang + '/' + filter.id + '/' + filter.page + '?doctype=' + filter.doctype + '&viewUI=' + filter.viewUI + '&useFiscalFilter=' + filter.useFiscalFilter, [tabs.juridique[0], tabs.juridique[1][0]]];
    case 'disc-juridique':
      return ['/DiscussionJuridique/ShowList/' + orgLang + '/' + filter.id + '/' + filter.page + '?discstate=' + filter.discstate, [tabs.juridique[0], tabs.juridique[1][1]]];
    case 'disc-profile':
      return ['/Home/DiscussionShowList/' + orgLang + '/' + filter.id + '/' + filter.page, ['', '']];
    case 'disc-profile-ca':
      return ['/Cabinet/DiscussionShowList/' + orgLang + '/' + filter.id + '/' + filter.page, [tabs.accueil[0], tabs.accueil[1][2]]];
    case 'disc-profile-cl':
      return ['/Client/DiscussionShowList/' + orgLang + '/' + filter.id + '/' + filter.page, [tabs.accueil[0], tabs.accueil[1][3]]];
    case 'clients':
      return ['/SubMenu/Administration/' + orgLang + '/Clients/', ['Administration', 'Clients']];
    case 'collaborators':
      return ['/SubMenu/Administration/' + orgLang + '/Collaborators/', ['Administration', 'Collaborators']];
    case 'document':
      return ['/Document/ShowList/' + orgLang + '/' + filter.id + '/' + filter.page, [tabs.accueil[0], tabs.accueil[1][1]]];

    case 'supplier':
      return ['/DocComptaSupplier/ShowSupplierList/' + orgLang + '/' + filter.id + '/' + filter.page + '?accounted=' + filter.accounted + '&nature=' + filter.nature + '&state=' + filter.state + '&doctype=' + filter.doctype + '&useSessionPosition=' + filter.useSessionPosition, [tabs.compta[0], tabs.compta[1][2]]];
    case 'docs-supplier':
      return ['/DocComptaSupplier/ShowDocEntityList/' + orgLang + '/' + filter.id + '/' + filter.page + '?accounted=' + filter.accounted + '&nature=' + filter.nature + '&state=' + filter.state + '&doctype=' + filter.doctype + '&days=' + filter.days + '&viewUI=' + filter.viewUI + '&q=' + filter.q + '&entId=' + filter.entId, [tabs.compta[0], tabs.compta[1][2]]];

    case 'customer':
      return ['/DocComptaCustomer/ShowCustomerList/' + orgLang + '/' + filter.id + '/' + filter.page + '?accounted=' + filter.accounted + '&nature=' + filter.nature + '&state=' + filter.state + '&doctype=' + filter.doctype + '&useSessionPosition=' + filter.useSessionPosition, [tabs.compta[0], tabs.compta[1][1]]];
    case 'docs-customer':
      return ['/DocComptaCustomer/ShowDocEntityList/' + orgLang + '/' + filter.id + '/' + filter.page + '?accounted=' + filter.accounted + '&nature=' + filter.nature + '&state=' + filter.state + '&doctype=' + filter.doctype + '&days=' + filter.days + '&viewUI=' + filter.viewUI + '&q=' + filter.q + '&entId=' + filter.entId, [tabs.compta[0], tabs.compta[1][1]]];

    case 'bank':
      return ['/DocComptaBank/ShowBankList/' + orgLang + '/' + filter.id + '/' + filter.page + '?useSessionPosition=' + filter.useSessionPosition, [tabs.compta[0], tabs.compta[1][3]]];
    case 'docs-bank':
      return ['/DocComptaBank/ShowDocBankList/' + orgLang + '/' + filter.id + '/' + filter.page + '?doctype=' + filter.doctype + '&viewUI=' + filter.viewUI + '&entId=' + filter.entId, [tabs.compta[0], tabs.compta[1][3]]];

    case 'fiscal':
      return ['/DocComptaFiscal/ShowFiscalList/' + orgLang + '/' + filter.id + '/' + filter.page + '?useSessionPosition=' + filter.useSessionPosition, [tabs.compta[0], tabs.compta[1][4]]];
    case 'docs-fiscal':
      return ['/DocComptaFiscal/ShowDocFiscalList/' + orgLang + '/' + filter.id + '/' + filter.page + '?doctype=' + filter.doctype + '&viewUI=' + filter.viewUI + '&entId=' + filter.entId, [tabs.compta[0], tabs.compta[1][4]]];

    case 'social':
      return ['/DocumentSocial/ShowSocialList/' + orgLang + '/' + filter.id + '/' + filter.page + '?useSessionPosition=' + filter.useSessionPosition, [tabs.social[0], tabs.social[1][0]]];
    case 'docs-social-concern':
      return ['/DocumentSocial/ShowDocSocialList/' + orgLang + '/' + filter.id + '/' + filter.page + '?doctype=' + filter.doctype + '&viewUI=' + filter.viewUI + '&entId=' + filter.entId, [tabs.social[0], tabs.social[1][0]]];

    case 'juridique':
      return ['/DocumentJuridique/ShowJuridiqueList/' + orgLang + '/' + filter.id + '/' + filter.page + '?useSessionPosition=' + filter.useSessionPosition, [tabs.juridique[0], tabs.juridique[1][0]]];
    case 'docs-juridique-concern':
      return ['/DocumentJuridique/ShowDocJuridiqueList/' + orgLang + '/' + filter.id + '/' + filter.page + '?doctype=' + filter.doctype + '&viewUI=' + filter.viewUI + '&entId=' + filter.entId, [tabs.juridique[0], tabs.juridique[1][0]]];

    case 'news':
      return ['/News/ShowList/' + orgLang + '/' + filter.id + '/' + filter.page, [tabs.news[0], tabs.news[1][0]]];
    default:
      return ['/SubMenu/Accueil/' + orgLang + '/Dashboard/', [tabs.accueil[0], tabs.accueil[1][0]]];
  }
};

LivePME.Site.create = function(type, filter, org, lang) {
  if (!lang) lang = 'fr-Fr';
  var orgLang = org + '/' + lang;
  setCurrentOrganization(org);
  var tabs = LivePME.Site.tab;
  switch (type) {
    case 'compta-disc-create':
      return ['/DiscussionCompta/ShowCreate/' + orgLang + '/' + filter, [tabs.compta[0], tabs.compta[1][5]]];
    case 'social-disc-create':
      return ['/DiscussionSocial/ShowCreate/' + orgLang + '/' + filter, [tabs.social[0], tabs.social[1][1]]];
    case 'juridique-disc-create':
      return ['/DiscussionJuridique/ShowCreate/' + orgLang + '/' + filter, [tabs.juridique[0], tabs.juridique[1][1]]];
    case 'Employee-create':
      return ['/Employee/ShowCreate/' + orgLang + '/' + filter, [tabs.social[0], tabs.social[1][2]]];
  }
};

LivePME.Site.edit = function(type, filter, org, lang, id) {
  if (!lang) lang = 'fr-Fr';
  var orgLang = org + '/' + lang;
  var tabs = LivePME.Site.tab;
  switch (type) {
    case 'Employee-edit':
      return ['/Employee/ShowEdit/' + orgLang + '/' + id + '/' + '?from=' + filter.from, [tabs.social[0], tabs.social[1][2]]];
    case 'Employee-AssignRights':
      return ['/Employee/ShowAssignRights/' + orgLang + '/' + id + '/' + '?from=' + filter.from, [tabs.social[0], tabs.social[1][2]]];
    case 'Profile-edit-cabinet':
      return ['/Cabinet/ShowEditUserProfile/' + orgLang + '/' + id, [tabs.accueil[0], tabs.accueil[1][2]]];
    case 'Avatar-edit-cabinet':
      return ['/Cabinet/ShowEditAvatar/' + orgLang + '/' + id, [tabs.accueil[0], tabs.accueil[1][2]]];
    case 'Profile-edit-client':
      return ['/Client/ShowEditUserProfile/' + orgLang + '/' + id, [tabs.accueil[0], tabs.accueil[1][3]]];
    case 'Avatar-edit-client':
      return ['/Client/ShowEditAvatar/' + orgLang + '/' + id, [tabs.accueil[0], tabs.accueil[1][3]]];
  }
};

LivePME.Site.view = function(type, filter, org, lang, id) {
  var orgLang = org + '/' + lang;
  setCurrentOrganization(org);
  filter = nullFilterValues(filter);
  var tabs = LivePME.Site.tab;
  switch (type) {
    case 'employee':
      if (typeof (filter.sortField) == 'undefined')
        filter.sortField = '';
      if (typeof (filter.sortDirection) == 'undefined')
        filter.sortDirection = '';
      return ['/Employee/ShowDetails/' + orgLang + '/' + id + '/' + filter.page + '?from=' + filter.from + '&sortField=' + filter.sortField + '&sortDirection=' + filter.sortDirection + '&sortPosition=' + filter.sortPosition + '&useId=' + filter.useId, [tabs.social[0], tabs.social[1][2]]];
    case 'profile':
      return ['/Home/GotoUser/' + orgLang + '/' + id, []];
    case 'cabinet-profile':
      return ['/Cabinet/ViewUser/' + orgLang + '/' + id, [tabs.accueil[0], tabs.accueil[1][2]]];
    case 'client-profile':
      return ['/Client/ViewUser/' + orgLang + '/' + id, [tabs.accueil[0], tabs.accueil[1][3]]];
    case 'cabinet':
      return ['/Cabinet/ShowCabinet/' + orgLang + '/' + id, [tabs.accueil[0], tabs.accueil[1][2]]];
    case 'client':
      return ['/Client/ShowClient/' + orgLang + '/' + id, [tabs.accueil[0], tabs.accueil[1][3]]];
    case 'client-editcompany':
      return ['/Client/EditClient/' + orgLang, [tabs.accueil[0], tabs.accueil[1][3]]];
    case 'client-editetab':
      return ['/Client/EditEtablissement/' + orgLang + '/' + id, [tabs.accueil[0], tabs.accueil[1][3]]];
    case 'results-search':
      var searchId = getCurrentTextSearch();
      return ['/Search/ShowView/' + orgLang + '/' + filter.id + '/' + filter.page + '?searchId=' + searchId + '&showComments=' + filter.showComments + '&useFiscalFilter=' + filter.useFiscalFilter, [tabs.document[0], tabs.document[1][2]]];
    case 'docs-profile':
      return ['/Home/PostedDocShowView/' + orgLang + '/' + filter.id + '/' + filter.page + '?userId=' + filter.userId, ['', '']];
    case 'docs-profile-ca':
      return ['/Cabinet/PostedDocShowView/' + orgLang + '/' + filter.id + '/' + filter.page + '?userId=' + filter.userId + '&showComments=' + filter.showComments, [tabs.accueil[0], tabs.accueil[1][2]]];
    case 'docs-profile-cl':
      return ['/Client/PostedDocShowView/' + orgLang + '/' + filter.id + '/' + filter.page + '?userId=' + filter.userId + '&showComments=' + filter.showComments, [tabs.accueil[0], tabs.accueil[1][3]]];
    case 'disc-profile':
      return ['/Home/DiscussionShowView/' + orgLang + '/' + filter.id + '/' + filter.page + '?userId=' + filter.userId, ['', '']];
    case 'disc-profile-ca':
      return ['/Cabinet/DiscussionShowView/' + orgLang + '/' + filter.id + '/' + filter.page + '?userId=' + filter.userId, [tabs.accueil[0], tabs.accueil[1][2]]];
    case 'disc-profile-cl':
      return ['/Client/DiscussionShowView/' + orgLang + '/' + filter.id + '/' + filter.page + '?userId=' + filter.userId, [tabs.accueil[0], tabs.accueil[1][3]]];
    case 'disc-profile-doc':
      return ['/Home/DetailDocument/' + orgLang + '/' + filter.DiscussionId + '/' + filter.Page + '/' + id + '?userId=' + filter.userId, ['', '']];
    case 'disc-profile-ca-doc':
      return ['/Cabinet/DetailDocument/' + orgLang + '/' + filter.DiscussionId + '/' + filter.Page + '/' + id + '?userId=' + filter.userId, [tabs.accueil[0], tabs.accueil[1][2]]];
    case 'disc-profile-cl-doc':
      return ['/Client/DetailDocument/' + orgLang + '/' + filter.DiscussionId + '/' + filter.Page + '/' + id + '?userId=' + filter.userId, [tabs.accueil[0], tabs.accueil[1][3]]];
    case 'compta-disc-doc':
      return ['/DiscussionCompta/DetailDocument/' + orgLang + '/' + filter.DiscussionId + '/' + filter.Page + '/' + id + '?discstate=' + filter.discstate, [tabs.compta[0], tabs.compta[1][5]]];
    case 'social-disc-doc':
      return ['/DiscussionSocial/DetailDocument/' + orgLang + '/' + filter.DiscussionId + '/' + filter.Page + '/' + id + '?discstate=' + filter.discstate, [tabs.social[0], tabs.social[1][1]]];
    case 'disc-juridique-doc':
      return ['/DiscussionJuridique/DetailDocument/' + orgLang + '/' + filter.DiscussionId + '/' + filter.Page + '/' + id + '?discstate=' + filter.discstate, [tabs.juridique[0], tabs.juridique[1][1]]];

    case 'compta-disc':
      return ['/DiscussionCompta/ShowView/' + orgLang + '/' + filter.id + '/' + filter.page + '?discstate=' + filter.discstate, [tabs.compta[0], tabs.compta[1][5]]];
    case 'compta-params':
      return ['/ParamsCompta/ShowView/' + orgLang + '/' + filter, [tabs.compta[0], 'Parameters']];
    case 'disc-juridique':
      return ['/DiscussionJuridique/ShowView/' + orgLang + '/' + filter.id + '/' + filter.page + '?discstate=' + filter.discstate, [tabs.juridique[0], tabs.juridique[1][1]]];
    case 'social-disc':
      return ['/DiscussionSocial/ShowView/' + orgLang + '/' + filter.id + '/' + filter.page + '?discstate=' + filter.discstate, [tabs.social[0], tabs.social[1][1]]];

    case 'docs-collection':
      return ['/Collection/ShowView/' + orgLang + '/' + filter.id + '/' + filter.page + '?entId=' + filter.entId + '&showComments=' + filter.showComments, [tabs.document[0], tabs.document[1][1]]];
    case 'docs-collection-pf':
      return ['/Collection/ShowDocUploadViewPF/' + orgLang + '/' + filter.id + '/' + filter.page + '?elmId=' + filter.elmId + '&showComments=' + filter.showComments, [tabs.document[0], tabs.document[1][1]]];

    case 'docs-mysite':
      return ['/MySite/ShowView/' + orgLang + '/' + filter.id + '/' + filter.page + '?eln=' + filter.eln + '&showComments=' + filter.showComments + '&doctype=' + filter.doctype + '&useFiscalFilter=' + filter.useFiscalFilter, [tabs.espace[0], tabs.espace[1][0]]];
    case 'docs-employee':
      return ['/EmployeeDocument/ShowView/' + orgLang + '/' + filter.id + '/' + filter.page + '?eln=' + filter.eln + '&showComments=' + filter.showComments + '&from=' + filter.from + '&userId=' + filter.userId + '&doctype=' + filter.doctype + '&useFiscalFilter=' + filter.useFiscalFilter, [tabs.social[0], tabs.social[1][2]]];

    case 'docs-compta':
      return ['/DocumentCompta/ShowView/' + orgLang + '/' + filter.id + '/' + filter.page + '?doctype=' + filter.doctype + '&showComments=' + filter.showComments + '&useFiscalFilter=' + filter.useFiscalFilter, [tabs.compta[0], tabs.compta[1][0]]];
    case 'docs-social':
      return ['/DocumentSocial/ShowView/' + orgLang + '/' + filter.id + '/' + filter.page + '?doctype=' + filter.doctype + '&showComments=' + filter.showComments + '&entId=' + filter.entId + '&useFiscalFilter=' + filter.useFiscalFilter, [tabs.social[0], tabs.social[1][0]]];
    case 'docs-juridique':
      return ['/DocumentJuridique/ShowView/' + orgLang + '/' + filter.id + '/' + filter.page + '?doctype=' + filter.doctype + '&showComments=' + filter.showComments + '&entId=' + filter.entId + '&useFiscalFilter=' + filter.useFiscalFilter, [tabs.juridique[0], tabs.juridique[1][0]]];
    case 'document':
      return ['/Document/ShowView/' + orgLang + '/' + filter.id + '/' + filter.page + '?showComments=' + filter.showComments, [tabs.accueil[0], tabs.accueil[1][1]]];

    case 'docs-compta-c':
      return ['/DocComptaCustomer/ShowView/' + orgLang + '/' + filter.id + '/' + filter.page + '?accounted=' + filter.accounted + '&nature=' + filter.nature + '&state=' + filter.state + '&doctype=' + filter.doctype + '&days=' + filter.days + '&showComments=' + filter.showComments + '&entId=' + filter.entId, [tabs.compta[0], tabs.compta[1][1]]];
    case 'docs-compta-s':
      return ['/DocComptaSupplier/ShowView/' + orgLang + '/' + filter.id + '/' + filter.page + '?accounted=' + filter.accounted + '&nature=' + filter.nature + '&state=' + filter.state + '&doctype=' + filter.doctype + '&days=' + filter.days + '&showComments=' + filter.showComments + '&entId=' + filter.entId, [tabs.compta[0], tabs.compta[1][2]]];
    case 'docs-compta-b':
      return ['/DocComptaBank/ShowView/' + orgLang + '/' + filter.id + '/' + filter.page + '?doctype=' + filter.doctype + '&showComments=' + filter.showComments + '&entId=' + filter.entId, [tabs.compta[0], tabs.compta[1][3]]];
    case 'docs-compta-f':
      return ['/DocComptaFiscal/ShowView/' + orgLang + '/' + filter.id + '/' + filter.page + '?doctype=' + filter.doctype + '&showComments=' + filter.showComments + '&entId=' + filter.entId, [tabs.compta[0], tabs.compta[1][4]]];

    case 'tasks-global':
      return ['/Task/ShowView/' + orgLang + '/' + filter.id + '/' + filter.page + '?tsp=' + filter.tsp + '&tsc=' + filter.tsc + '&tss=' + filter.tss + '&tsa=' + filter.tsa + '&tst=' + filter.tst + '&mis=' + filter.mis + '&t=' + filter.t, [tabs.accueil[0], tabs.accueil[1][4]]];
    case 'tasks-mysite':
      return ['/Task/ShowViewMySite/' + orgLang + '/' + filter.id + '/' + filter.page + '?tsp=' + filter.tsp + '&tsc=' + filter.tsc + '&tss=' + filter.tss + '&tsa=' + filter.tsa + '&tst=' + filter.tst + '&mis=' + filter.mis + '&t=' + filter.t, [tabs.espace[0], tabs.espace[1][1]]];
    case 'docs-tasks-global':
      return ['/Task/TaskDocumentsViewGlobal/' + orgLang + '/' + filter.id + '/' + filter.page + '?showComments=' + filter.showComments + '&tskId=' + filter.tskId + '&tsp=' + filter.tsp + '&tsc=' + filter.tsc + '&tss=' + filter.tss + '&tsa=' + filter.tsa + '&tst=' + filter.tst + '&mis=' + filter.mis + '&t=' + filter.t + '&p=' + filter.p, [tabs.accueil[0], tabs.accueil[1][4]]];
    case 'docs-tasks-mysite':
      return ['/Task/TaskDocumentsViewMySite/' + orgLang + '/' + filter.id + '/' + filter.page + '?showComments=' + filter.showComments + '&tskId=' + filter.tskId + '&tsp=' + filter.tsp + '&tsc=' + filter.tsc + '&tss=' + filter.tss + '&tsa=' + filter.tsa + '&tst=' + filter.tst + '&mis=' + filter.mis + '&t=' + filter.t + '&p=' + filter.p, [tabs.espace[0], tabs.espace[1][1]]];
    case 'news':
      return ['/News/ShowView/' + orgLang + '/' + filter.id + '/' + filter.page, [tabs.news[0], tabs.news[1][0]]];
    case 'ent-c':
      if (typeof (filter.sortField) == 'undefined')
        filter.sortField = '';
      if (typeof (filter.sortDirection) == 'undefined')
        filter.sortDirection = '';
      return ['/DocComptaCustomer/ShowEntityView/' + orgLang + '/' + filter.id + '/' + filter.page + '?entId=' + filter.entId + '&sortField=' + filter.sortField + '&sortDirection=' + filter.sortDirection + '&sortPosition=' + filter.sortPosition + '&usePager=' + filter.usePager, [tabs.compta[0], tabs.compta[1][1]]];
    case 'ent-c-details':
      return ['/DocComptaCustomer/ShowEntityViewDetails/' + orgLang + '/' + filter.id + '/' + filter.page + '?entId=' + filter.entId + '&sortField=' + filter.sortField + '&sortDirection=' + filter.sortDirection + '&sortPosition=' + filter.sortPosition + '&usePager=' + filter.usePager, [tabs.compta[0], tabs.compta[1][1]]];
    case 'ent-s':
      if (typeof (filter.sortField) == 'undefined')
        filter.sortField = '';
      if (typeof (filter.sortDirection) == 'undefined')
        filter.sortDirection = '';
      return ['/DocComptaSupplier/ShowEntityView/' + orgLang + '/' + filter.id + '/' + filter.page + '?entId=' + filter.entId + '&sortField=' + filter.sortField + '&sortDirection=' + filter.sortDirection + '&sortPosition=' + filter.sortPosition + '&usePager=' + filter.usePager, [tabs.compta[0], tabs.compta[1][2]]];
    case 'ent-s-details':
      return ['/DocComptaSupplier/ShowSupplierViewDetails/' + orgLang + '/' + filter.id + '/' + filter.page + '?entId=' + filter.entId + '&sortField=' + filter.sortField + '&sortDirection=' + filter.sortDirection + '&sortPosition=' + filter.sortPosition + '&usePager=' + filter.usePager, [tabs.compta[0], tabs.compta[1][2]]];

    case 'extjs4':
      return ['/ExtJS4/Index/' + orgLang + '/' + filter.id + '/' + filter.page, [tabs.accueil[0], tabs.accueil[1][5]]];

  }
};
pack('LivePME.DashBoard');

LivePME.DashBoard = function _livePME_dashBoard() {
  if (!(this instanceof arguments.callee))
    return new arguments.callee();

  var self = this;
  self.rapport = 4 / 5;
};

LivePME.DashBoard.prototype.launch = function _livePME_dashBoard_launch(element, isopen, refreshCallback, timer, tooltipText) {
  var self = this;

  self.isopen = isopen;
  self.isopen_hover = true;
  self.element = $(element);
  self.refreshCallback = refreshCallback;
  self.refreshTimeOut = timer;

  self.refreshCallback();
  self.refreshTimeOutId = setTimeout(self.refreshCallback, self.refreshTimeOut);

  self.tooltipText = tooltipText;

  if (self.isopen)
    self.open();

  self.element.find('#liveboard-button, .liveboard-close').unbind('click').click(function() {
    self.toggle();
  });

  self.element.find('#liveboard-button').hover(function(e) {
    self.open_deb(e);
  }, function(e) {
    if (self.isopen_hover == true) { ; self.close(); }
  });

  self.element.unbind('hover').mouseover(function(e) {
    if (self.element.css('top') == self.top + 'px')
      self.element.css('z-index', '2');
  });

  self.design();

};

/* members */
LivePME.DashBoard.isopen = null;
LivePME.DashBoard.isopen_hover = null;
LivePME.DashBoard.element = null;
LivePME.DashBoard.bottom = null;
LivePME.DashBoard.top = null;
LivePME.DashBoard.height = null;
LivePME.DashBoard.opendeb = null;
LivePME.DashBoard.contentheight = null;
LivePME.DashBoard.rapport = null;
LivePME.DashBoard.refreshFlag = null;
LivePME.DashBoard.refreshTimeOut = null;
LivePME.DashBoard.refreshTimeOutId = null;
LivePME.DashBoard.refreshCallback = null;
LivePME.DashBoard.tooltipText = null;
LivePME.DashBoard.windowHeight = null;

/* methods */
LivePME.DashBoard.prototype.setHeight = function _livePME_dashBoard_setHeight(h) {
  var self = this;
  self.windowHeight = h;
  board.design();
}

LivePME.DashBoard.prototype.design = function _livePME_dashBoard_design() {
  var self = this;

  self.height = $(window).height() * self.rapport;
  self.contentheight = self.height - 70;
  self.top = Math.round(-self.height - 8);
  self.bottom = '-50';
  self.opendeb = -self.height - 2;

  $("#liveboard-button").attr("title", "Afficher les informations");
  $("#liveboard-button").css("top", self.height);
  $("#liveboard-outer").css("top", self.top);
  $("#liveboard-outer").css("height", self.height + 35);
  $("#liveboard-content").css("height", self.contentheight);
  $("#liveboard-inner").css("height", self.height);
  $("#liveboard-outer").css("display", "block");
  $("#liveboard-outer").css("visibility", "visible");
  $("#liveboard-outer").css("z-index", '997');
};

LivePME.DashBoard.prototype.toggle = function _livePME_dashBoard_toggle() {
  var self = this;
  if (self.isopen)
    self.close();
  else
    self.open();
};
LivePME.DashBoard.prototype.open = function _livePME_dashBoard_open() {
  var self = this;
  self.isopen = true;
  self.isopen_hover = false;

  self.element.css('z-index', '997');
  $("#liveboard-button").attr("title", "Masquer les informations");

  //hideTooltip();

  if (self.refreshFlag)
    self.refreshCallback();

  $(self.element).animate(
    { top: self.bottom },
    { duration: 900, easing: 'easeOutBack' }
  )

  $('<div id="fadeDashboard"</div>').css({
    position: 'fixed',
    width: $(document).width() + 'px',
    height: $(document).height() + 'px',
    top: '0px',
    left: '0px',
    zIndex: '996',
    backgroundColor:'#000000'
  }).appendTo('body').css("opacity","0.0").css("filter","alpha(opacity=0)").show();

  $("#fadeDashboard").click(function () {
    self.close();
  });

};

LivePME.DashBoard.prototype.open_deb = function _livePME_dashBoard_open_deb() {
  var self = this;
  var top = self.element.css('top');
  if (top == self.top + 'px') {
    if (!self.isopen) {
      //showTooltip(event, self.tooltipText);
      //self.element.find('.tooltip').show();
      self.isopen_hover = true;
      $(self.element).animate(
				{ top: self.opendeb },
				{ duration: 400, easing: 'easeOutBack' }
			)
    }
  }
  $("#liveboard-outer").css("z-index", '997');
};

LivePME.DashBoard.prototype.close = function _livePME_dashBoard_close() {
  var self = this;
  self.isopen = false;
  self.isopen_hover = false;
  $(self.element).animate(
		{ top: self.top },
		{ duration: 600, easing: 'easeInBack' }, function () {
		  self.element.css('z-index', '997');
		}
  );
  $("#fadeDashboard").hide();
  $("#fadeDashboard").remove();
  $("#liveboard-button").attr("title", "Afficher les informations");
};

LivePME.DashBoard.prototype.setContent = function _livePME_dashBoard_setContent(target, content) {
  var self = this;
  $(target).html(content);
  clearTimeout(self.refreshTimeOutId);
  self.refreshTimeOutId = setTimeout(self.refreshCallback, self.refreshTimeOut);
  self.setRefreshFlag(false);
};

LivePME.DashBoard.prototype.setRefreshFlag = function _livePME_dashBoard_setRefreshFlag(val) {
  var self = this;
  self.refreshFlag = val;
};
	
Ext.onReady(function() {
  // Modification du label Filters dans les grid ExtJS.
  if (Ext.ux.grid.GridFilters) {
    Ext.override(Ext.ux.grid.GridFilters, {
      menuFilterText: 'Filtres'
    });
  }
  // modification des labels du datefilter
  if (Ext.ux.grid.filter.DateFilter) {
    Ext.override(Ext.ux.grid.filter.DateFilter, {
      onText: 'Le',
      beforeText: 'Avant',
      afterText: 'Apr&egrave;s'
    });
  }
  // Ajout d'un format frMoney
  if (Ext.util.Format) {
    Ext.util.Format.frMoney = function(v) {
      v = (Math.round((v - 0) * 100)) / 100;
      v = (v == Math.floor(v)) ? v + ".00" : ((v * 10 == Math.floor(v * 10)) ? v + "0" : v);
      v = String(v);
      var ps = v.split('.');
      var whole = ps[0];
      var sub = ps[1] ? ',' + ps[1] : ',00';
      var r = /(\d+)(\d{3})/;
      while (r.test(whole)) {
        whole = whole.replace(r, '$1' + ' ' + '$2');
      }
      v = whole + sub;
      if (v.charAt(0) == '-') {
        return '-' + v.substr(1) + " &euro;";
      }
      return v + " &euro;";
    };
  }
});

function findTanganeBox(nameBox) {
  return findTanganeBoxRecursiv(nameBox, 'Tangane.Url.Box.root');
}

function findTanganeBoxRecursiv(nameBox, nameCurrentBox) {
  /*************
  ATTENTION : cette fonction retourne le première occurence trouvée.
  **************/
  var currentBox = eval(nameCurrentBox);
  //Vérifie si la box recherchée est dans la box courante
  if (nameBox in currentBox) {
    var box = eval(nameCurrentBox + '.' + nameBox);
    if (isTanganeBox(box))
    //si c'est la box que l'on recherche, on la retourne
      return box;
  } else {//sinon lance la recherche dans les boxs enfants
    for (nameAttr in currentBox) {
      var objectAttr = eval(nameCurrentBox + '.' + nameAttr);
      if (isTanganeBox(objectAttr))
        return findTanganeBoxRecursiv(nameBox, nameCurrentBox + '.' + nameAttr);
    }
  }
  //quand on a fini de parcourir les attributs de la box courante et qu'on n'a pas trouvé de box enfant où faire la recherche
  //ou si l'objet recherché n'est pas une TanganeBox
  //on retourne null 
  return null;
}
function isTanganeBox(object) {
  /*******
  TODO :
  - En cas d'apparition de faux positif, ajouté des attributs à tester dans le tableau tabAttrTest.
  - En cas d'apparition de faux négatif, retiré l'attribut causant le faux négatif.
  ATTENTION : plus le nombre de test est faible plus la probabilité de faux positif est forte!!!
        
  Autres atributs sur lesquels on peut tester:
  _ctx, _unbind, _inner, _loading, $self, getParent, applyParentInfo, 
  invalidate, branchInfo, rawApplyState, applyState, clear, fill, fromUrl
  */
  var tabAttrTest = ['_urlBox', 'getState', 'setState'];
  var compteurOK = 0;
  for (i = 0; i < tabAttrTest.length; i++) {
    if (typeof (object) == "object") {
      if (tabAttrTest[i] in object)
        compteurOK++;
    }
  }
  return compteurOK == tabAttrTest.length;
}
function gridpreview(popinTimeout, imgDom, imgHeight, mousePadding, div, alignMiddle) {
  var popinTimeId = null;
  var view = false;
  $(div + " img.popin").live('mouseover', function (e) {
    if (!view) {
      view = !view;
      removePreview();
      var self = this;
      var posX = e.pageX + mousePadding;
      var posY = e.pageY + mousePadding + 20;
      popinTimeId = setTimeout(function () {
        posY = ($(document).height() - posY) < imgHeight + mousePadding ? posY - imgHeight - mousePadding * 2 : posY;

        if (alignMiddle) {
          posY = ($(document).height() / 2) - (imgHeight / 2);
        }

        $("body").prepend('<div id="' + imgDom + '" style="max-height:'
            + imgHeight + 'px;position:absolute;top:' + posY + 'px; left:' + posX + 'px;z-index:9999;background-color:#aaaaaa;padding:2px;"></div>');
        var now = new Date();
        var date = now.getMinutes() + '-' + now.getSeconds();
        var getvalue = $(self).attr('rel') + '&uid=' + Math.random() + '&time=' + date;
        if (alignMiddle) {
          $("#" + imgDom).html($('<img id="gridPreview" style="height:' + imgHeight + 'px;"/>').attr("src", getvalue));
        } else {
          $("#" + imgDom).html($('<img id="gridPreview" style="max-height:' + imgHeight + 'px;"/>').attr("src", getvalue));
        }
        
      }, popinTimeout);
    }
  }).live('mouseout', function () {
    view = false;
    clearTimeout(popinTimeId);
    removePreview();
  });

  function removePreview() {
    $("#gridPreview").remove();
    $("#" + imgDom).remove();
  }
}
pack('LivePME.Initialize');
LivePME.Initialize = Tangane.Box.contents(function(cfg) {
  var self = this;
  new LivePME.TabMenu(self.box, Tangane.Context.root, cfg.menuTabs);
});
function myJsonAutocomplete(target, url, callback, minChars, width, max, extraparam, codeOnly) {
  var selected;
  var selectValue;
  var $t = $(target);

  $t.liveautocomplete(url, {
    dataType: 'json', delay: 0, minChars: minChars, width: width, selectFirst: true, max: max, matchContains: true,
    extraParams: { supId: extraparam },
    highlight: function(v, s) {
      var reg = new RegExp('(' + s + ')', 'gi');
      return v.replace(reg, '<b>$1</b>');
    },
    parse: function(data) {
      var rows = new Array();
      for (var i = 0; i < data.length; i++) {
        rows[i] = { data: data[i], value: data[i].First, result: data[i].Second, id: data[i].First };
      }
      return rows;
    },
    formatItem: function(row) {
      return row.Second;
    }
  }).result(function(event, row) {
    if (row && callback) callback.call();
    if (codeOnly) {
      $t.val($.trim($t.val().split('-')[0]));
    }
    if ($t.prev()) {
      $t.prev().val(row.First);
      selected = row.Second;
      selectValue = row.First;
    }
  });
  $t.blur(function() { manageIds(); }).focus(function() { manageIds(); }).keyup(function() { manageIds(); });
  function manageIds() {
    if ($.trim($t.val()).length == 0) {
      $t.prev().val("");
    }
  }
}

function myJsonAutocompleteCollectiveAccount(target, url, callback, minChars, width, max, extraparam, codeOnly, natureEntity) {
  var selected;
  var selectValue;
  var $t = $(target);

  $t.liveautocomplete(url, {
    dataType: 'json', delay: 0, minChars: minChars, width: width, selectFirst: true, max: max, matchContains: true,
    extraParams: { supId: extraparam, entityNature: natureEntity },
    highlight: function(v, s) {
      var reg = new RegExp('(' + s + ')', 'gi');
      return v.replace(reg, '<b>$1</b>');
    },
    parse: function(data) {
      var rows = new Array();
      for (var i = 0; i < data.length; i++) {
        rows[i] = { data: data[i], valueAccount: data[i].First, thirdName: data[i].Second, thirdId: data[i].Third };
      }
      return rows;
    },
    formatItem: function(row) {
      return row.First;
    }
  }).result(function(event, row) {
    if (row && callback) callback.call();
    if (codeOnly) {
      $t.val($.trim($t.val().split('-')[0]));
    }
    $("#ElementDetail_RattachementId").val(row.Third);
    $t.val(row.First);
  });
  $t.blur(function() { manageIds(); }).focus(function() { manageIds(); }).keyup(function() { manageIds(); });
  function manageIds() {
    if ($.trim($t.val()).length == 0) {
      $t.prev().val("");
    }
  }
}
pack("LivePME.Loader");

LivePME.Loader = function (loaderMsg, loaderOrgMsg, loaderUrl) {
  var self = this;
  self.AjaxRequestDOM = "requestLoad";
  self.AjaxRequestDOMOrg = "fadeOrgLoad";
  self.AjaxRequestDOMOrgLoad = "msgOrgLoad";
  self.AjaxRequestLoaderActive = true;
  self.AjaxRequestTimeout = null;
  self.AjaxLoaderURL = loaderUrl;
  self.AjaxOrgMsg = loaderOrgMsg;

  // Loader général chargement trop long.
  self.AjaxLoader = $('<div id="' + self.AjaxRequestDOM + '">' + loaderMsg + '</div>').css({
    position: 'fixed',
    width: '250px',
    top: '0px',
    left: '' + (($(window).width() - 250) / 2) + 'px',
    zIndex: '9999',
    backgroundColor: '#ffffa0',
    textAlign: "center",
    color: "#444",
    padding: "5px"
  });

  // Loader changement d'organisation
  self.AjaxLoaderOrg = $('<div id="' + self.AjaxRequestDOMOrg + '"></div>').css({
    position: 'fixed',
    width: $(window).width() + 'px',
    height: $(window).height() + 'px',
    top: '0px',
    left: '0px',
    zIndex: '998',
    backgroundColor: '#000000'
  });
}

LivePME.Loader.prototype.LoaderEnable = function () {
  var self = this;
  if (self.AjaxRequestLoaderActive) {
    self.AjaxLoader.appendTo('body').hide().css("font-size", "1.4em").css("font-weight", "bold");
    var DomElem = $("#" + self.AjaxRequestDOM);
    self.AjaxRequestTimeout = setTimeout(function () {
      DomElem.fadeIn(100);
    }, 800);
  }
}

LivePME.Loader.prototype.LoaderEnableOrg = function () {
  var self = this;
  if (self.AjaxRequestLoaderActive) {
    var img = '<img src="' + self.AjaxLoaderURL + '" alt="..." />';

    self.AjaxLoaderOrg.appendTo('body').css("opacity", "0.5").css("filter", "alpha(opacity=50)")
    .css("height", $(window).height()).css("width", $(window).width())
    .show();

    $('<div id="' + self.AjaxRequestDOMOrgLoad + '" style="display:inline-block;">' + self.AjaxOrgMsg
    + img + '</div>').css({
      position: 'fixed',zIndex: '999',
      color: '#3488D2',fontWeight: 'bold',
      background: 'none repeat scroll 0 0 #EFEFEF',fontSize: '15px',
      textAlign: 'center', padding: '20px',
      border: '1px solid #7FABD1',borderRadius: '5px 5px 5px 5px'
    }).appendTo('body').show();

    var textTop = ($(window).height() / 2 - 100);
    var textLeft = ($(window).width() - $("#"+self.AjaxRequestDOMOrgLoad).width()) / 2;

    $("#"+self.AjaxRequestDOMOrgLoad).css("position", "absolute").css("top", textTop).css("left", textLeft).css("z-index", 1000);
  }
}

LivePME.Loader.prototype.LoaderDisableOrg = function () {
  var self = this;
  if (self.AjaxRequestLoaderActive) {
    var DomElem = $("#" + self.AjaxRequestDOMOrg);
    if (DomElem.length) {
      setTimeout(function () {
        DomElem.fadeOut('slow', function () {
          DomElem.remove();
        });
        $("#"+self.AjaxRequestDOMOrgLoad).fadeOut('slow', function () {
          $("#"+self.AjaxRequestDOMOrgLoad).remove();
        });
      }, 500);
    }
  }
}

LivePME.Loader.prototype.LoaderDisable = function () {
  var self = this;
  if (self.AjaxRequestLoaderActive) {
    clearTimeout(self.AjaxRequestTimeout);
    setTimeout(function () {
      var DomElem = $("#" + self.AjaxRequestDOM);
      if (DomElem.length) {
        DomElem.fadeOut('slow', function () {
          $(this).remove();
        });
      }
    }, 200);
  }
}

pack('LivePME.MessageBox');

LivePME.MessageBox.show = function (message, title, buttons, height) {

  var h = height ? height : 200;
  var title = (title == null || title == '') ? 'Alerte' : title;
  var dialogButtons = (buttons == null || buttons == undefined) ? {} : buttons.get();

  $('body').append('<div id="messagebox" title="' + title + '">' + message + '</div>');

  $("#messagebox").dialog({
    closeOnEscape: true,
    autoOpen: false,
    show: {
      effect: "slide",
      speed: 1000
    },
    resizable: false,
    draggable: true,
    width: 500,
    closeText: 'X',
    height:h,
    modal: true,
    close: function (event, ui) {
      $(this).dialog("destroy");
      $("#messagebox").remove();
      $("body").css("overflow", "");
    },
    open: function (event, ui) {
      $(".ui-dialog-buttonpane :button span").removeClass().addClass("button-inner")
      $(".ui-dialog-buttonpane :button").removeClass().addClass("button button-gray")
      $("body").css("overflow", "hidden");
    },
    buttons: dialogButtons
  });

  $("#messagebox").dialog("open");
  $("#messagebox").focus();
};

LivePME.MessageBox.hide = function()
{
  if ($("#messagebox").length != 0)
  {
    $("#messagebox").dialog("close");
  }
}

LivePME.MessageBoxButtons = function _livePME_messageBoxButtons() 
{
  this._buttons = {};
};

LivePME.MessageBoxButtons.prototype.add = function _livePME_messageBoxButtons_add(title, onClick, closeDialog) 
{
  this._buttons[title] = new Function('', onClick + ' if (' + closeDialog + ') $("#messagebox").dialog("close");');
  return this;
}

LivePME.MessageBoxButtons.prototype.get = function _livePME_messageBoxButtons_get() 
{
  return this._buttons;
};
pack('LivePME.Notification');

/************

 cet objet permet l'affichage de notifications dans une zone définie. Cette zone est définie via le selecteur passé en paramètre du constructeur.
  _Members : 
    - notificationPanel : selecteur de la zone de notification
    - fadeOutTime       : temps (en ms) au bout duquel les notification "inline" disparaissent. 
                        Par défaut la valeur est 2000 (2 secondes).
                        Si fadeOutTime = 0, la notification restera visible.
    - allowCloseBlock : détermine si on affiche un bouton permetant la fermeture d'une notification affiché en "Block"

  _Methods : 
    Il existe trois types de notifications : 
      - "Error";
      - "Help";
      - "Validation".
    Il existe deux type d'affichage : 
      - "Inline" qui disparait au bout d'un temps donné et qui s'adapte à la longueur du texte (non multiligne);
      - "Block" qui reste visible et qui prend toute la largeur du panel dans il se trouve (multiligne).
    Chaque type de notification peut être affiché dans chaque type d'affichage grâce à la méthode correspondante : show[type d'affichage][type de notification]; qui prend en paramètre le message à afficher

    Il existe un type particulier de notification : "warning". Il s'affiche grâce à la méthode "showWarning" qui prend en paramètre le message à afficher. Il s'affiche automatiquement en type d'affichage "block"

  _Exemple :
    <div id="notificationPanel"></div> 

    <script type="text/javascript">
      var oNotification = new LivePME.Notification('#notificationPanel');
      oNotification.showInlineValidation("l'action s'est déroulé avec succès.");
    </script>

*************/
LivePME.Notification = function (panelSelector) {
  /* Members */
  var notificationPanel;
  var fadeOutTime;
  var allowCloseBlock;


  /* Methods */
  this.showInlineError = function (msg) {
    showNotification(this, "error", msg, true);
  }

  this.showBlockError = function (msg) {
    showNotification(this, "error", msg, false);
  }

  this.showInlineValidation = function (msg) {
    showNotification(this, "validation", msg, true);
  }

  this.showBlockValidation = function (msg) {
    showNotification(this, "validation", msg, false);
  }

  this.showInlineHelp = function (msg) {
    showNotification(this, "help", msg, true);
  }

  this.showBlockHelp = function (msg) {
    showNotification(this, "help", msg, false);
  }

  this.showWarning = function (msg) {
    showNotification(this, "warning", msg, false);
  }

  /* Private methods */
  function showNotification(o, c, msg, displayInline) {
    var displayValue = "inline-block";
    if (!displayInline)
      displayValue = "block";
    if ($.trim(msg).length > 0) {
      var msgPanel = $(".message-panel");
      msgPanel.html(msg).removeClass().addClass("message-panel").addClass(c).css("display", displayValue);
      if (displayInline && typeof (o.fadeOutTime) != "undefined" && o.fadeOutTime > 0) {
        setTimeout(function () {
          closeMsgPanel(msgPanel);
        }, o.fadeOutTime);
      } else {
        if (o.allowCloseBlock) {
          addCloseButton(msgPanel);
        }
      }
    }
  }

  function addCloseButton(msgPanel) {
    msgPanel.css('position', 'relative');
    msgPanel.prepend('<div class="closeBtn" style="position: absolute;top: 0px;right: 2px;cursor: pointer;">X</div>');
    msgPanel.find('.closeBtn')
      .click(function () {
        closeMsgPanel($(this).parent());
      });
  }

  function closeMsgPanel(msgPanel) {
    msgPanel.fadeOut();
  }
  /*Constructeur*/
  function ctor(o, arg) {
    if ($(arg[0]).length > 0) {
      o.notificationPanel = $(arg[0]);
      o.notificationPanel.html("<span class=\"message-panel\"></span>");
      o.fadeOutTime = 3000;
      o.allowCloseBlock = true;
    }
  }

  /*appel au constructeur*/
  ctor(this, arguments);
}
pack('LivePME.ping');
LivePME.ping = function(url) {
setInterval(function() { Tangane.Context.root.ajaxGet(url); }, 3*60*1000); //3mn
};
pack('LivePME.PostData');
LivePME.PostData = Tangane.Box.contents(function(cfg) {
    var self = this;
    self.box.$self.find('#' + cfg.select).click(function() {
        var conf = true;
        if (cfg.txtConfirmation != undefined)
            conf = confirm(cfg.txtConfirmation);
//        if (cfg.isPopup) {
//            function confirmPost() {
//                self.ctx.ajaxPost(cfg.urlTarget, { id: cfg.keyTarget }, function() {
//                    if (cfg.isUpdatable) {
//                        self.box.invalidate();
//                    }
//                    self.ctx.trigger(cfg.trigger);
//                });
//            }
//            $('#' + cfg.popupDivTarget).dialog("open");
//        }
//        else {
            if (conf) {
                self.ctx.ajaxPost(cfg.urlTarget, { id: cfg.keyTarget }, function() {
                    if (cfg.isUpdatable) {
                        self.box.invalidate();
                    }
                    self.ctx.trigger(cfg.trigger);
                });
            //}
        }
    });
});

/**
 * http://www.openjs.com/scripts/events/keyboard_shortcuts/
 * Version : 2.01.B
 * By Binny V A
 * License : BSD
 */
shortcut = {
	'all_shortcuts':{},//All the shortcuts are stored in this array
	'add': function(shortcut_combination,callback,opt) {
		//Provide a set of default options
		var default_options = {
			'type':'keydown',
			'propagate':false,
			'disable_in_input':false,
			'target':document,
			'keycode':false
		}
		if(!opt) opt = default_options;
		else {
			for(var dfo in default_options) {
				if(typeof opt[dfo] == 'undefined') opt[dfo] = default_options[dfo];
			}
		}

		var ele = opt.target;
		if(typeof opt.target == 'string') ele = document.getElementById(opt.target);
		var ths = this;
		shortcut_combination = shortcut_combination.toLowerCase();

		//The function to be called at keypress
		var func = function(e) {
			e = e || window.event;
			
			if(opt['disable_in_input']) { //Don't enable shortcut keys in Input, Textarea fields
				var element;
				if(e.target) element=e.target;
				else if(e.srcElement) element=e.srcElement;
				if(element.nodeType==3) element=element.parentNode;

				if(element.tagName == 'INPUT' || element.tagName == 'TEXTAREA') return;
			}
	
			//Find Which key is pressed
			if (e.keyCode) code = e.keyCode;
			else if (e.which) code = e.which;
			var character = String.fromCharCode(code).toLowerCase();
			
			if(code == 188) character=","; //If the user presses , when the type is onkeydown
			if(code == 190) character="."; //If the user presses , when the type is onkeydown

			var keys = shortcut_combination.split("+");
			//Key Pressed - counts the number of valid keypresses - if it is same as the number of keys, the shortcut function is invoked
			var kp = 0;
			
			//Work around for stupid Shift key bug created by using lowercase - as a result the shift+num combination was broken
			var shift_nums = {
				"`":"~",
				"1":"!",
				"2":"@",
				"3":"#",
				"4":"$",
				"5":"%",
				"6":"^",
				"7":"&",
				"8":"*",
				"9":"(",
				"0":")",
				"-":"_",
				"=":"+",
				";":":",
				"'":"\"",
				",":"<",
				".":">",
				"/":"?",
				"\\":"|"
			}
			//Special Keys - and their codes
			var special_keys = {
				'esc':27,
				'escape':27,
				'tab':9,
				'space':32,
				'return':13,
				'enter':13,
				'backspace':8,
	
				'scrolllock':145,
				'scroll_lock':145,
				'scroll':145,
				'capslock':20,
				'caps_lock':20,
				'caps':20,
				'numlock':144,
				'num_lock':144,
				'num':144,
				
				'pause':19,
				'break':19,
				
				'insert':45,
				'home':36,
				'delete':46,
				'end':35,
				
				'pageup':33,
				'page_up':33,
				'pu':33,
	
				'pagedown':34,
				'page_down':34,
				'pd':34,
	
				'left':37,
				'up':38,
				'right':39,
				'down':40,
	
				'f1':112,
				'f2':113,
				'f3':114,
				'f4':115,
				'f5':116,
				'f6':117,
				'f7':118,
				'f8':119,
				'f9':120,
				'f10':121,
				'f11':122,
				'f12':123
			}
	
			var modifiers = { 
				shift: { wanted:false, pressed:false},
				ctrl : { wanted:false, pressed:false},
				alt  : { wanted:false, pressed:false},
				meta : { wanted:false, pressed:false}	//Meta is Mac specific
			};
                        
			if(e.ctrlKey)	modifiers.ctrl.pressed = true;
			if(e.shiftKey)	modifiers.shift.pressed = true;
			if(e.altKey)	modifiers.alt.pressed = true;
			if(e.metaKey)   modifiers.meta.pressed = true;
                        
			for(var i=0; k=keys[i],i<keys.length; i++) {
				//Modifiers
				if(k == 'ctrl' || k == 'control') {
					kp++;
					modifiers.ctrl.wanted = true;

				} else if(k == 'shift') {
					kp++;
					modifiers.shift.wanted = true;

				} else if(k == 'alt') {
					kp++;
					modifiers.alt.wanted = true;
				} else if(k == 'meta') {
					kp++;
					modifiers.meta.wanted = true;
				} else if(k.length > 1) { //If it is a special key
					if(special_keys[k] == code) kp++;
					
				} else if(opt['keycode']) {
					if(opt['keycode'] == code) kp++;

				} else { //The special keys did not match
					if(character == k) kp++;
					else {
						if(shift_nums[character] && e.shiftKey) { //Stupid Shift key bug created by using lowercase
							character = shift_nums[character]; 
							if(character == k) kp++;
						}
					}
				}
			}
			
			if(kp == keys.length && 
						modifiers.ctrl.pressed == modifiers.ctrl.wanted &&
						modifiers.shift.pressed == modifiers.shift.wanted &&
						modifiers.alt.pressed == modifiers.alt.wanted &&
						modifiers.meta.pressed == modifiers.meta.wanted) {
				callback(e);
	
				if(!opt['propagate']) { //Stop the event
					//e.cancelBubble is supported by IE - this will kill the bubbling process.
					e.cancelBubble = true;
					e.returnValue = false;
	
					//e.stopPropagation works in Firefox.
					if (e.stopPropagation) {
						e.stopPropagation();
						e.preventDefault();
					}
					return false;
				}
			}
		}
		this.all_shortcuts[shortcut_combination] = {
			'callback':func, 
			'target':ele, 
			'event': opt['type']
		};
		//Attach the function with the event
		if(ele.addEventListener) ele.addEventListener(opt['type'], func, false);
		else if(ele.attachEvent) ele.attachEvent('on'+opt['type'], func);
		else ele['on'+opt['type']] = func;
	},

	//Remove the shortcut - just specify the shortcut and I will remove the binding
	'remove':function(shortcut_combination) {
		shortcut_combination = shortcut_combination.toLowerCase();
		var binding = this.all_shortcuts[shortcut_combination];
		delete(this.all_shortcuts[shortcut_combination])
		if(!binding) return;
		var type = binding['event'];
		var ele = binding['target'];
		var callback = binding['callback'];

		if(ele.detachEvent) ele.detachEvent('on'+type, callback);
		else if(ele.removeEventListener) ele.removeEventListener(type, callback, false);
		else ele['on'+type] = false;
  },
  
	'removeAll': function() {
    for(x in this.all_shortcuts) {
      this.remove(x);
    }
  }
	
}
pack('LivePME.SimplePopup');

LivePME.SimplePopup = function _livePME_simplePopup() {
  if (!(this instanceof arguments.callee))
    return new arguments.callee();

  var self = this;
  self.externaldiv = "#pop";
  self.internaldiv = "#tmpPop";
};

/* members */
LivePME.SimplePopup.refresh = null;
LivePME.SimplePopup.target = null;
LivePME.SimplePopup.width = null;
LivePME.SimplePopup.title = null;
LivePME.SimplePopup.externaldiv = null;
LivePME.SimplePopup.internaldiv = null;

/* methods */
LivePME.SimplePopup.prototype.createDiv = function _livePME_simplePopup_createDiv() {
  var popup = '<div id="pop" style="display:none;visibility:hidden;">' +
                '<div id="tmpPop" title=""></div>' +
              '</div>';
  $('body').append(popup);
};

LivePME.SimplePopup.prototype.clearDiv = function _livePME_simplePopup_clearDiv() {
  var self = this;
  $(self.externaldiv).remove();
};

LivePME.SimplePopup.prototype.open = function _livePME_simplePopup_open() {
  var self = this;
  $(self.internaldiv).html($(self.target).html());
  $(self.internaldiv).attr("title", self.title);
  self.populate();
  $(self.internaldiv).dialog("option", "width", self.width);
  $(self.internaldiv).dialog("open");
};

LivePME.SimplePopup.prototype.close = function _livePME_simplePopup_close(refresh) {
  var self = this;
  if (refresh != undefined)
    self.refresh = refresh;
  $(self.internaldiv).dialog("close");
};

LivePME.SimplePopup.prototype.errorContent = function _livePME_simplePopup_errorcontent(data, subtarget) {
  var self = this;
  var pop = self.internaldiv;
  if (subtarget != null)
    pop += " " + subtarget;
  $(pop).html(data);
};

LivePME.SimplePopup.prototype.content = function _livePME_simplePopup_content(data) {
  var self = this;
  $(self.internaldiv).html(data);
  var top = ($(window).height() - $(self.internaldiv).height()) / 2;
  top -= 50;
  $(".ui-dialog").css("top", Math.round(top) + $(document).scrollTop() +  "px");
};

/* popup */
LivePME.SimplePopup.prototype.populate = function _livePME_simplePopup_populate() {
  var self = this;
  $(self.internaldiv).dialog({
    bgiframe: true,
    closeOnEscape: true,
    autoOpen: false,
    draggable: true,
    closeText: 'X',
    modal: true,
    minHeight: 0,
    minWidth: 0,
    maxHeight: 500,
    resizable: false,
    position: 'center',
    open: function(){
      $("body").css("overflow", "hidden");
    },
    close: function(event, ui) {
      if (self.refresh) {
        Tangane.Url.Box.root.invalidate();
        Tangane.Url.Box.root.setState(Tangane.Url.Box.root.getState());
      }
      $("body").css("overflow", "");
      self.reset();
    }
  });
};

/* Reset */
LivePME.SimplePopup.prototype.reset = function _livePME_simplePopup_reset() {
  var self = this;
  $(self.internaldiv).dialog("destroy").remove();
  $(self.internaldiv).removeAttr("title");
  $(self.externaldiv).html($(self.externaldiv).html());
  this.refresh = null;
  this.target = null;
  this.width = 0;
  this.title = null;
  $(".ui-dialog").css("top", "");
};

LivePME.SimplePopup.prototype.popupLive = function _livePME_simplePopup_popup(target, width, refresh) {
  this.reset();
  
  this.refresh = refresh;
  this.target = target;
  this.title = $(target).attr("title");
  this.width = (width == null || width == 0) ? 500 : width;
  this.createDiv();
  this.open();
  this.clearDiv();
};
pack("LivePME.Slider");

// --------------------------------------------------------------------
// Constructeur
// --------------------------------------------------------------------
LivePME.Slider = function (tokenUrl, footerHeight, iframeTimer, addresses) {
  var self = this;
  self.DnnTokenUrl = tokenUrl;
  self.IsIE7 = window.navigator.appName == "Microsoft Internet Explorer" && document.documentMode <= 7;

  // Class et id des éléments du DOM
  self.bodyHtml = "body";
  self.sliderClass = "#sliding";
  self.menuClass = ".showSlide";
  self.varDOM = "slide";
  self.iframeClass = "frame";
  self.divIframeClass = "." + self.iframeClass;

  // variables d'initialisation
  self.currentSlide = 1;
  self.footerHeight = footerHeight;
  self.w = $(window).width();
  self.h = $(window).height() - self.footerHeight;
  self.previousW = self.w;
  self.previousH = self.h;

  self.scrollWidth = self.getScrollWidth();
  self.hasScroll = false;
  self.resizeTimeOut = null;
  self.iframeTimer = iframeTimer;

  // Adresse pour les iframes
  self.addressSlides = addresses;

  if (self.IsIE7) {
    $(self.menuClass).each(function () {
      var i = $(this).attr("rel");
      $(this).attr("target", "_blank");
      $(this).attr("href", self.addressSlides[i]);
    });
  } else {
    $("body").css("padding-bottom", "30px");
    $(".sliderBottom").show();

    // Création des slides
    self.createSlides();

    // Gestion du resize
    $(window).resize(function (e) {
      if (self.resizeTimeOut != null) {
        clearTimeout(self.resizeTimeOut);
      }
      self.resizeTimeOut = setTimeout(function () {
        self.manageAfterResize();
      }, 300);
    });

    // Gestion du click sur les liens vers les slides
    $(self.menuClass).click(function () {
      self.showSlide(this);
    });
  }

};

// --------------------------------------------------------------------
// Création des slides
// --------------------------------------------------------------------
LivePME.Slider.prototype.createSlides = function () {
  var self = this;
  for (var i = 2; i < self.addressSlides.length; i++) {
    // Closure récupération token
    (function (i) {
      $.postJSON(self.DnnTokenUrl, {}, function (data) {
        if (data.IsOk) {
          var token = data.token;
          var url = self.addressSlides[i];
          // Construction iframe
          var iframe = '<iframe rel="' + i + '"frameborder="0" src="' + url + token +
              '" style="width:' + self.w + 'px;height:' + self.h + 'px;" border="0"></iframe>';
          // Construction div + iframe
          var div = $('<div rel="' + i + '" class="' + self.iframeClass + '" id="' + self.varDOM + i
              + '" style="position:absolute;top:0;left:' + self.w * (i - 1)
              + 'px;width:' + self.w + 'px;height:100%;" >' + iframe + '</div>');
          div.insertAfter("#" + self.varDOM + (i - 1)).hide();
        }
      });
    })(i);
  }
  // Rafraichissement des iframes
  setTimeout(function () {
    $("iframe").each(function () {
      $(this).attr("src", $(this).attr("src"));
    });
  }, self.iframeTimer);
};

// --------------------------------------------------------------------
// Affichage d'un slide
// --------------------------------------------------------------------
LivePME.Slider.prototype.showSlide = function (o) {
  var self = this;
  var i = $(o).attr("rel");
  self.changeSlide(i, o);
};

// --------------------------------------------------------------------
// Affichage d'un slide
// --------------------------------------------------------------------
LivePME.Slider.prototype.changeSlide = function (i, o, delay) {
  var self = this;

  var delay = delay ? delay : 1000;
  var w = $(window).width();
  var l = (i - 1) * w;

  // Unbind click pendant le sliding
  $(self.menuClass).unbind('click');

  // Prise en compte de la largeur des scrollbars
  self.hasScroll = self.hasScrollBar();
  if ((self.hasScroll || (!$.browser.msie && $(document).height() != $(window).height())) && self.currentSlide == 1) {
    l += self.scrollWidth * (i - 1);
  }

  // Masquage scroll pour iframe
  if (i != 1) {
    $(self.sliderClass).css("position", "absolute").css("width", "100%");
    $(self.bodyHtml).css("overflow", "hidden");
  }

  // Gestion selection menu
  $("#slidingMenu a").removeClass("selected");
  $(o).addClass("selected");
  $("#" + self.varDOM + i).show();

  // Sliding vers le bon slide.
  $(self.sliderClass).animate({ top: 0, left: -l }, { duration: delay, easing: 'easeOutBack', complete: function () {
    self.manageAfterSliding(i);
  }
  });

  self.currentSlide = i;
};

// --------------------------------------------------------------------
// Gestion après sliding
// --------------------------------------------------------------------
LivePME.Slider.prototype.manageAfterSliding = function (i) {

  var self = this;
  // Masquage des slide non affichés après sliding
  $(self.divIframeClass).each(function () {
    var slide = $(this).attr("rel");
    if (slide != i) { $(this).hide(); }
  });
  // Gestion si retour vers premier slide
  if (i == 1) {
    $(self.sliderClass).css("position", "relative");
    $(self.bodyHtml).css("overflow", "auto");
    self.hasScroll = self.hasScrollBar();
  }
  // Sliding vers le haut si besoin
  if ($(document).scrollTop() != 0) {
    $("html, body").animate({ scrollTop: 0 }, 500);
  }
  // Binding click après sliding
  $(self.menuClass).bind('click', function () {
    self.showSlide(this);
  });
};

// --------------------------------------------------------------------
// Gestion après resize
// --------------------------------------------------------------------
LivePME.Slider.prototype.manageAfterResize = function () {
  var self = this;

  // Nouvelle largeur et hauteur
  var previous = $(self.bodyHtml).css("overflow");
  $(self.bodyHtml).css("overflow", "hidden");
  var w = $(window).width();
  var h = $(window).height() - self.footerHeight;
  $(self.bodyHtml).css("overflow", previous);

  if (self.previousW != w || self.previousH != h) { 

    // Modification largeur
    $(self.divIframeClass).each(function () {
      var i = $(this).attr("rel");
      $(this).width(w).css("left", w * (i - 1));
      $("iframe[rel=" + i + "]").width(w);
    });

    // Modification hauteur
    $(self.divIframeClass).height(h);
    $("iframe").height(h);

    // Recalage du slide courant
    self.changeSlide(self.currentSlide, $(self.menuClass + "[rel=" + self.currentSlide + "]"), 1);

    self.previousW = w;
    self.previousH = h;
  }

};

// --------------------------------------------------------------------
// Retourne true si on a des scroll bar, et false sinon
// --------------------------------------------------------------------
LivePME.Slider.prototype.hasScrollBar = function () {
  var result = false;
  var select = "html, body";
  var prev = $(select).scrollTop();
  $(select).scrollTop(prev + 1);
  result = $(select).scrollTop() !== 0;
  $(select).scrollTop(prev);
  return result;
}

// --------------------------------------------------------------------
// Retourne la largeur d'une scrollBar en fonction du navigateur.
// --------------------------------------------------------------------
LivePME.Slider.prototype.getScrollWidth = function () {
  var scr = null;
  var inn = null;
  var wNoScroll = 0;
  var wScroll = 0;
  scr = document.createElement('div');
  scr.style.position = 'absolute';
  scr.style.top = '-1000px';
  scr.style.left = '-1000px';
  scr.style.width = '100px';
  scr.style.height = '50px';
  scr.style.overflow = 'hidden';
  inn = document.createElement('div');
  inn.style.width = '100%';
  inn.style.height = '200px';
  scr.appendChild(inn);
  document.body.appendChild(scr);
  wNoScroll = inn.offsetWidth;
  scr.style.overflow = 'auto';
  wScroll = inn.offsetWidth;
  document.body.removeChild(document.body.lastChild);
  return (wNoScroll - wScroll);
}
/*
<div class="component-tabMenu">
  <div class="-tabs-main">
    <ul>
      <li class="{cssClass} -tab -tab-selected"><a>Item</a></li>
      <li class="{cssClass} -tab"><a>Item</a></li>
    </ul>
  </div>
  <div class="-tabs-sub">
    <div class="{cssClass} -tab -tab-selected">
      <ul>
        <li class="-tab -tab-selected"><a>Item</a></li>
      </ul>
      <div class="clearer"></div>
    </div>    
    <div class="{cssClass}">
      <ul>
      </ul>
    </div>
  </div>
</div>
*/

/**
 * Menu supérieur : gestion des onglets.
 */
pack('LivePME').TabMenu = function _livePME_tabMenu(box, ctx, config, _next) {
    var self = this;

    this.ctx = ctx;
    this._tabs = [];
    this._activeTab = null;
    this._activeSubTab = null;
    this._box = box;

    box.applyState.bind(function(state) {
        state = LivePME.Site.getTab(state);
        self.set(state[0], state[1]);
    });

    $(function() {
        self.$self = $(config.select).addClass('component-tabMenu');

        self.$main =
      $('<div class="-tabs-main"><ul></ul></div>')
      .appendTo(self.$self)
      .children('ul');

        self.$sub =
      $('<div class="-tabs-sub"></div>')
      .appendTo(self.$self);

        var state = box.getState();

        state = LivePME.Site.getTab(state);
        self.setTabs(config.tabs, state[0], state[1]);

        if (_next) _next(self);
    });
};

/**
 * Modifier la liste des tabs. Le format est une arborescence:
 *
 * { <slug> : 
 *   { label : texte à afficher 
 *     cssClass : classe du <li>
 *     action : config pour un Url Box Changer activé par clic
 *     sub : 
 *     { <slug> : 
 *       { label
 *         action 
 *       },
 *       ...
 *     }
 *   }, 
 *   ...
 * }
 */
pack('LivePME').TabMenu.prototype.setTabs = function _livePME_tabMenu_setTabs(tabs,tab,subtab)
{
  this._tabs = tabs;
  return this.set(tab,subtab);
};


pack('LivePME').TabMenu.prototype.set = function _livePME_tabMenu_setTabs(tab,subtab)
{
  this._activeTab = tab;
  this._activeSubTab = subtab;
  return this.render();
};

pack('LivePME').TabMenu.prototype.render = function _livePME_tabMenu_render()
{
  var self = this;
  
  self.$main.html('');
  self.$sub.html('');
  
  $.each(this._tabs,function(slug,tab)
  {
    tab.$tab = 
      $('<li class="-tab"></li>')
      .addClass(tab.cssClass).appendTo(self.$main);
          
    tab.$a =
      $('<a href="javascript:void(0)"><span class="conner-left"><img width="6" height="6" src="../../Content/img/menu/coingauche.png"></span>' + tab.label + '<span class="conner-right"><img width="6" height="6" src="../../Content/img/menu/coindroit.png"></span></a>')
      //.text(tab.label)
      .appendTo(tab.$tab);
    
    tab.$sub = 
      $('<div class="-tab"></div>')
      .addClass(tab.cssClass)
      .appendTo(self.$sub);
    
    tab.$subList = 
      $('<ul></ul>')
      .appendTo(tab.$sub);
                
    if (slug == self._activeTab/* || !self._activeTab*/) 
    {
      self._activeTab = slug;
      tab.$tab.addClass('-tab-selected');
      tab.$sub.addClass('-tab-selected');
    }
    
    tab.$a.click(function(){
      Tangane.Url.Box.Changer(
        self._box,
        self.ctx,
        tab.action
      ).apply();      
    });

    /* activation d'un tab au survol
    tab.$a.mouseover(function(){
    self.$main.find('.-tab-selected').removeClass('-tab-selected');      
    self.$sub.find('div.-tab-selected').removeClass('-tab-selected');      
    tab.$tab.addClass('-tab-selected');
    tab.$sub.addClass('-tab-selected');
    });*/
    
    $.each(tab.sub, function(subSlug,subtab)
    {
      subtab.$tab =
        $('<li class="-tab"></li>')
        .appendTo(tab.$subList);
        
      subtab.$a = 
        $('<a href="javascript:void(0)"></a>')
        .text(subtab.label)
        .appendTo(subtab.$tab);
  
      /*if (!self._activeSubTab) 
      {
        self._activeSubTab = subSlug;
      }*/
      
      if (slug == self._activeTab && subSlug == self._activeSubTab )
      {
        subtab.$tab.addClass('-tab-selected');
      }
      
      subtab.$a.click(function(){
        Tangane.Url.Box.Changer(
          Tangane.Url.Box.root,
          self.ctx,
          subtab.action
        ).apply();      
      });
    });
  });
  
  return this;
};

pack('LivePME.UploadManager');

$("input[@name='uploadMailOptions']").live('change', function () {
  $("#uploadMailChoice").val($("input[@name='uploadMailOptions']:checked").val());
  if ($("input[@name='uploadMailOptions']:checked").val() == 2) {
    isUploadMailTask = true;
  } else {
    isUploadMailTask = false;
  }
});

$(document).ready(function () {
  $("#VisibleByUpload").live('change', function () {
    manageMailOptions();
  });
});

function manageMailOptions() {
  if ($("#VisibleByUpload").val() != "ClientProvider") {
    $(".mOptions").hide();
  } else {
    $(".mOptions").show();
  }
}

/* constructor & init */
LivePME.UploadManager = function _livePME_uploadManager(uploadUrl, concernUrl, docTypesUrl, fiscalsUrl, onClose) 
{
  if (!(this instanceof arguments.callee))
    return new arguments.callee();
    
  this.uploadUrl = uploadUrl;
  this.concernUrl = concernUrl;
  this.docTypesUrl = docTypesUrl;
  this.fiscalsUrl = fiscalsUrl;
  this.onClose = onClose;
};

LivePME.UploadManager.prototype.init = function _livePME_uploadManager_init(fileTypes, fileTypesDescription) {
  this.fileTypes = fileTypes;
  this.fileTypesDescription = fileTypesDescription;
  this.concern = 0;
  this.concernDisabled = false;
  this.docNature = 0;
  this.docType = 0;
  this.docTypeDisabled = false;
  this.fiscal = 0;
  this.entityId = 0;
  this.user = 0;
  this.refreshPage = true,
  $.uploadManager = this;
};

/* members */
//LivePME.UploadManager.swfComponent = null;
LivePME.UploadManager.misCode = null;
LivePME.UploadManager.orgId = null;
LivePME.UploadManager.uploadUrl = null;
LivePME.UploadManager.nonce = null;
LivePME.UploadManager.fileTypes = null;
LivePME.UploadManager.fileTypesDescription = null;
LivePME.UploadManager.onClose = null;
LivePME.UploadManager.lang = null;
LivePME.UploadManager.buttonText = '';
LivePME.UploadManager.uploadUrl = null;
LivePME.UploadManager.docTypesUrl = null;
LivePME.UploadManager.concern = null;
LivePME.UploadManager.concernDisabled = null;
LivePME.UploadManager.docType = null;
LivePME.UploadManager.docTypeDisabled = null;
LivePME.UploadManager.docNature = null;
LivePME.UploadManager.fiscal = null;
LivePME.UploadManager.user = null;
LivePME.UploadManager.shareTypesSelect = null;
LivePME.UploadManager.uploadSuccessMessage = null;
LivePME.UploadManager.title = null;
LivePME.UploadManager.closeButton = null;
LivePME.UploadManager.isCreditInvoice = null;
LivePME.UploadManager.isPayslip = null;
LivePME.UploadManager.entityId = null;
LivePME.UploadManager.refreshPage = null;
LivePME.UploadManager.planFolderLoaderToRefresh = null;
LivePME.UploadManager.planFolderTreeGridRootNodeForRefresh = null;
LivePME.UploadManager.docTypeIsSetByPlanFolder = null;
LivePME.UploadManager.IsEC = null;
LivePME.UploadManager.lockVisibility = true;

/* methods */
LivePME.UploadManager.prototype.createDiv = function _livePME_uploadManager_createDiv() {
  var self = this;

  var mailOptions = self.IsEc ? '<div class="info-item mOptions">' +
                    '<table><tr>' +
                      '<td><input type="radio" name="uploadMailOptions" id="uploadMailOptions1" value="1" style="width:15px;" checked="checked"/></td>' +
// TODO:TRANSLATE
                      '<td><label for="uploadMailOptions1" style="cursor:pointer;">&nbsp;Je notifie simplement par mail mon client que des documents sont à sa disposition.</label></td></tr>' +
                    '' +
                    '<tr><td><input type="radio" name="uploadMailOptions" id="uploadMailOptions2" value="2" style="width:15px;"/></td>' +
  // TODO:TRANSLATE
                      '<td><label for="uploadMailOptions2" style="cursor:pointer;">&nbsp;Je souhaite avoir un Accusé/Réception de cette mise à disposition de documents.</label></td></tr></table>' +
                    '</div><input type="hidden" id="uploadMailChoice" value="1"/>' : '';

  var concernSelect = '<div class="info-item" style="' + self.concernVisibility + '">' +
  // TODO:TRANSLATE
					              '<label>Concerne&nbsp;</label>' +
  // TODO:TRANSLATE
                        '<select name="ConcernUpload" id="ConcernUpload" onchange="$.uploadManager.updateDocumentTypes(this.value);" disabled="true">' +
                          '<option value="-1">&nbsp;</option>' +
                        '</select>' +
                      '</div>';

  var docTypeSelect = '<div class="info-item" style="' + self.typeVisibility + '">' +
  // TODO:TRANSLATE
					              '<label>Type de document&nbsp;</label>' +
                        '<select name="DocTypeUpload" id="DocTypeUpload" onchange="$.uploadManager.updateFiscals(this.value);" disabled="true">' +
                          '<option value="-1">&nbsp;</option>' +
                        '</select>' +
                      '</div>';

  var fiscalsSelect = '<div class="info-item" id="exercice">' +
                      '</div>';

  var userDiv = '<div style="display:none;visibility:hidden;" id="user">' +
                      '</div>';

  var uploadTextDiv = '<span class="info-item">' + self.uploadText + '</span>';
  var disableVisibility = self.lockVisibility ? 'disabled="disabled"' : '';

  var divContent =
    '<div id="dialogUpload" title="' + self.title + '">' +
      '<form id="uploadform" action="#" method="post" enctype="multipart/form-data">' +
        '<div id="swfu_container" style="margin: 0px 10px;">' +
          '<div> ' +
uploadTextDiv +
              '<div style="padding-left: 5px;">' +
                '<div class="info-item">' +
  // TODO:TRANSLATE
					        '<label>Les documents téléchargés seront visible par&nbsp;</label>' +
                  '<select name="VisibleByUpload" id="VisibleByUpload" ' + disableVisibility + '>' +
                    self.shareTypesSelect +
                  '</select>' +
                '</div>';
  //if (!this.concern)
  divContent += concernSelect;
  //if (!this.docType)
  divContent += docTypeSelect;
  divContent += fiscalsSelect;
  divContent += userDiv;
  divContent += mailOptions;
  divContent += '<div class="modal-action">' +
					        '<span id="spanButtonPlaceholder"></span>' +
					        '<input id="btnUpload" type="button" class="submit-button" value="' + self.buttonText + '" style="width: 116px; height: 22px; z-index:0;"/>' +
				        '</div>' +
				        '<br style="clear: both;" />' +
				        '<div id="dragtarget" class="dragdefault">' +
  // TODO:TRANSLATE
	                'Déposez vos fichers ici...' +
                '</div>' +
			          '<br style="clear: both;" />' +
			          '<div id="progressbar" style="height:5px;"></div>' +
			          '<div id="progressStatus"></div>' +
			          '<br style="clear: both;" /> ' +
		          '</div>' +
		          '<div  id="fsUploadProgress" style="overflow:auto;max-height:400px;">' +
		          '</div>' +
	        '</div>  ' +
        '</div>' +
      '</form>' +
    '</div>';

  $('body').append(divContent);
};

LivePME.UploadManager.prototype.clearDiv = function _livePME_uploadManager_clearDiv()
{
  $('#dragtarget').hide();
  $('#fsUploadProgress').empty();
  $("#progressStatus").html("&nbsp;");
  
  $("#progressbar").hide(); 
  $(".uploadfinish").remove();  
  
  $("#dialogUpload").parents(".ui-dialog-buttonpane button:eq(0)").attr("id","uploadCancel"); 
  $("#dialogUpload").parents(".ui-dialog-buttonpane button:eq(1)").attr("id","uploadClose");
  
  /*$("#uploadClose").addClass("button button-gray");
  $("#uploadCancel").addClass("button button-gray");
  
  $("#uploadClose").removeClass("ui-state-default ui-corner-all");
  $("#uploadCancel").removeClass("ui-state-default ui-corner-all");*/
  
  $("#uploadCancel").hide();
  $("#uploadClose").show();
};

LivePME.UploadManager.prototype.onInitComponent = function _livePME_uploadManager_onInitComponent()
{
  $.refreshUpload = false;       
};

LivePME.UploadManager.prototype.createComponent = function _livePME_uploadManager_createComponent() {
  var self = this;
  $.refreshUpload = false;

  /*var entityId = 0;
  if (self.fiscal == 0) {
    entityId = self.entityId;
  } else {
    entityId = self.fiscal;
  }*/
  
  $.swfUploadInstance = new SWFUpload(
  {
    upload_url: self.uploadUrl,
    post_params: {
      id: self.nonce,
      OrganizationId: self.orgId,
      misCode: self.misCode,
      visibleBy: 3,
      groupId: $.uploadGroupId,
      ennCode: self.concern,
      eltId: self.docType,
      entId: self.fiscal,
      entityId: self.entityId,
      lnkUsrId: self.user
    },
    file_size_limit: 0,
    file_types: self.fileTypes,
    file_types_description: self.fileTypesDescription, //'Mes fichiers',
    file_upload_limit: 1000,
    file_queue_limit: 300,
    use_query_string: false,
    requeue_on_error: false,
    prevent_swf_caching: true,
    preserve_relative_urls: false,

    //button_window_mode: SWFUpload.WINDOW_MODE.TRANSPARENT,
    button_cursor: SWFUpload.CURSOR.HAND,
    button_image_url: '/Scripts/swfupload-core/images/XPButtonNoText_160x22.png',
    button_text: '<span class="submit-button">' + self.buttonText + '</span>',
    button_text_style: '.submit-button { display:-moz-inline-box;display:inline-block;outline:none;padding:2px 6px 3px 6px;vertical-align:bottom;color:#666666;'
	    + 'border-color:#999999;font-size:11px;height:22px;cursor:pointer;-moz-box-shadow:0 1px 0 rgba(0,0,0,.1);-webkit-box-shadow:0 1px 0 rgba(0,0,0,.1);'
	    + 'font-family: "lucida grande", tahoma, verdana, Arial, Verdana, sans-serif; }'
	    + ' .submit-button:hover { background-position: 0px -49px;color:#FFFFFF;border-color:#7FABD1; }',


    swfupload_preload_handler: preLoad,
    swfupload_loaded_handler: self.onInitComponent,
    file_dialog_complete_handler: fileDialogComplete,
    upload_progress_handler: uploadProgress,
    upload_error_handler: uploadError,
    upload_success_handler: uploadSuccess,
    upload_complete_handler: uploadComplete,
    file_queue_error_handler: fileQueueError,
    upload_start_handler: uploadStart,
    file_queued_handler: fileQueued,

    button_placeholder_id: 'spanButtonPlaceholder',
    button_width: 115,
    button_height: 22,
    button_text_top_padding: 1,
    button_text_left_padding: 5,
    flash_url: '/Scripts/swfupload-core/swfupload.swf',
    flash9_url: '/Scripts/swfupload-core/swfupload_FP9.swf',
    custom_settings: {
      progressTarget: 'fsUploadProgress',
      cancelButtonId: 'btnCancel',
      uploadSuccessMessage: self.uploadSuccessMessage,
      closeButton: self.closeButton
    },
    debug: false
  });
};

LivePME.UploadManager.prototype.populate = function _livePME_uploadManager_populate() {
  var self = this;
  $.swfUploadInstance = null;

  $("#progressbar").progressbar({ value: 0 });

  var buttons = {};
  // TODO:TRANSLATE
  buttons['Annuler les téléchargements'] = function() {
    if (self.swfComponent) {
      $.swfUploadInstance.stopUpload();
      var stats;

      do {
        stats = $.swfUploadInstance.getStats();
        $.swfUploadInstance.cancelUpload();
      } while (stats.files_queued !== 0);
    }
    $(this).dialog("close");
  };
  // TODO:TRANSLATE
  buttons['Fermer'] = function() { $(this).dialog("close"); };

  $("#dialogUpload").dialog({
    closeOnEscape: true,
    autoOpen: false,
    draggable: true,
    width: 464,
    closeText: 'X',
    modal: true,
    minHeight: 0,
    minWidth: 0,
    resizable: false,
    close: function(event, ui) {
      var stats = $.swfUploadInstance.getStats();
      if ($.refreshUpload) {
        self.onClose($.uploadGroupId, $("#ConcernUpload").val(), self.isPayslip, self.isCreditInvoice, stats.successful_uploads, self.refreshPage, self.planFolderLoaderToRefresh, self.planFolderTreeGridRootNodeForRefresh, $("#uploadMailChoice").val(), self.misCode);
        //Tangane.Url.Box.root.invalidate();
        //Tangane.Url.Box.root.setState(Tangane.Url.Box.root.getState());
      };
      $('#dragtarget').removeClass('dragleave');
      $('#dragtarget').removeClass('dragenter');
      $.swfUploadInstance = null;
      $("#dialogUpload").remove();
      $("body").css("overflow", "");
    },
    beforeclose: function(event, ui) {
      setTimeout(function() { }, 1000);
    },
    open: function(event, ui) {
      $(".ui-dialog-buttonpane :button span").removeClass().addClass("button-inner")
      $(".ui-dialog-buttonpane :button").removeClass().addClass("button button-gray")
      $("body").css("overflow", "hidden");
      manageMailOptions();
    },
    buttons: buttons
  });

  self.createComponent(self.uploadUrl, self.nonce, self.fileTypes, self.orgId);

  //        $('#dragtarget').dragUploadable({
  //           fieldName: "upload",
  //           dragleaveClass: "dragleave",
  //           dragenterClass: "dragenter",
  //           progressTarget:'fsUploadProgress',
  //           params: { },
  //           postUrl: '/Upload.aspx/ReceiveHtml5/',
  //           onBeforeSend: function(options) {
  //             $.refreshUpload = true;
  //             var org = getCurrentOrganization().toString();
  //             var lang = getCurrentLanguage();
  //             
  //             var selectVisibleBy = $("#VisibleByUpload");
  //             var visibleBy = selectVisibleBy.val();
  //      
  //             options.postUrl = '/Upload.aspx/ReceiveHtml5/'+org+'/'+lang;
  //             options.params = { misCode: globalMisCode, shareType: visibleBy, groupId:$.uploadGroupId };
  //           },
  //           onProgress: function(e) {
  //           }
  //         });
};

LivePME.UploadManager.prototype.updateConcern = function _livePME_uploadManager_updateConcern(misCode) {
  $('#ConcernUpload').attr("disabled", true);
  var self = this;
  var org = self.orgId.toString();
  var lang = self.lang;
  $.ajaxSetup({ cache: true, async: false });
  $.getJSON(self.concernUrl + '/' + org + '/' + lang + '?missionCode=' + misCode.toString(), function(datas) {
    var item = !self.isCreditInvoice ? '<option value="-1">&nbsp;</option>' : '';
    for (var i = 0; i < datas.length; i++) {
      // alert(i + ' - ' + datas[i].NatureCode.toString() + '/' + datas[i].NatureName);
      if (self.isCreditInvoice && (datas[i].NatureCode == 6 || datas[i].NatureCode == 7) || !self.isCreditInvoice) {
        item += '<option value="' + datas[i].NatureCode.toString() + '" ';
        if (self.concern != null && self.concern != 0 && datas[i].NatureCode.toString() == self.concern.toString())
          item += 'selected="selected"';
        item += '>';
        item += datas[i].NatureName + '</option>';
      }
    }
    $('#ConcernUpload').html(item);
    if (!self.concernDisabled)
      $('#ConcernUpload').removeAttr("disabled");
  });

  if (self.concern != null && self.concern != 0 || self.isCreditInvoice)
    self.updateDocumentTypes($('#ConcernUpload').val());

  self.updateFiscals($('#DocTypeUpload').val());

};

LivePME.UploadManager.prototype.updateDocumentTypes = function _livePME_uploadManager_updateDocumentTypes(concern) {
  $('#DocTypeUpload').attr("disabled", true);
  var self = this;
  var org = self.orgId.toString();
  var lang = self.lang;
  $.ajaxSetup({ cache: false, async: false });
  $.getJSON(self.docTypesUrl + '/' + org + '/' + lang + '?ennCode=' + concern.toString(), function(datas) {
    var item = !self.isCreditInvoice ? '<option value="-1">&nbsp;</option>' : '';
    for (var i = 0; i < datas.length; i++) {
      //alert(i + ' - ' + datas[i].ElCode.toString() + '/' + datas[i].Name + '/' + self.docNature);
      if (self.docNature != 0 || (self.isCreditInvoice && (datas[i].ElCode == 110 || datas[i].ElCode == 111)) || (!self.isCreditInvoice && datas[i].ElCode != 104 && datas[i].ElCode != 110 && datas[i].ElCode != 111 && self.docNature == 0)) {
        item += '<option value="' + datas[i].ElType.toString() + '" ';
				if(self.docTypeIsSetByPlanFolder)
				{
					if (
						self.docNature != 0 && datas[i].ElCode.toString() == self.docNature.toString() && self.docType!=0 && self.docType!=null && self.docType.toString() == datas[i].ElType.toString() 
						|| (datas[i].ElCode == 110 && self.isCreditInvoice)
					){
						item += 'selected="selected"';
					}
				} 
				else 
				{
					if (self.docNature != 0 && datas[i].ElCode.toString() == self.docNature.toString() || datas[i].ElCode == 110){
						item += 'selected="selected"';
					}
				}
        item += '>';
        item += datas[i].Name + '</option>';
      }
    }
    $('#DocTypeUpload').html(item);
    if (!self.docTypeDisabled)
      $('#DocTypeUpload').removeAttr("disabled");

    $('#exercice').html("");
  });
  self.updateFiscals($('#DocTypeUpload').val());
};

LivePME.UploadManager.prototype.updateFiscals = function _livePME_uploadManager_updateFiscals(type) {
  var self = this;
  if (self.isCreditInvoice) {
    var org = self.orgId.toString();
    var lang = self.lang;
    $.ajaxSetup({ cache: false, async: false });
    $.getJSON(self.fiscalsUrl + '/' + org + '/' + lang, function (datas) {
      // TODO:TRANSLATE
      var item = '<label>Exercice fiscal&nbsp;</label>' +
                        '<select name="FiscalUpload" id="FiscalUpload">';
      for (var i = 0; i < datas.length; i++) {
        item += '<option value="' + datas[i].Value.toString() + '"';
				if(self.fiscal!=null && datas[i].Value!=null){
					if (datas[i].Value.toString()==self.fiscal.toString())
						item += 'selected="selected"';
				} else{
					if (datas[i].Selected)
						item += 'selected="selected"';
				}
        item += '>';
        item += datas[i].Text + '</option>';
      }
      item += '</select>';
      $('#exercice').html(item);
    });
  } else if (IsSocialDoc($("#ConcernUpload").val())) {
    $('#exercice').html("");
    $("#user").html(self.user);
  } else {
    $('#exercice').html("");
  }

};

LivePME.UploadManager.prototype.reset = function _livePME_uploadManager_reset() {
  var self = this;
  self.concern = 0;
  self.concernDisabled = false;
  self.docNature = 0;
  self.docType = 0;
  self.fiscal = 0;
  self.docTypeDisabled = false;
  self.user = 0;
  // TODO:TRANSLATE
  self.uploadSuccessMessage = '<span class="info-item">Termin&eacute;.</span>';
  self.typeVisibility = '';
  self.concernVisibility = '';
  self.closeButton = 'Fermer';
  self.uploadText = '';
  self.isCreditInvoice = false;
  self.isPayslip = false;
  self.refresh = true;
	self.docTypeIsSetByPlanFolder = false;
};


LivePME.UploadManager.prototype.uploadFiles = function _livePME_uploadManager_uploadFiles(shareTypes, misCode, orgId, nonce, groupId, lang, buttonText, ennCode, elnCode, userId, isCreditInvoice, isPayslip, isEc) {
  var self = this;

  self.reset();

  self.IsEc = isEc;
  self.shareTypesSelect = shareTypes;
  self.misCode = misCode;
  self.orgId = orgId;
  self.nonce = nonce;
  $.uploadGroupId = groupId;
  self.lang = lang;
  self.buttonText = buttonText;
  self.title = buttonText;
  self.isCreditInvoice = isCreditInvoice;
  self.isPayslip = isPayslip;
  self.lockVisibility = false;

  if (ennCode) {
    self.concern = ennCode;
    self.concernDisabled = true;
  }
  if (elnCode) {
    self.docNature = elnCode;
    self.docTypeDisabled = true;
    if (elnCode == 104) {
      // TODO:TRANSLATE
      self.uploadSuccessMessage = '<span class="info-item">Vos fiches de paie sont maintenant t&eacute;l&eacute;charg&eacute;es. Cliquez sur le bouton suivant pour les associer à des salari&eacute;s.</span>';
      // TODO:TRANSLATE
      self.title = 'Etape 1/2 : Ajouter les fiches de paie...';
      self.typeVisibility = 'display:none;visibility:hidden;';
      self.concernVisibility = 'display:none;visibility:hidden;';
      // TODO:TRANSLATE
      self.closeButton = 'Suivant...';
      // TODO:TRANSLATE
      self.uploadText = 'Sélectionnez les documents « fiche de paie » par salarié. <br />Précisez qui peut voir ces documents.';
    }
  }
  if (self.isCreditInvoice) {
    // TODO:TRANSLATE
    self.title = 'Etape 1/2 : Ajouter des factures et avoirs...';
    // TODO:TRANSLATE
    self.uploadSuccessMessage = '<span class="info-item">Vos factures sont maintenant t&eacute;l&eacute;charg&eacute;es. Cliquez sur le bouton suivant pour leur apporter des pr&eacute;cisions.</span>';
    // TODO:TRANSLATE
    self.closeButton = 'Suivant...';
  }
  if (self.concernDisabled)
    self.concernVisibility = 'display:none;visibility:hidden;';

  if (userId)
    self.user = userId;

  self.createDiv();
  self.populate();
  self.clearDiv();

  self.updateConcern(misCode);

  $('#dragtarget').hide();
  $("#dialogUpload").dialog("open");
};

LivePME.UploadManager.prototype.uploadFilesFromPlanFolderTab = function _livePME_uploadManager_uploadFilesFromPlanFolderTab(shareTypes, misCode, orgId, nonce, groupId, lang, buttonText, ennCode, elnCode, docTypeEltId, userId, isCreditInvoice, isPayslip, loaderToRefresh, treeGridRootNodeForRefresh, concernDisabled, docTypeDisabled, year, month, fiscalExercice, isEc) {
  var self = this;

  self.reset();

  self.IsEc = isEc;

	// don't refresh page !
  self.refreshPage = false;
	
	self.planFolderLoaderToRefresh = loaderToRefresh;
  self.planFolderTreeGridRootNodeForRefresh = treeGridRootNodeForRefresh;
	
  self.shareTypesSelect = shareTypes;
  self.misCode = misCode;
  self.orgId = orgId;
  self.nonce = nonce;
  $.uploadGroupId = groupId;
  self.lang = lang;
  self.buttonText = buttonText;
  self.title = buttonText;
  self.isCreditInvoice = isCreditInvoice;
  self.isPayslip = isPayslip;
  self.lockVisibility = false;
	
	if(docTypeEltId!=null) {
		self.docType = docTypeEltId;
		self.docTypeIsSetByPlanFolder = true;
	}
	
	if(fiscalExercice!=null){
		self.fiscal = fiscalExercice;
		self.updateFiscals(elnCode);
	}

  if (ennCode) {
    self.concern = ennCode;
  }
  
  if (elnCode) {
    self.docNature = elnCode;
    if (elnCode == 104) {
      // TODO:TRANSLATE
      self.uploadSuccessMessage = '<span class="info-item">Vos fiches de paie sont maintenant t&eacute;l&eacute;charg&eacute;es. Cliquez sur le bouton suivant pour les associer à des salari&eacute;s.</span>';
      // TODO:TRANSLATE
      self.title = 'Etape 1/2 : Ajouter les fiches de paie...';
      self.typeVisibility = 'display:none;visibility:hidden;';
      self.concernVisibility = 'display:none;visibility:hidden;';
      // TODO:TRANSLATE
      self.closeButton = 'Suivant...';
      // TODO:TRANSLATE
      self.uploadText = 'Sélectionnez les documents « fiche de paie » par salarié. <br />Précisez qui peut voir ces documents.';
    }
  }

  if (self.isCreditInvoice) {
    // TODO:TRANSLATE
    self.title = 'Etape 1/2 : Ajouter des factures et avoirs...';
    // TODO:TRANSLATE
    self.uploadSuccessMessage = '<span class="info-item">Vos factures sont maintenant t&eacute;l&eacute;charg&eacute;es. Cliquez sur le bouton suivant pour leur apporter des pr&eacute;cisions.</span>';
    // TODO:TRANSLATE
    self.closeButton = 'Suivant...';
  }

	self.concernDisabled = concernDisabled;
	self.docTypeDisabled = docTypeDisabled;
	
  if (self.concernDisabled)
    self.concernVisibility = 'display:none;visibility:hidden;';
	if (self.docTypeDisabled)
		self.typeVisibility = 'display:none;visibility:hidden;';

  if (userId)
    self.user = userId;

  self.createDiv();
  self.populate();
  self.clearDiv();

  self.updateConcern(misCode);

  $('#dragtarget').hide();
  $("#dialogUpload").dialog("open");

};

LivePME.UploadManager.prototype.uploadFilesPlanFolderForSharedFolders = function _livePME_uploadManager_uploadFilesPlanFolderForSharedFolders(shareTypes, misCode, orgId,
nonce, groupId, lang, buttonText, ennCode, elnCode, docTypeEltId, userId, isCreditInvoice, isPayslip, entId, loaderToRefresh, treeGridRootNodeForRefresh, isEc) {
	var self = this;

  self.reset();

  self.IsEc = isEc;
  self.planFolderLoaderToRefresh = loaderToRefresh;
  self.planFolderTreeGridRootNodeForRefresh = treeGridRootNodeForRefresh;
  self.shareTypesSelect = shareTypes;
  self.misCode = misCode;
  self.orgId = orgId;
  self.nonce = nonce;
  $.uploadGroupId = groupId;
  self.lang = lang;
  self.buttonText = buttonText;
  self.title = buttonText;
  self.isCreditInvoice = isCreditInvoice;
  self.isPayslip = isPayslip;
  self.entityId = entId;
  self.lockVisibility = true;

  // don't refresh page !
  self.refreshPage = false;

  if (ennCode) {
    self.concern = ennCode;
    self.concernDisabled = true;
  }
	
	if(docTypeEltId!=null) {
		self.docType = docTypeEltId;
		self.docTypeIsSetByPlanFolder = true;
	}
	if (elnCode) {
    self.docNature = elnCode;
    if (elnCode == 104) {
      // TODO:TRANSLATE
      self.uploadSuccessMessage = '<span class="info-item">Vos fiches de paie sont maintenant t&eacute;l&eacute;charg&eacute;es. Cliquez sur le bouton suivant pour les associer à des salari&eacute;s.</span>';
      // TODO:TRANSLATE
      self.title = 'Etape 1/2 : Ajouter les fiches de paie...';
      self.typeVisibility = 'display:none;visibility:hidden;';
      self.concernVisibility = 'display:none;visibility:hidden;';
      // TODO:TRANSLATE
      self.closeButton = 'Suivant...';
      // TODO:TRANSLATE
      self.uploadText = 'Sélectionnez les documents « fiche de paie » par salarié. <br />Précisez qui peut voir ces documents.';
    }
  }

  $('#ConcernUpload').attr("disabled", true);
  self.concernVisibility = 'display:none;visibility:hidden;';
  self.typeVisibility = 'display:none;visibility:hidden;';
  
  self.createDiv();
  self.populate();
  self.clearDiv();

  //self.updateConcern(misCode);

  $('#ConcernUpload').html('<option value="' + self.concern + '" selected="selected"></option>');

  $('#dragtarget').hide();
  $("#dialogUpload").dialog("open");
};

LivePME.UploadManager.prototype.uploadFilesPlanFolder = function _livePME_uploadManager_uploadFilesPlanFolder(shareTypes, misCode, orgId,
nonce, groupId, lang, buttonText, ennCode, elnCode, userId, isCreditInvoice, isPayslip, entId, loaderToRefresh, treeGridRootNodeForRefresh, isEc) {
  var self = this;

  self.reset();

  self.IsEc = isEc;
  self.planFolderLoaderToRefresh = loaderToRefresh;
  self.planFolderTreeGridRootNodeForRefresh = treeGridRootNodeForRefresh;
  self.shareTypesSelect = shareTypes;
  self.misCode = misCode;
  self.orgId = orgId;
  self.nonce = nonce;
  $.uploadGroupId = groupId;
  self.lang = lang;
  self.buttonText = buttonText;
  self.title = buttonText;
  self.isCreditInvoice = isCreditInvoice;
  self.isPayslip = isPayslip;
  self.entityId = entId;
  self.lockVisibility = false;
  // don't refresh page !
  self.refreshPage = false;

  if (ennCode) {
    self.concern = ennCode;
    self.concernDisabled = true;
  }

  $('#ConcernUpload').attr("disabled", true);
  self.concernVisibility = 'display:none;visibility:hidden;';
  self.typeVisibility = 'display:none;visibility:hidden;';

  self.createDiv();
  self.populate();
  self.clearDiv();

  //self.updateConcern(misCode);

  $('#ConcernUpload').html('<option value="' + self.concern + '" selected="selected"></option>');

  $('#dragtarget').hide();
  $("#dialogUpload").dialog("open");
};

pack('LivePME.ImageViewer');

var mode;

/* Constructeur */
LivePME.ImageViewer = function _livePME_imageViewer() {
  if (!(this instanceof arguments.callee))
    return new arguments.callee();
};

/* Membres */
LivePME.ImageViewer.elm = null;
LivePME.ImageViewer.elm_w = null;
LivePME.ImageViewer.elm_h = null;
LivePME.ImageViewer.img = null;
LivePME.ImageViewer.img_w = null;
LivePME.ImageViewer.img_h = null;
LivePME.ImageViewer.img_w_init = null;
LivePME.ImageViewer.img_h_init = null;
LivePME.ImageViewer.rapport = null;
LivePME.ImageViewer.zoom_max = null;
LivePME.ImageViewer.zoom_multiple = null;
LivePME.ImageViewer.page = null;

/* Actions */
LivePME.ImageViewer.origin = '#tool-6';
LivePME.ImageViewer.zoomin = '#tool-9';
LivePME.ImageViewer.zoomout = '#tool-8';
LivePME.ImageViewer.zoomsel = '#tool-4';
LivePME.ImageViewer.drag = '#tool-3';
LivePME.ImageViewer.maxwidth = '#tool-5';

LivePME.ImageViewer.prototype.create = function _livePME_imageViewer_create(div, page) {
  var self = this;

  self.page = page;
  self.zoom_max = 400;
  self.zoom_multiple = 1.3;

  $(LivePME.ImageViewer.origin).click(function() {
    self.origin();
    self.notdrag();
  });

  $(LivePME.ImageViewer.zoomin).click(function() {
    self.zoomin();
    self.notdrag();
  });

  $(LivePME.ImageViewer.zoomout).click(function() {
    self.zoomout();
    self.notdrag();
  });

  $(LivePME.ImageViewer.maxwidth).click(function() {
    self.maxwidth();
    self.notdrag();
  });

  $(LivePME.ImageViewer.zoomsel).click(function() {
    self.zoomsel();
  });

  $(LivePME.ImageViewer.drag).click(function() {
    self.drag();
  });

  self.elm = $(div);
  self.img = $(div).find("img");

  self.keepmode();
};

LivePME.ImageViewer.prototype.initialize = function _livePME_imageViewer_initialize(h) {
  var self = this;

  self.img.css('position', 'relative');

  // Div height
  self.elm.height(h || self.elm.width());
  
  if (jQuery.browser.msie)
    self.elm.height(self.elm.height() + 3);
  
  // Div sizes
  self.elm_w = self.elm.width();
  self.elm_h = self.elm.height();

  // Images sizes
  self.img_w = self.img.width();
  self.img_h = self.img.height();

  // Portrait ou paysage
  self.rapport = (self.img_w / self.img_h);

  if (self.rapport < 1) {
    self.img_h_init = self.elm.height();
    self.img_w_init = self.img_h_init * self.rapport;
  } else {
    self.img_w_init = self.elm.width();
    self.img_h_init = self.img_w_init / self.rapport;
  }
    
};

LivePME.ImageViewer.prototype.keepmode = function _livePME_imageViewer_keepmode() {
  var self = this;
  self.initialize();
  if (mode == "maxwidth") {
    self.maxwidth();
    $(LivePME.ImageViewer.maxwidth).addClass('selected');
    $(LivePME.ImageViewer.origin).removeClass('selected');
  } else {
    self.origin();
    $(LivePME.ImageViewer.origin).addClass('selected');
    $(LivePME.ImageViewer.maxwidth).removeClass('selected');
  }
};

LivePME.ImageViewer.prototype.keepmodefull = function _livePME_imageViewer_keepmode(h) {
  var self = this;
  self.initialize(h);
  if (mode == "maxwidth") {
    self.maxwidthfull(h);
    $(LivePME.ImageViewer.maxwidth).addClass('selected');
    $(LivePME.ImageViewer.origin).removeClass('selected');
  } else {
    self.origin();
    $(LivePME.ImageViewer.origin).addClass('selected');
    $(LivePME.ImageViewer.maxwidth).removeClass('selected');
  }
};

// Affiche une image en taille max pour quelle soit entièrement visible.
LivePME.ImageViewer.prototype.origin = function _livePME_imageViewer_origin() {
  mode = "origin";
  var self = this;
  self.img.width(self.img_w_init);
  self.img.height(self.img_h_init);
  self.verticalcenter();
  self.elm.css("overflow-x", "hidden");
};

// Idem mais pour le mode plein écran.
LivePME.ImageViewer.prototype.maxwidthfull = function _livePME_imageViewer_maxwidthfull(h) {
  var self = this;
  self.initialize(h);
  self.maxwidth();
  $(LivePME.ImageViewer.maxwidth).addClass('selected');
  $(LivePME.ImageViewer.origin).removeClass('selected');
};

// Affiche une image en taille max pour quelle soit entièrement visible.
LivePME.ImageViewer.prototype.maxwidth = function _livePME_imageViewer_maxwidth() {
  mode = "maxwidth";
  var self = this;
  if (self.rapport < 1) {
    self.img.width(self.elm.width());
    self.img.height((self.img.width() / self.rapport));
  } else {
    self.img.width(self.elm.width());
    self.img.height((self.img.width() / self.rapport));
  }
  self.verticalcenter();
  self.elm.css("overflow-x", "hidden");
};

// Zoom +
LivePME.ImageViewer.prototype.zoomin = function _livePME_imageViewer_zoomin() {
  var self = this;
  var new_w = self.img.width() * self.zoom_multiple;
  var new_h = self.img.height() * self.zoom_multiple;
  var p = 100 * (new_w / self.img_w_init);

  // Zoom max atteint
  if (p > self.zoom_max) {
    new_w = Math.floor(self.img_w_init * self.zoom_max / 100);
    new_h = Math.floor(self.img_h_init * self.zoom_max / 100);
  }
  self.img.width(new_w);
  self.img.height(new_h);
  self.verticalcenter();
  self.elm.css("overflow-x", "");
};

// Zoom -
LivePME.ImageViewer.prototype.zoomout = function _livePME_imageViewer_zoomout() {
  var self = this;
  var new_w = self.img.width() / self.zoom_multiple;
  var new_h = self.img.height() / self.zoom_multiple;
  var p = 100 * (new_w / self.img_w_init);

  // Zoom min atteint
  if (p <= 100) {
    new_w = self.img_w_init;
    new_h = self.img_h_init;
    $(LivePME.ImageViewer.origin).addClass('selected');
    self.elm.css("overflow-x", "hidden");
  }
  else
    self.elm.css("overflow-x", "");
  self.img.width(new_w);
  self.img.height(new_h);
  self.verticalcenter();
  
};

// Centre verticalement une image en mode paysage.
LivePME.ImageViewer.prototype.verticalcenter = function _livePME_imageViewer_verticalcenter() {
  var self = this;
  var h = self.img.height();
  self.img.css("margin-top", (Math.max(0, (self.elm.height() - h) / 2)) + "px");
  self.img.css("top", "").css("left", "").css("right", "").css("bottom", "");
};

// draggable
LivePME.ImageViewer.prototype.drag = function _livePME_imageViewer_drag() {
  var self = this;
  self.notdrag();
  self.img.draggable({ disabled: false });
  self.elm.css("overflow-x", "");
};

// not draggable
LivePME.ImageViewer.prototype.notdrag = function _livePME_imageViewer_notdrag() {
  var self = this;
  self.img.draggable({ disabled: true });
  self.img.imgAreaSelect({ remove: true });
};

LivePME.ImageViewer.prototype.zoomsel = function _livePME_imageViewer_zoomsel() {
  var self = this;
  self.notdrag();

  self.s = self.img.imgAreaSelect({
    movable: false,
    hide: true,
    handles: false,
    autoHide: true,
    onSelectEnd: function(image, sel) {
      if (sel.x1 != sel.x2 && sel.y1 != sel.y2) {
        var p = 100 * (self.img.width() / self.img_w_init);

        if (p < self.zoom_max) {
          var t = self.img.width() / sel.width;
          var z = 100 * (self.img.width() * t) / self.img_w_init;

          if (z > self.zoom_max)
            z = self.zoom_max;

          self.img.width(self.img_w_init * z / 100);
          self.img.height(self.img_h_init * z / 100);
          self.img.css('left', (sel.x1/p*100) * -1 * z / 100 + 'px').css('top', (sel.y1/p*100) * -1 * z / 100 + 'px');
        }
        self.s.imgAreaSelect({ remove: true });
        self.zoomsel();
      }
    }
  });
  self.elm.css("overflow-x", "");
};

pack('LivePME.ImageViewerFull');

var isFull = false;
var hide = "#pagefootercontainer, #headerpanel, #liveboard-outer, #navig";

var fullInWasCalled = false;

var viewerdiv = null;
var viewer = null;
var compta = false;
var first = true;
var isPlanFolder = false;

/* Constructeur */
LivePME.ImageViewerFull = function _livePME_imageViewerFull() {
  if (!(this instanceof arguments.callee))
    return new arguments.callee();
};

/* Membres */
LivePME.ImageViewerFull.full = '#tool-7';
LivePME.ImageViewerFull.fullOut = '.fullOut';

LivePME.ImageViewerFull.prototype.create = function _livePME_imageViewerFull_create(div, v, c) {
  var self = this;
  viewerdiv = $(div);
  viewer = v;
  compta = c == 1 ? true : false;

  if ($('#planFolder-fullOut') != null && typeof ($('#planFolder-fullOut')) != 'undefined')
    isPlanFolder = true;

  if (isFull)
    fullIn();

  $(LivePME.ImageViewerFull.full).click(function () {
    if (!isFull) {
      fullIn();
      fullIn();
    }
    else
      fullOut();
  });

  $(LivePME.ImageViewerFull.fullOut).click(function () {
    fullOut();
  });
};

var previousHeight = 0;
var fullHeight = 0;

function fullIn() {
  fullInWasCalled = true;

  isFull = true;
  previousHeight = viewerdiv.height();

  if (fullHeight == 0)
    fullHeight = viewerdiv.height();
  else {
    viewerdiv.height(fullHeight);
  }

  var pos = viewerdiv.offset();
  var docHeight = $(document).height();

  $(hide).css("display", "none");
  $(hide).css("visibility", "hidden");

  $("li.full").addClass('selected');
  $("#pagecontainer").css("max-width", "100%");
  viewerdiv.width("100%");

  var h = docHeight - (pos!=null?pos.top:0) - 20;

  viewerdiv.height(h);

  if (first && compta) {
    viewer.maxwidthfull(h);
    first = false;
  }
  else {
    viewer.keepmodefull(h);
  }

  if (!isPlanFolder) {
    // D�sactivation du focus dans le cas du planfolder
    // car cela provoque un pb de calage sous Chrome
    viewerdiv.focus();
  }

  if (compta) {
    viewerdiv.scrollTop(viewerdiv.height() + 200);
  }

  scrollTo(0, 0);

  // only in plan folder tab
  if (isPlanFolder) {
    // in plan folder => show a link to come back to non full screen mode
    $('#planFolder-fullOut').css("display", "block");
    $('#planFolder-fullOut').css("visibility", "visible");
    $('#planFolder-fullIn').css('display', 'none');
    $('#planFolder-fullIn').css('visibility', 'hidden');

    // to correctly adjust panels width for full screen in plan folder
    var mainPanelInPlanFolder = Ext.getCmp('MainPanel');
    if (mainPanelInPlanFolder != undefined) {
      mainPanelInPlanFolder.setWidth(mainPanelInPlanFolder.getWidth());
    }
    if (typeof (modifyHeightDocImagePanel) == "function") {
      modifyHeightDocImagePanel();
    }
  }
  
}

function fullOut() {
  fullInWasCalled = false;

  if (isFull) {
    isFull = false;
    $(hide).css("display", "block");
    $(hide).css("visibility", "visible");
    $("li.full").removeClass('selected');
    $("#pagecontainer").css("max-width", "1235px");

    viewerdiv.height(previousHeight);
    viewer.keepmode();
    viewerdiv.focus();

    $(document).scrollTop(null);
    compta = false;
    first = true;

    // only in plan folder tab
    if ($('#planFolder-fullOut').attr('id') != undefined) {
      // in plan folder => hide the link that allows to come back to non full screen mode
      $('#planFolder-fullOut').css("display", "none");
      $('#planFolder-fullOut').css("visibility", "hidden");
      $('#planFolder-fullIn').css('display', 'block');
      $('#planFolder-fullIn').css('visibility', 'visible');

      // to correctly adjust panels width for full screen in plan folder
      var mainPanelInPlanFolder = Ext.getCmp('MainPanel');
      if (mainPanelInPlanFolder != undefined) {
        mainPanelInPlanFolder.setWidth(mainPanelInPlanFolder.getWidth());
      }

      // to have good width and height for doc images
      // doesn't work anymore
      // var stateImageBox = Tangane.Url.Box.root.contentBox.DocDetailPF.getState();
      // Tangane.Url.Box.root.contentBox.DocDetailPF.invalidate();
      // Tangane.Url.Box.root.contentBox.DocDetailPF.setState(stateImageBox);
      // new version
      reloadDocPreview();

      modifyHeightDocImagePanel();
    }
    
  }  
}

if (navigator.appName == 'Microsoft Internet Explorer') {

  EditDocumentButton3 = new ActiveXObject("SharePoint.OpenDocuments.3");
  if (!EditDocumentButton3) {
    EditDocumentButton2 = new ActiveXObject("SharePoint.OpenDocuments.2");
    if (!EditDocumentButton2) {
      EditDocumentButton1 = new ActiveXObject("SharePoint.OpenDocuments.1");
    }
  }

  function editDocWebdav(idElemToGetPath) {
    if (EditDocumentButton3) {
      EditDocumentButton3.EditDocument($('#' + idElemToGetPath).val());
    } else if (EditDocumentButton2) {
      EditDocumentButton2.EditDocument($('#' + idElemToGetPath).val());
    } else if (EditDocumentButton1) {
      EditDocumentButton1.EditDocument($('#' + idElemToGetPath).val());
    }
  }

}

