/*!
 * jQuery JavaScript Library v1.4.2
 * http://jquery.com/
 *
 * Copyright 2010, John Resig
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * Includes Sizzle.js
 * http://sizzlejs.com/
 * Copyright 2010, The Dojo Foundation
 * Released under the MIT, BSD, and GPL Licenses.
 *
 * Date: Sat Feb 13 22:33:48 2010 -0500
 */
(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o<i;o++)e(a[o],b,f?d.call(a[o],o,e(a[o],b)):d,j);return a}return i?
e(a[0],b):w}function J(){return(new Date).getTime()}function Y(){return false}function Z(){return true}function na(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function oa(a){var b,d=[],f=[],e=arguments,j,i,o,k,n,r;i=c.data(this,"events");if(!(a.liveFired===this||!i||!i.live||a.button&&a.type==="click")){a.liveFired=this;var u=i.live.slice(0);for(k=0;k<u.length;k++){i=u[k];i.origType.replace(O,"")===a.type?f.push(i.selector):u.splice(k--,1)}j=c(a.target).closest(f,a.currentTarget);n=0;for(r=
j.length;n<r;n++)for(k=0;k<u.length;k++){i=u[k];if(j[n].selector===i.selector){o=j[n].elem;f=null;if(i.preType==="mouseenter"||i.preType==="mouseleave")f=c(a.relatedTarget).closest(i.selector)[0];if(!f||f!==o)d.push({elem:o,handleObj:i})}}n=0;for(r=d.length;n<r;n++){j=d[n];a.currentTarget=j.elem;a.data=j.handleObj.data;a.handleObj=j.handleObj;if(j.handleObj.origHandler.apply(j.elem,e)===false){b=false;break}}return b}}function pa(a,b){return"live."+(a&&a!=="*"?a+".":"")+b.replace(/\./g,"`").replace(/ /g,
"&")}function qa(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function ra(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var j in f)for(var i in f[j])c.event.add(this,j,f[j][i],f[j][i].data)}}})}function sa(a,b,d){var f,e,j;b=b&&b[0]?b[0].ownerDocument||b[0]:s;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===s&&!ta.test(a[0])&&(c.support.checkClone||!ua.test(a[0]))){e=
true;if(j=c.fragments[a[0]])if(j!==1)f=j}if(!f){f=b.createDocumentFragment();c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=j?f:1;return{fragment:f,cacheable:e}}function K(a,b){var d={};c.each(va.concat.apply([],va.slice(0,b)),function(){d[this]=a});return d}function wa(a){return"scrollTo"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},Ra=A.jQuery,Sa=A.$,s=A.document,T,Ta=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/,
Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&&
(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this,
a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b===
"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this,
function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(j in e){i=a[j];o=e[j];if(a!==o)if(f&&o&&(c.isPlainObject(o)||c.isArray(o))){i=i&&(c.isPlainObject(i)||
c.isArray(i))?i:c.isArray(o)?[]:{};a[j]=c.extend(f,i,o)}else if(o!==w)a[j]=o}return a};c.extend({noConflict:function(a){A.$=Sa;if(a)A.jQuery=Ra;return c},isReady:false,ready:function(){if(!c.isReady){if(!s.body)return setTimeout(c.ready,13);c.isReady=true;if(Q){for(var a,b=0;a=Q[b++];)a.call(s,c);Q=null}c.fn.triggerHandler&&c(s).triggerHandler("ready")}},bindReady:function(){if(!xa){xa=true;if(s.readyState==="complete")return c.ready();if(s.addEventListener){s.addEventListener("DOMContentLoaded",
L,false);A.addEventListener("load",c.ready,false)}else if(s.attachEvent){s.attachEvent("onreadystatechange",L);A.attachEvent("onload",c.ready);var a=false;try{a=A.frameElement==null}catch(b){}s.documentElement.doScroll&&a&&ma()}}},isFunction:function(a){return $.call(a)==="[object Function]"},isArray:function(a){return $.call(a)==="[object Array]"},isPlainObject:function(a){if(!a||$.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!aa.call(a,"constructor")&&!aa.call(a.constructor.prototype,
"isPrototypeOf"))return false;var b;for(b in a);return b===w||aa.call(a,b)},isEmptyObject:function(a){for(var b in a)return false;return true},error:function(a){throw a;},parseJSON:function(a){if(typeof a!=="string"||!a)return null;a=c.trim(a);if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return A.JSON&&A.JSON.parse?A.JSON.parse(a):(new Function("return "+
a))();else c.error("Invalid JSON: "+a)},noop:function(){},globalEval:function(a){if(a&&Va.test(a)){var b=s.getElementsByTagName("head")[0]||s.documentElement,d=s.createElement("script");d.type="text/javascript";if(c.support.scriptEval)d.appendChild(s.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,b,d){var f,e=0,j=a.length,i=j===w||c.isFunction(a);if(d)if(i)for(f in a){if(b.apply(a[f],
d)===false)break}else for(;e<j;){if(b.apply(a[e++],d)===false)break}else if(i)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d=a[0];e<j&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||"").replace(Wa,"")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a==="string"||c.isFunction(a)||typeof a!=="function"&&a.setInterval?ba.call(b,a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=0,f=b.length;d<f;d++)if(b[d]===
a)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length==="number")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!==w;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,j=a.length;e<j;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,j=0,i=a.length;j<i;j++){e=b(a[j],j,d);if(e!=null)f[f.length]=e}return f.concat.apply([],f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b==="string"){d=a;a=d[b];b=w}else if(b&&
!c.isFunction(b)){d=b;b=w}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b},uaMatch:function(a){a=a.toLowerCase();a=/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||!/compatible/.test(a)&&/(mozilla)(?:.*? rv:([\w.]+))?/.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}},browser:{}});P=c.uaMatch(P);if(P.browser){c.browser[P.browser]=true;c.browser.version=P.version}if(c.browser.webkit)c.browser.safari=
true;if(ya)c.inArray=function(a,b){return ya.call(b,a)};T=c(s);if(s.addEventListener)L=function(){s.removeEventListener("DOMContentLoaded",L,false);c.ready()};else if(s.attachEvent)L=function(){if(s.readyState==="complete"){s.detachEvent("onreadystatechange",L);c.ready()}};(function(){c.support={};var a=s.documentElement,b=s.createElement("script"),d=s.createElement("div"),f="script"+J();d.style.display="none";d.innerHTML="   <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected,
parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent=
false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n=
s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true,
applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando];
else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this,
a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===
w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i,
cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1)if(e.className){for(var j=" "+e.className+" ",
i=e.className,o=0,k=b.length;o<k;o++)if(j.indexOf(" "+b[o]+" ")<0)i+=" "+b[o];e.className=c.trim(i)}else e.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(k){var n=c(this);n.removeClass(a.call(this,k,n.attr("class")))});if(a&&typeof a==="string"||a===w)for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1&&e.className)if(a){for(var j=(" "+e.className+" ").replace(Aa," "),i=0,o=b.length;i<o;i++)j=j.replace(" "+b[i]+" ",
" ");e.className=c.trim(j)}else e.className=""}return this},toggleClass:function(a,b){var d=typeof a,f=typeof b==="boolean";if(c.isFunction(a))return this.each(function(e){var j=c(this);j.toggleClass(a.call(this,e,j.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var e,j=0,i=c(this),o=b,k=a.split(ca);e=k[j++];){o=f?o:!i.hasClass(e);i[o?"addClass":"removeClass"](e)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,"__className__",this.className);this.className=
this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(Aa," ").indexOf(a)>-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j<d;j++){var i=
e[j];if(i.selected){a=c(i).val();if(b)return a;f.push(a)}}return f}if(Ba.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Za,"")}return w}var o=c.isFunction(a);return this.each(function(k){var n=c(this),r=a;if(this.nodeType===1){if(o)r=a.call(this,k,n.val());if(typeof r==="number")r+="";if(c.isArray(r)&&Ba.test(this.type))this.checked=c.inArray(n.val(),r)>=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected=
c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");
a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g,
function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split(".");
k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a),
C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B<r.length;B++){u=r[B];if(d.guid===u.guid){if(i||k.test(u.namespace)){f==null&&r.splice(B--,1);n.remove&&n.remove.call(a,u)}if(f!=
null)break}}if(r.length===0||f!=null&&r.length===1){if(!n.teardown||n.teardown.call(a,o)===false)Ca(a,e,z.handle);delete C[e]}}else for(var B=0;B<r.length;B++){u=r[B];if(i||k.test(u.namespace)){c.event.remove(a,n,u.handler,B);r.splice(B--,1)}}}if(c.isEmptyObject(C)){if(b=z.handle)b.elem=null;delete z.events;delete z.handle;c.isEmptyObject(z)&&c.removeData(a)}}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type=
e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&&
f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;
if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e<j;e++){var i=d[e];if(b||f.test(i.namespace)){a.handler=i.handler;a.data=i.data;a.handleObj=i;i=i.handler.apply(this,arguments);if(i!==w){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
fix:function(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||s;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=s.documentElement;d=s.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||
d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==w)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,a.origType,c.extend({},a,{handler:oa}))},remove:function(a){var b=true,d=a.origType.replace(O,"");c.each(c.data(this,
"events").live||[],function(){if(d===this.origType.replace(O,""))return b=false});b&&c.event.remove(this,a.origType,oa)}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};var Ca=s.removeEventListener?function(a,b,d){a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent("on"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=
a;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y,
isImmediatePropagationStopped:Y};var Da=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},Ea=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ea:Da,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ea:Da)}}});if(!c.support.submitBubbles)c.event.special.submit=
{setup:function(){if(this.nodeName.toLowerCase()!=="form"){c.event.add(this,"click.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="submit"||d==="image")&&c(b).closest("form").length)return na("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="text"||d==="password")&&c(b).closest("form").length&&a.keyCode===13)return na("submit",this,arguments)})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}};
if(!c.support.changeBubbles){var da=/textarea|input|select/i,ea,Fa=function(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",
e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,
"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a,
d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j<o;j++)c.event.add(this[j],d,i,f)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&&
!a.preventDefault)for(var d in a)this.unbind(d,a[d]);else{d=0;for(var f=this.length;d<f;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,f){return this.live(b,d,f,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind("live"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}},
toggle:function(a){for(var b=arguments,d=1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(f){var e=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,e+1);f.preventDefault();return b[e].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var Ga={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,f,e,j){var i,o=0,k,n,r=j||this.selector,
u=j?this:c(this.context);if(c.isFunction(f)){e=f;f=w}for(d=(d||"").split(" ");(i=d[o++])!=null;){j=O.exec(i);k="";if(j){k=j[0];i=i.replace(O,"")}if(i==="hover")d.push("mouseenter"+k,"mouseleave"+k);else{n=i;if(i==="focus"||i==="blur"){d.push(Ga[i]+k);i+=k}else i=(Ga[i]||i)+k;b==="live"?u.each(function(){c.event.add(this,pa(i,r),{data:f,selector:r,handler:e,origType:i,origHandler:e,preType:n})}):u.unbind(pa(i,r),e)}}return this}});c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),
function(a,b){c.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});A.attachEvent&&!A.addEventListener&&A.attachEvent("onunload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});(function(){function a(g){for(var h="",l,m=0;g[m];m++){l=g[m];if(l.nodeType===3||l.nodeType===4)h+=l.nodeValue;else if(l.nodeType!==8)h+=a(l.childNodes)}return h}function b(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];
if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1&&!p){t.sizcache=l;t.sizset=q}if(t.nodeName.toLowerCase()===h){y=t;break}t=t[g]}m[q]=y}}}function d(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1){if(!p){t.sizcache=l;t.sizset=q}if(typeof h!=="string"){if(t===h){y=true;break}}else if(k.filter(h,[t]).length>0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift();
t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D||
g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h<g.length;h++)g[h]===g[h-1]&&g.splice(h--,1)}return g};k.matches=function(g,h){return k(g,null,null,h)};k.find=function(g,h,l){var m,q;if(!g)return[];
for(var p=0,v=n.order.length;p<v;p++){var t=n.order[p];if(q=n.leftMatch[t].exec(g)){var y=q[1];q.splice(1,1);if(y.substr(y.length-1)!=="\\"){q[1]=(q[1]||"").replace(/\\/g,"");m=n.find[t](q,h,l);if(m!=null){g=g.replace(n.match[t],"");break}}}}m||(m=h.getElementsByTagName("*"));return{set:m,expr:g}};k.filter=function(g,h,l,m){for(var q=g,p=[],v=h,t,y,S=h&&h[0]&&x(h[0]);g&&h.length;){for(var H in n.filter)if((t=n.leftMatch[H].exec(g))!=null&&t[2]){var M=n.filter[H],I,D;D=t[1];y=false;t.splice(1,1);if(D.substr(D.length-
1)!=="\\"){if(v===p)p=[];if(n.preFilter[H])if(t=n.preFilter[H](t,v,l,p,m,S)){if(t===true)continue}else y=I=true;if(t)for(var U=0;(D=v[U])!=null;U++)if(D){I=M(D,t,U,v);var Ha=m^!!I;if(l&&I!=null)if(Ha)y=true;else v[U]=false;else if(Ha){p.push(D);y=true}}if(I!==w){l||(v=p);g=g.replace(n.match[H],"");if(!y)return[];break}}}if(g===q)if(y==null)k.error(g);else break;q=g}return v};k.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var n=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
CLASS:/\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},
relative:{"+":function(g,h){var l=typeof h==="string",m=l&&!/\W/.test(h);l=l&&!m;if(m)h=h.toLowerCase();m=0;for(var q=g.length,p;m<q;m++)if(p=g[m]){for(;(p=p.previousSibling)&&p.nodeType!==1;);g[m]=l||p&&p.nodeName.toLowerCase()===h?p||false:p===h}l&&k.filter(h,g,true)},">":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m<q;m++){var p=g[m];if(p){l=p.parentNode;g[m]=l.nodeName.toLowerCase()===h?l:false}}}else{m=0;for(q=g.length;m<q;m++)if(p=g[m])g[m]=
l?p.parentNode:p.parentNode===h;l&&k.filter(h,g,true)}},"":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("parentNode",h,m,g,p,l)},"~":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("previousSibling",h,m,g,p,l)}},find:{ID:function(g,h,l){if(typeof h.getElementById!=="undefined"&&!l)return(g=h.getElementById(g[1]))?[g]:[]},NAME:function(g,h){if(typeof h.getElementsByName!=="undefined"){var l=[];
h=h.getElementsByName(g[1]);for(var m=0,q=h.length;m<q;m++)h[m].getAttribute("name")===g[1]&&l.push(h[m]);return l.length===0?null:l}},TAG:function(g,h){return h.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,h,l,m,q,p){g=" "+g[1].replace(/\\/g,"")+" ";if(p)return g;p=0;for(var v;(v=h[p])!=null;p++)if(v)if(q^(v.className&&(" "+v.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},
CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m,
g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},
text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},
setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return h<l[3]-0},gt:function(g,h,l){return h>l[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h=
h[3];l=0;for(m=h.length;l<m;l++)if(h[l]===g)return false;return true}else k.error("Syntax error, unrecognized expression: "+q)},CHILD:function(g,h){var l=h[1],m=g;switch(l){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(l==="first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":l=h[2];var q=h[3];if(l===1&&q===0)return true;h=h[0];var p=g.parentNode;if(p&&(p.sizcache!==h||!g.nodeIndex)){var v=0;for(m=p.firstChild;m;m=
m.nextSibling)if(m.nodeType===1)m.nodeIndex=++v;p.sizcache=h}g=g.nodeIndex-q;return l===0?g===0:g%l===0&&g/l>=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m===
"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g,
h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l<m;l++)h.push(g[l]);else for(l=0;g[l];l++)h.push(g[l]);return h}}var B;if(s.documentElement.compareDocumentPosition)B=function(g,h){if(!g.compareDocumentPosition||
!h.compareDocumentPosition){if(g==h)i=true;return g.compareDocumentPosition?-1:1}g=g.compareDocumentPosition(h)&4?-1:g===h?0:1;if(g===0)i=true;return g};else if("sourceIndex"in s.documentElement)B=function(g,h){if(!g.sourceIndex||!h.sourceIndex){if(g==h)i=true;return g.sourceIndex?-1:1}g=g.sourceIndex-h.sourceIndex;if(g===0)i=true;return g};else if(s.createRange)B=function(g,h){if(!g.ownerDocument||!h.ownerDocument){if(g==h)i=true;return g.ownerDocument?-1:1}var l=g.ownerDocument.createRange(),m=
h.ownerDocument.createRange();l.setStart(g,0);l.setEnd(g,0);m.setStart(h,0);m.setEnd(h,0);g=l.compareBoundaryPoints(Range.START_TO_END,m);if(g===0)i=true;return g};(function(){var g=s.createElement("div"),h="script"+(new Date).getTime();g.innerHTML="<a name='"+h+"'/>";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&&
q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML="<a href='#'></a>";
if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="<p class='TEST'></p>";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}();
(function(){var g=s.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}:
function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q<p;q++)k(g,h[q],l);return k.filter(m,l)};c.find=k;c.expr=k.selectors;c.expr[":"]=c.expr.filters;c.unique=k.uniqueSort;c.text=a;c.isXMLDoc=x;c.contains=E})();var eb=/Until$/,fb=/^(?:parents|prevUntil|prevAll)/,
gb=/,/;R=Array.prototype.slice;var Ia=function(a,b,d){if(c.isFunction(b))return c.grep(a,function(e,j){return!!b.call(e,j,e)===d});else if(b.nodeType)return c.grep(a,function(e){return e===b===d});else if(typeof b==="string"){var f=c.grep(a,function(e){return e.nodeType===1});if(Ua.test(b))return c.filter(b,f,!d);else b=c.filter(b,f)}return c.grep(a,function(e){return c.inArray(e,b)>=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f<e;f++){d=b.length;
c.find(a,this[f],b);if(f>0)for(var j=d;j<b.length;j++)for(var i=0;i<d;i++)if(b[i]===b[j]){b.splice(j--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,f=b.length;d<f;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(Ia(this,a,false),"not",a)},filter:function(a){return this.pushStack(Ia(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j=
{},i;if(f&&a.length){e=0;for(var o=a.length;e<o;e++){i=a[e];j[i]||(j[i]=c.expr.match.POS.test(i)?c(i,b||this.context):i)}for(;f&&f.ownerDocument&&f!==b;){for(i in j){e=j[i];if(e.jquery?e.index(f)>-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a===
"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",
d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?
a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType===
1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/<tbody/i,jb=/<|&#?\w+;/,ta=/<script|<object|<embed|<option|<style/i,ua=/checked\s*(?:[^=]|=\s*.checked.)/i,Ma=function(a,b,d){return hb.test(d)?
a:b+"></"+d+">"},F={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=
c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},
wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},
prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,
this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);
return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja,
""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(f){this.empty().append(a)}}else c.isFunction(a)?this.each(function(e){var j=c(this),i=j.html();j.empty().append(function(){return a.call(this,e,i)})}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&
this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=c(this),f=d.html();d.replaceWith(a.call(this,b,f))});if(typeof a!=="string")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){function f(u){return c.nodeName(u,"table")?u.getElementsByTagName("tbody")[0]||
u.appendChild(u.ownerDocument.createElement("tbody")):u}var e,j,i=a[0],o=[],k;if(!c.support.checkClone&&arguments.length===3&&typeof i==="string"&&ua.test(i))return this.each(function(){c(this).domManip(a,b,d,true)});if(c.isFunction(i))return this.each(function(u){var z=c(this);a[0]=i.call(this,u,b?z.html():w);z.domManip(a,b,d)});if(this[0]){e=i&&i.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:sa(a,this,o);k=e.fragment;if(j=k.childNodes.length===
1?(k=k.firstChild):k.firstChild){b=b&&c.nodeName(j,"tr");for(var n=0,r=this.length;n<r;n++)d.call(b?f(this[n],j):this[n],n>0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]);
return this}else{e=0;for(var j=d.length;e<j;e++){var i=(e>0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["",
""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]==="<table>"&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e=
c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]?
c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja=
function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter=
Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a,
"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f=
a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=
a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=/<script(.|\s)*?\/script>/gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!==
"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("<div />").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this},
serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),
function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,
global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&&
e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)?
"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache===
false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B=
false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since",
c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E||
d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x);
g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===
1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b===
"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional;
if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");
this[a].style.display=d||"";if(c.css(this[a],"display")==="none"){d=this[a].nodeName;var f;if(la[d])f=la[d];else{var e=c("<"+d+" />").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a<b;a++)this[a].style.display=c.data(this[a],"olddisplay")||"";return this}},hide:function(a,b){if(a||a===0)return this.animate(K("hide",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");!d&&d!=="none"&&c.data(this[a],
"olddisplay",c.css(this[a],"display"))}a=0;for(b=this.length;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b){var d=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||d?this.each(function(){var f=d?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(K("toggle",3),a,b);return this},fadeTo:function(a,b,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d)},
animate:function(a,b,d,f){var e=c.speed(b,d,f);if(c.isEmptyObject(a))return this.each(e.complete);return this[e.queue===false?"each":"queue"](function(){var j=c.extend({},e),i,o=this.nodeType===1&&c(this).is(":hidden"),k=this;for(i in a){var n=i.replace(ia,ja);if(i!==n){a[n]=a[i];delete a[i];i=n}if(a[i]==="hide"&&o||a[i]==="show"&&!o)return j.complete.call(this);if((i==="height"||i==="width")&&this.style){j.display=c.css(this,"display");j.overflow=this.style.overflow}if(c.isArray(a[i])){(j.specialEasing=
j.specialEasing||{})[i]=a[i][1];a[i]=a[i][0]}}if(j.overflow!=null)this.style.overflow="hidden";j.curAnim=c.extend({},a);c.each(a,function(r,u){var z=new c.fx(k,j,r);if(Ab.test(u))z[u==="toggle"?o?"show":"hide":u](a);else{var C=Bb.exec(u),B=z.cur(true)||0;if(C){u=parseFloat(C[2]);var E=C[3]||"px";if(E!=="px"){k.style[r]=(u||1)+E;B=(u||1)/z.cur(true)*B;k.style[r]=B+E}if(C[1])u=(C[1]==="-="?-1:1)*u+B;z.custom(B,u,E)}else z.custom(B,u,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);
this.each(function(){for(var f=d.length-1;f>=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration===
"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||
c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;
this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=
this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem,
e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||
c.fx.stop()},stop:function(){clearInterval(W);W=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===b.elem}).length};c.fn.offset="getBoundingClientRect"in s.documentElement?
function(a){var b=this[0];if(a)return this.each(function(e){c.offset.setOffset(this,a,e)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);var d=b.getBoundingClientRect(),f=b.ownerDocument;b=f.body;f=f.documentElement;return{top:d.top+(self.pageYOffset||c.support.boxModel&&f.scrollTop||b.scrollTop)-(f.clientTop||b.clientTop||0),left:d.left+(self.pageXOffset||c.support.boxModel&&f.scrollLeft||b.scrollLeft)-(f.clientLeft||b.clientLeft||0)}}:function(a){var b=
this[0];if(a)return this.each(function(r){c.offset.setOffset(this,a,r)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,f=b,e=b.ownerDocument,j,i=e.documentElement,o=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;for(var k=b.offsetTop,n=b.offsetLeft;(b=b.parentNode)&&b!==o&&b!==i;){if(c.offset.supportsFixedPosition&&f.position==="fixed")break;j=e?e.getComputedStyle(b,null):b.currentStyle;
k-=b.scrollTop;n-=b.scrollLeft;if(b===d){k+=b.offsetTop;n+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.nodeName))){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=d;d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&j.overflow!=="visible"){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=j}if(f.position==="relative"||f.position==="static"){k+=o.offsetTop;n+=o.offsetLeft}if(c.offset.supportsFixedPosition&&
f.position==="fixed"){k+=Math.max(i.scrollTop,o.scrollTop);n+=Math.max(i.scrollLeft,o.scrollLeft)}return{top:k,left:n}};c.offset={initialize:function(){var a=s.body,b=s.createElement("div"),d,f,e,j=parseFloat(c.curCSS(a,"marginTop",true))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b);
c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a,
d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top-
f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset":
"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in
e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window);
jQuery.noConflict();
/**
 * 
 * All content on this website (including text, images, source
 * code and any other original works), unless otherwise noted,
 * is licensed under a Creative Commons License.
 * 
 * http://creativecommons.org/licenses/by-nc-sa/2.5/
 * 
 * Copyright (C) 2004-2010 Open-Xchange, Inc.
 * Mail: info@open-xchange.com 
 * 
 * @author Viktor Pracht <viktor.pracht@open-xchange.com>
 * 
 */

var init = {};

function initAdd(name, ids) {
	var list = init[name];
	if (!list) list = init[name] = [];
	for (var i in ids) list.push(ids[i]);
}

function initSet(name, ids) {
	var list = init[name];
	if (!list) list = init[name] = {};
	for (var i in ids) list[i] = ids[i];
}
/**
 * All content on this website (including text, images, source
 * code and any other original works), unless otherwise noted,
 * is licensed under a Creative Commons License.
 * 
 * http://creativecommons.org/licenses/by-nc-sa/2.5/
 * 
 * Copyright (C) 2009 Open-Xchange, Inc.
 * Mail: info@open-xchange.com 
 * 
 * @author Viktor Pracht <viktor.pracht@open-xchange.com>
 */

/**
 * Creates the prototype object of a subclass.
 * @param {Function} parent The constructor function of the superclass.
 * @param {Object} prototype The prototype object of the subclass, containing
 * only new and overridden members.
 * @type Object
 * @return The prototype object with all inherited members added to it.
 */
function extend(parent, prototype) {
    for (var i in parent.prototype)
        if (!(i in prototype)) prototype[i] = parent.prototype[i];
    return prototype;
}

/**
 * Empty function. Does nothing.
 */
function emptyFunction() {}

/**
 * Identity function. Returns its first parameter.
 */
function identity(x) { return x; }

/**
 * Constant function.
 * Returns a new function which returns the first parameter of this function.
 * @param x
 * @type Function
 * @return A function which returns x.
 */
function constant(x) { return function() { return x; }; }

/**
 * Returns whether an object is empty.
 * @param {Object} x The tested object
 * @type Boolean
 * @return True if the object does not have enumerable properties,
 * false otherwise.
 */
function isEmpty(x) {
    for (var i in x) return false;
    return true;
}

/** @ignore */
function assert(value) { if (!value) alert("Assertion failed"); }

/**
 * Internet Explorer version number, or undefined if using a real browser.
 * Since comparisons with undefined always return false, checks involving this
 * variable should use the "<" or "<=" operators, e. g. "if (IE < 8)".
 */
var IE = (navigator.appName != "Microsoft Internet Explorer") ? undefined
    : Number(navigator.appVersion.match(/MSIE (\d+\.\d+)/)[1]);

/**
 * Boolean indicating the MacOS platform. If true, selections use the Meta key
 * instead of the Ctrl key.
 */
var Mac = navigator.platform.substring(0, 3) == "Mac";

/**
 * Creates a function which can add or remove a CSS class to a DOM node.
 * When adding, a class name is not duplicated if already present.
 * When removing, all instances of a class name are removed if present multiple
 * times.
 * @param {String} name The class name to add or remove. It must not contain any
 * characters which have special meaning in regular expressions.
 * @type Function
 * @return A function which accepts two parameters:<ul> 
 * <li>{DOM node} node The DOM node to which the class name is applied.</li>
 * <li>{Boolean} set Whether the class name should be added (true) or
 * removed (false).</li>
 * @ignore
 */
function classNameSetter(name) {
    var regex = new RegExp("(\\s)\\s*" + name + "\\s+|\\s*" + name + "\\s*",
                           "g");
    return function (node, set) {
        var c = String(node.className);
        regex.lastIndex = 0;
        if (set) {
            if (!regex.test(c)) c = c ? c + " " + name : name;
        } else {
            c = c.replace(regex, "$1");
        }
        node.className = c;
        return c;
    };
}

/**
 * Escapes a string to be included in an HTML file.
 * The characters '<', '>', '&', '"' and "'" are replaced by their HTML entity
 * references.
 * @param {String} s The string to escape.
 * @type String
 * @return The escaped string.
 */
function escapeHTML(s) {
    return s.replace(/[<>&"]/g, function(c) { return htmlEscapes[c]; });
}
var htmlEscapes = { "<": "&lt;", ">": "&gt;", "&": "&amp;", "\"": "&quot;" };

/**
 * Escapes a string to be included in a regular expression.
 * @param {String} s The string to escape.
 * @type String
 * @return The escaped string.
 */
function escapeRegExp(s) { return s.replace(/([|^$\\.*+?()[\]{}])/g, '\\$1'); }
/**
 * 
 * All content on this website (including text, images, source
 * code and any other original works), unless otherwise noted,
 * is licensed under a Creative Commons License.
 * 
 * http://creativecommons.org/licenses/by-nc-sa/2.5/
 * 
 * Copyright (C) 2004-2010 Open-Xchange, Inc.
 * Mail: info@open-xchange.com 
 * 
 * @author Viktor Pracht <viktor.pracht@open-xchange.com>
 * 
 */

  ////////////////////////
 //   Event handling   //
////////////////////////

/**
 * Table of currently registered global event listeners.
 * @private
 */
var events = {};

/**
 * Encapsulates a method of an object in a function.
 * @param {Object} obj The instance of the object to call.
 * @param {String} methodname The name of the method to call on obj.
 * @return A function that can be used as the second parameter to {@link #register}.
 * @type Function
 * @see #register
 */
function encapsulateMethod(obj, methodname) {
	return function() { obj[methodname].apply(obj, arguments); };
}

/**
 * Registers a function as a listener for a global event.
 * Registering the same function for the same event multiple times has no effect.
 * Any given function is called exactly once for every triggered event.
 * The order of calls to different functions registered for the same event is
 * not guaranteed.
 * @param {String} name The name of the event. Event names are global.
 * @param {Function} callback A function to be called when the named event is
 * triggered.
 * @see #unregister
 */
function register(name, callback) {
    if (name in triggerSingleton.singletons) {
        callback.apply(window, triggerSingleton.singletons[name]);
    }
    var list = (name in events) ? events[name] : events[name] = new Array();
	for (var i in list) if (list[i] == callback) return;
	list.push(callback);
}

/**
 * Unregisters a previously registered listener from a global event.
 * Unregistering a listener which was not previously registered for the specified
 * event has no effect.
 * @param {String} name The name of the event. Event names are global.
 * @param {Function} callback The same function object as was used in a call to
 * {@link #register}. Return values from {@link #encapsulateMethod} must be stored
 * and reused instead of calling #encapsulateMethod again.
 */
function unregister(name, callback) {
	var list = events[name];
	if (!list) return;
	for (var i = 0; i < list.length; i++) {
		if (list[i] == callback) {
			list.splice(i, 1);
			return;
		}
	}
}
/*	
	if (!list) return;
	var cb = list.pop();
	if (cb == callback) return;
	for (var i = list.length - 1; i >= 0; i--) {
		if (list[i] == callback) {
			list[i] = cb;
			return;
		}
	}
	if(cb)
		list.push(cb);
}
*/

/**
 * Triggers a global event and calls all registered listeners.
 * @param {String} name The name of the event. Event names are global.
 * @param params Any further parameters are passed to the called listeners.
 * @see #register
 */
function triggerEvent() {
	var list = events[arguments[0]];
	var args = null;
	args = new Array(arguments.length - 1);
	for (var i = 1; i < arguments.length; i++)
		args[i - 1] = arguments[i];
	if (list) 
	{
		for (var cb in list)
		{ 
			list[cb].apply(null, args);
		}
	}
}

/**
 * Triggers a singleton event. A singleton event should be triggered
 * only once. Listeners, which are registered after the event was
 * triggered, are called immediately. The arguments to the event are saved
 * forever to be passed to eventual late listeners.
 * @param {String} name The name of the event. Event names are global.
 * @param params Any further parameters are passed to the called listeners.
 * @see #triggerEvent
 */
function triggerSingleton(name) {
    triggerEvent.apply(this, arguments);
    var args = triggerSingleton.singletons[name] = [];
    for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);
}
triggerSingleton.singletons = {};

function Events() {
	this.events = {};
	this.posted = {};
}

Events.prototype = {
	/**
	 * Registers a function as a listener for an event.
	 * Registering the same function for the same event multiple times has no
	 * effect. Any given function is called exactly once for every triggered
	 * event. The order of calls to different functions registered for the same
	 * event is not guaranteed.
	 * @param {String} name The name of the event. Event names are specific to
	 * an Events instance.
	 * @param {Function} callback A function to be called when the named event
	 * is triggered. Which parameters are passed to the callback is defined by
	 * the event.
	 * @see #unregister
	 */
	register: function(name, callback) {
		var list = (name in this.events) ? this.events[name]
		                                 : this.events[name] = new Array();
		for (var i in list) if (list[i] == callback) return;
		list.push(callback);
	},

	/**
	 * Unregisters a previously registered listener from an event.
	 * Unregistering a listener which was not previously registered for the
	 * specified event has no effect.
	 * @param {String} name The name of the event. Event names are specific to
	 * an Events instance.
	 * @param {Function} callback The same function object as was used in a call
	 * to {@link #register}. Return values from {@link #encapsulateMethod} must
	 * be stored and reused instead of calling #encapsulateMethod again.
	 */
	unregister: function(name, callback) {
		var list = this.events[name];
		if (!list) return;
		var cb = list.pop();
		if (cb == callback) return;
		for (var i = list.length - 1; i >= 0; i--) {
			if (list[i] == callback) {
				list[i] = cb;
				return;
			}
		}
		if(cb)
			list.push(cb);
	},

	/**
	 * Triggers an event and calls all registered listeners.
	 * @param {String} name The name of the event. Event names are specific to
	 * an Events instance.
	 * @param params Any further parameters are passed to the called listeners.
	 * @see #register
	 */
	trigger: function() {
		var list = this.events[arguments[0]];
		if (list) {
			var args = new Array(arguments.length - 1);
			for (var i = 1; i < arguments.length; i++)
				args[i - 1] = arguments[i];
			for (var cb in list) list[cb].apply(null, args);
		}
	},
	
	/**
	 * Postpones the triggering of an event until the currently executing
	 * JavaScript code exits and the browser enters its event loop.
	 * If an event is already posted, any further posts for the same event have
	 * no effect.
	 * @param {String} name The name of the event. Event names are specific to
	 * an Events instance.
	 * @param params Any further parameters are passed to the called listeners.
	 * If a parameter is a fuction, then it is evaluated once immediately before
	 * calling the first listener and the return value is passed to
	 * the listeners. In the case of multiple calls with the same event name
	 * without an opportunity to execute the listeners, the parameter values
	 * from the last call are used.
	 * @see #trigger
	 */
	post: function(name) {
		if (!this.posted[name]) {
			var Self = this;
			setTimeout(function() {
				var args = Self.posted[name];
				delete Self.posted[name];
				var list = Self.events[name];
				if (list) {
					var params = new Array(args.length - 1);
					for (var i = 1; i < args.length; i++) {
						if (args[i].constructor == Function)
							params[i - 1] = args[i]();
						else
							params[i - 1] = args[i];
					}
					for (var cb in list) list[cb].apply(null, params);
				}
			}, 0);
		}
		this.posted[name] = arguments;
	}
};
/**
 * 
 * All content on this website (including text, images, source
 * code and any other original works), unless otherwise noted,
 * is licensed under a Creative Commons License.
 * 
 * http://creativecommons.org/licenses/by-nc-sa/2.5/
 * 
 * Copyright (C) 2004-2010 Open-Xchange, Inc.
 * Mail: info@open-xchange.com 
 * 
 * @author Viktor Pracht <viktor.pracht@open-xchange.com>
 * 
 */

/**
 * Translates a string
 * @function
 * @param {String} text The original English text to translate.
 * @type String
 * @return The translated text.
 * @ignore
 */
var _;

/**
 * Translates a string
 * @function
 * @param {String} text The original English text to translate.
 * @type String
 * @return The translated text.
 * @ignore
 */
var gettext;

/**
 * Translates a string
 * @function
 * @param {String} context A context to differentiate multiple identical texts
 * with different translations.
 * @param {String} text The original English text to translate.
 * @type String
 * @return The translated text.
 * @ignore
 */
var pgettext;

/**
 * Translates a string
 * @function
 * @param {String} domain An i18n domain to use for the translation.
 * @param {String} context A context to differentiate multiple identical texts
 * with different translations.
 * @param {String} text The original English text to translate.
 * @type String
 * @return The translated text.
 */
var dpgettext;

/**
 * Translates a string containing numbers.
 * @function
 * @param {String} singular The original English text for the singular form.
 * @param {String} plural The original English text for the plural form.
 * @param {Number} n The number which determines which text form is used.
 * @param {String} context An optional context to differentiate multiple
 * identical texts with different translations.
 * @param {String} domain An optional i18n domain to use for the translation.
 * @type String
 * @return The translated text.
 * @ignore
 */
var ngettext;

/**
 * Translates a string containing numbers.
 * @function
 * @param {String} context A context to differentiate multiple identical texts
 * with different translations.
 * @param {String} singular The original English text for the singular form.
 * @param {String} plural The original English text for the plural form.
 * @param {Number} n The number which determines which text form is used.
 * @type String
 * @return The translated text.
 * @ignore
 */
var npgettext;

/**
 * Translates a string containing numbers.
 * @function
 * @param {String} domain An i18n domain to use for the translation.
 * @param {String} context A context to differentiate multiple identical texts
 * with different translations.
 * @param {String} singular The original English text for the singular form.
 * @param {String} plural The original English text for the plural form.
 * @param {Number} n The number which determines which text form is used.
 * @type String
 * @return The translated text.
 */
var dnpgettext;

/**
 * Adds a new i18n domain, usually for a plugin.
 * @function
 * @param {String} domain A new domain name, usually the plugin name.
 * @param {String} pattern A Pattern which is used to find the PO or JS file on
 * the server. The pattern is processed by formatting it with the language ID
 * as the only parameter. The formatted result is used to download the file
 * from the server.
 */
var bindtextdomain;

/**
 * Changes the current language which is used for all subsequent translations.
 * Also translates all currently displayed strings.
 * @function
 * @param {String} name The ID of the new language.
 */
var setLanguage;

/**
 * Returns the translation dictionary for the specified language.
 * @private
 * @function
 * @param {String} name The language ID of the dictionary to return.
 * @type Object
 * @return The translation dictionary of the specified language.
 */
var getDictionary;

/**
 * Formats a string by replacing printf-style format specifiers in the string
 * with dynamic parameters. Flags, width, precision and length modifiers are
 * not supported. All type conversions are performed by the standard toString()
 * JavaScript method.
 * @param {I18nString} string The format string.
 * @param params Either an array with parameters or multiple separate
 * parameters.
 * @type I18nString
 * @return The formatted string.
 */
function format(string, params) {
	var param_array = params;
	if (typeof(params) != "object") {
		param_array = new Array(arguments.length - 1);
		for (var i = 1; i < arguments.length; i++)
			param_array[i - 1] = arguments[i];
	}
    if (typeof string == "function") {
        return function() { return formatRaw(string(), param_array); };
    } else {
        return formatRaw(string, param_array);
    }
}

/**
 * Formats a string by replacing printf-style format specifiers in the string
 * with dynamic parameters. Flags, width, precision and length modifiers are
 * not supported. All type conversions (except from I18nString) are performed
 * by the standard toString() JavaScript method.
 * @param {String} string The format string.
 * @param params An array with parameters.
 * @type String
 * @return The formatted string.
 * @ignore
 */
function formatRaw(string, params) {
    var index = 0;
    return String(string).replace(/%(([0-9]+)\$)?[A-Za-z]/g,
        function(match, pos, n) {
            if (pos) index = n - 1;
            return params[index++];
        }).replace(/%%/, "%");
}

/**
 * Formats and translates an error returned by the server.
 * @param {Object} result the JSON object as passed to a JSON callback function.
 * @param {String} formatString an optional format string with the replacement
 * parameters <dl><dt>%1$s</dt><dd>the error code,</dd>
 * <dt>%2$s</dt><dd>the fomratter error message,</dd>
 * <dt>%3$s</dt><dd>the unique error ID.</dd></dl>
 * @type String
 * @returns the formatted and translated error message.
 * @ignore
 */
function formatError(result, formatString) {
	//#. %1$s is the error code.
	//#. %2$s is the formatted error message.
    //#. %3$s is the unique error ID.
	//#, c-format
	return format(formatString || _("Error: %2$s (%1$s, %3$s)"), result.code,
                  format(_(result.error), result.error_params),
                  result.error_id);
}

(function() {
    var current, current_lang;
    var domains = { "": "lang/%s.js" };
    var languages = {};
    var originals = {};
	var counter = 0;

	_ = gettext = function(text) { return dpgettext("", "", text); };
    
    pgettext = function(context, text) { return dpgettext("", context, text); };
    
    dpgettext = function(domain, context, text) {
        var c = current && current[domain || ""];
        var key = context ? context + "\0" + text : text;
        return c && c.dictionary[key] || text;
    };
    
    ngettext = function(singular, plural, n) {
        return dnpgettext("", "", singular, plural, n);
    };

	npgettext = function(context, singular, plural, n) {
        return dnpgettext("", context, singular, plural, n);
    };
    
    dnpgettext = function(domain, context, singular, plural, n) {
		var text = n != 1 ? plural : singular;
        var c = current && current[domain || ""];
        if (!c) return text;
        var key = context ? [context, "\0", singular, "\x01", plural].join("")
                          : [               singular, "\x01", plural].join("");
		var translation = c.dictionary[key];
		if (!translation) return text;
		return translation[Number(c.plural(n))] || text;
	};

    function parse(pattern, file) {
        if (pattern.substring(pattern.length - 2) == "po") {
            return parsePO(file);
        } else {
            return (new Function("return " + file))();
        }
    }
    
    bindtextdomain = function(domain, pattern) {
        domains[domain] = pattern;
        if (languages[current_lang] === current) setLanguage(current_lang);
    };

	setLanguage = function(name) {
        current_lang = name;
        var new_lang = languages[name];
		if (!new_lang) {
            loadLanguage(name);
            return;
        }
        for (var i in domains) {
            if (!(i in new_lang)) {
                loadLanguage(name);
                return;
            }
        }
		current = new_lang;
		for (var i in init.i18n) {
			var attrs = init.i18n[i].split(",");
			var node = $(i);
			if(node) {
				for (var j = 0; j < attrs.length; j++) {
					var attr = attrs[j];
					var id = attr + "," + i;
					var text = attr ? node.getAttributeNode(attr)
					                : node.firstChild;
                    var val = text && String(text.nodeValue);
					if (!val || val == "\xa0" )
                        alert(format('Invalid i18n for id="%s"', i));
					var original = originals[id];
					if (!original) original = originals[id] = val;
                    var context = "";
                    var pipe = original.indexOf("|");
                    if (pipe >= 0) {
                        context = original.substring(0, pipe);
                        original = original.substring(pipe + 1);
                    }
					text.nodeValue = dpgettext("", context, original);
				}
			}
		}
        triggerEvent("LanguageChangedInternal");
		triggerEvent("LanguageChanged");
    };
    
    function loadLanguage(name) {
		// check the main window
        if (corewindow != window) {
            var core_dict = corewindow.getDictionary(name);
            if (core_dict) {
    			current = languages[name] = core_dict;
                setLanguage(name);
                return;
    		}
        }
        var curr = languages[name];
        if (!curr) curr = languages[name] = {};
        var join = new Join(function() { setLanguage(name); });
        for (var d in domains) {
            if (!(d in curr)) {
            	// get file name
            	var file = format(domains[d], name);
            	// add pre-compression (specific languages only)
            	file = file.replace(/(de_DE|en_GB|en_US)\.js/, "$1.jsz");
            	// inject version
            	var url = urlify(file);
            	// get language file
                (new JSON()).get(url, null,
                    join.add((function(domain) {
                        return function(file) {
                            try {
                                languages[name][domain] =
                                    parse(domains[domain], file);
                            } catch (e) {
                                triggerEvent("OX_New_Error", 4, e);
                                join.add(); // prevent setLanguage()
                            }
                        };
                    })(d)), join.alt((function(domain) {
                        return function(result, status) {
                            languages[name][domain] = false;
                            return status == 404;
                        };
                    })(d)), true);
            }
		}
	}
	
	getDictionary = function(name) { return languages[name]; };	

})();

function parsePO(file) {
    parsePO.tokenizer.lastIndex = 0;
    var line_no = 1;
    
    function next() {
        while (parsePO.tokenizer.lastIndex < file.length) {
            var t = parsePO.tokenizer.exec(file);
            if (t[1]) continue;
            if (t[2]) {
                line_no++;
                continue;
            }
            if (t[3]) return t[3];
            if (t[4]) return t[4];
            if (t[5]) throw new Error(format(
                "Invalid character in line %s.", line_no));
        }
    }

    var lookahead = next();

    function clause(name, optional) {
        if (lookahead == name) {
            lookahead = next();
            var parts = [];
            while (lookahead && lookahead.charAt(0) == '"') {
                parts.push((new Function("return " + lookahead))());
                lookahead = next();
            }
            return parts.join("");
        } else if (!optional) {
            throw new Error(format(
                "Unexpected '%1$s' in line %3$s, expected '%2$s'.",
                lookahead, name, line_no));
        }
    }
    
    if (clause("msgid") != "") throw new Error("Missing PO file header");
    var header = clause("msgstr");
    if (parsePO.headerRegExp.exec(header)) {
        var po = (new Function("return " + header.replace(parsePO.headerRegExp,
            "{ nplurals: $1, plural: function(n) { return $2; }, dictionary: {} }"
            )))();
    } else {
        var po = { nplurals: 1, plural: function(n) { return 0; },
                   dictionary: {} };
    }
    while (lookahead) {
        var ctx = clause("msgctxt", true);
        var id = clause("msgid");
        var id_plural = clause("msgid_plural", true);
        var str;
        if (id_plural !== undefined) {
            id = id += "\x01" + id_plural;
            str = {};
            for (var i = 0; i < po.nplurals; i++) {
                str[i] = clause("msgstr[" + i + "]");
            }
        } else {
            str = clause("msgstr");
        }
        if (ctx) id = ctx + "\0" + id;
        po.dictionary[id] = str;
    }
    return po;
}

parsePO.tokenizer = new RegExp(
    '^(#.*|[ \\t\\v\\f]+)$' +                  // comment or empty line
    '|(\\r\\n|\\r|\\n)' +                      // linebreak (for line numbering)
    '|^(msg[\\[\\]\\w]+)(?:$|[ \\t\\v\\f]+)' + // keyword
    '|[ \\t\\v\\f]*(".*")\\s*$' +              // string
    '|(.)',                                    // anything else is an error
    "gm");

parsePO.headerRegExp = new RegExp(
    '^(?:[\\0-\\uffff]*\\n)?' +                         // ignored prefix
    'Plural-Forms:\\s*nplurals\\s*=\\s*([0-9]+)\\s*;' + // nplurals
                 '\\s*plural\\s*=\\s*([^;]*);' +        // plural
    '[\\0-\\uffff]*$'                                   // ignored suffix
);

/**
 * Encapsulation of a single translated text node which is created at runtime.
 * @param {Function} callback A function which is called as a method of
 * the created object and returns the current translated text.
 * @param {Object} template An optional object which is used for the initial
 * translation. All enumerable properties of the template will be copied to
 * the newly created object before the first call to callback.
 *
 * Fields of the created object:
 *
 * node: The DOM text node which is automatically translated.
 * @ignore
 */
function I18nNode(callback, template) {
    if (template) for (var i in template) this[i] = template[i];
    if (typeof callback != "function") {
        if (debug) {
            (console.warn || console.log || alert)(format(
                "The string \"%s\" is not internationalized!", callback));
            eval("debugger");
        }
        this.callback = function() { return _(callback); };
    } else {
        this.callback = callback;
    }
    this.index = ++I18nNode.counter;
	this.node = document.createTextNode(this.callback());
	this.enable();
}

I18nNode.prototype = {
	/**
	 * Updates the node contents. Is called whenever the current language
	 * changes and should be also called when the displayed value changes.
	 * @ignore
     */
	update: function() {
        if (typeof this.callback != "function") {
            console.error(format(
                "The callback \"%s\" has type \"%s\".",
                this.callback, typeof this.callback));
        } else {
/**#nocode+*/
            this.node.data = this.callback();
/**#nocode-*/
        }
    },
	
	/**
	 * Disables automatic updates for this object.
	 * Should be called when the text node is removed from the DOM tree.
     * @ignore
	 */
	disable: function() { delete I18nNode.nodes[this.index]; },
	
	/**
	 * Reenables previously disabled updates.
     * @ignore
	 */
 	enable: function() { I18nNode.nodes[this.index] = this; }
};

I18nNode.nodes = {};
I18nNode.counter = 0;

register("LanguageChanged", function() {
	for (var i in I18nNode.nodes) I18nNode.nodes[i].update();
});

/**
 * Creates an automatically updated node from a static text. The node can not
 * be removed.
 * @param {I18nString} text The text to be translated. It must be marked with
 * the <code>&#57;*i18n*&#57;</code> comment.
 * @param {String} context An optional context to differentiate multiple
 * identical texts with different translations. It must be marked with
 * the <code>&#57;*i18n context*&#57;</code> comment.
 * @param {String} domain An optional i18n domain to use for the translation.
 * @type Object
 * @return The new DOM text node.
 * @ignore
 */
function addTranslated(text, context, domain) {
	return (new I18nNode(typeof text == "function" ? text
        : function() { return dpgettext(domain, context, text); })).node;
}

/**
 * Returns whether a date is today.
 * @param utc The date. Any valid parameter to new Date() will do.
 * @type Boolean
 * @return true if the parameter has today's date, false otherwise.
 * @ignore
 */
function isToday(utc) {
    var today = new Date(now());
    today.setUTCHours(0, 0, 0, 0);
    var diff = (new Date(utc)).getTime() - today.getTime();
    return diff >= 0 && diff < 864e5; // ms/day
}

/**
 * The first week with at least daysInFirstWeek days in a given year is defined
 * as the first week of that year.
 * @ignore
 */
var daysInFirstWeek = 4;

/**
 * First day of the week.
 * 0 = Sunday, 1 = Monday and so on.
 * @ignore
 */
var weekStart = 1;

function getDays(d) { return Math.floor(d / 864e5); }

/**
 * Computes the week number of the specified Date object, taking into account
 * daysInFirstWeek and weekStart.
 * @param {Date} d The date for which to calculate the week number.
 * @param {Boolean} inMonth True to compute the week number in a month,
 * False for the week number in a year 
 * @type Number
 * @return Week number of the specified date.
 * @ignore
 */
function getWeek(d, inMonth) {
	var keyDay = getKeyDayOfWeek(d);
	var keyDate = new Date(keyDay * 864e5);
	var jan1st = Date.UTC(keyDate.getUTCFullYear(),
	                      inMonth ? keyDate.getUTCMonth() : 0);
	return Math.floor((keyDay - getDays(jan1st)) / 7) + 1;
}
 
/**
 * Returns the day of the week which decides the week number
 * @return Day of week
 */
function getKeyDayOfWeek(d) {
	var firstDay = getDayInSameWeek(d, weekStart);
	return (firstDay + 7 - daysInFirstWeek);
}

/**
 * Computes the number of the first day of the specified week, taking into
 * account weekStart.
 * @param  {Date} d The date for which to calculate the first day of week number.
 * type Number
 * @return First day in the week as the number of days since 1970-01-01.
 * @ignore
 */
function getDayInSameWeek(d, dayInWeek) {
	return getDays(d.getTime()) - (d.getUTCDay() - dayInWeek + 7) % 7; 
}

/**
 * Formats a Date object according to a format string.
 * @function
 * @param {String} format The format string. It has the same syntax as Java's
 * java.text.SimpleDateFormat, assuming a Gregorian calendar.
 * @param {Date} date The Date object to format. It must contain a Time value as
 * defined in the HTTP API specification.
 * @type String
 * @return The formatted date and/or time.
 */
var formatDateTime;

/**
 * Parses a date and time according to a format string.
 * @function
 * @param {String} format The format string. It has the same syntax as Java's
 * java.text.SimpleDateFormat, assuming a Gregorian calendar.
 * @param {String} string The string to parse.
 * @type Date
 * @return The parsed date as a Date object. It will contain a Time value as
 * defined in the HTTP API specification.
 */
var parseDateTime;

/**
 * An array with translated week day names.
 * @ignore
 */
var weekdays = [];

(function() {

    var regex = /(G+|y+|M+|w+|W+|D+|d+|F+|E+|a+|H+|k+|K+|h+|m+|s+|S+|z+|Z+)|\'(.+?)\'|(\'\')/g;

	function num(n, x) {
		var s = x.toString();
		n -= s.length;
		if (n <= 0) return s;
		var a = new Array(n);
		for (var i = 0; i < n; i++) a[i] = "0";
		a[n] = s;
		return a.join("");
	}
	function text(n, full, shrt) {
		return n >= 4 ? _(full) : _(shrt);
	}
	var months = [
		"January"/*i18n*/, "February"/*i18n*/,     "March"/*i18n*/,
		  "April"/*i18n*/,      "May"/*i18n*/,      "June"/*i18n*/,
		   "July"/*i18n*/,   "August"/*i18n*/, "September"/*i18n*/,
		"October"/*i18n*/, "November"/*i18n*/,  "December"/*i18n*/
	];
	var shortMonths = [
		"Jan"/*i18n*/, "Feb"/*i18n*/, "Mar"/*i18n*/, "Apr"/*i18n*/,
		"May"/*i18n*/, "Jun"/*i18n*/, "Jul"/*i18n*/, "Aug"/*i18n*/,
		"Sep"/*i18n*/, "Oct"/*i18n*/, "Nov"/*i18n*/, "Dec"/*i18n*/
	];
	var days = weekdays.untranslated = [
		   "Sunday"/*i18n*/,   "Monday"/*i18n*/, "Tuesday"/*i18n*/,
		"Wednesday"/*i18n*/, "Thursday"/*i18n*/,  "Friday"/*i18n*/,
		 "Saturday"/*i18n*/
	];
	var shortDays = [
		"Sun"/*i18n*/, "Mon"/*i18n*/, "Tue"/*i18n*/, "Wed"/*i18n*/,
		"Thu"/*i18n*/, "Fri"/*i18n*/, "Sat"/*i18n*/
	];
	var funs = {
		G: function(n, d) {
			return d.getTime() < -62135596800000 ? _("BC") : _("AD");
		},
		y: function(n, d) {
			var y = d.getUTCFullYear();
			if (y < 1) y = 1 - y;
			return num(n, n == 2 ? y % 100 : y);
		},
		M: function(n, d) {
			var m = d.getUTCMonth();
			if (n >= 3) {
				return text(n, months[m], shortMonths[m]);
			} else {
				return num(n, m + 1);
			}
		},
		w: function(n, d) { return num(n, getWeek(d)); },
		W: function(n, d) { return num(n, getWeek(d, true)); },
		D: function(n, d) {
			return num(n,
				getDays(d.getTime() - Date.UTC(d.getUTCFullYear(), 0)) + 1);
		},
		d: function(n, d) { return num(n, d.getUTCDate()); },
		F: function(n, d) {
			return num(n, Math.floor(d.getUTCDate() / 7) + 1);
		},
		E: function(n, d) {
			var m = d.getUTCDay();
			return text(n, days[m], shortDays[m]);
		},
		a: function(n, d) {
            return d.getUTCHours() < 12 ? _("AM") : _("PM");
        },
		H: function(n, d) { return num(n, d.getUTCHours()); },
		k: function(n, d) { return num(n, d.getUTCHours() || 24); },
		K: function(n, d) { return num(n, d.getUTCHours() % 12); },
		h: function(n, d) { return num(n, d.getUTCHours() % 12 || 12); },
		m: function(n, d) { return num(n, d.getUTCMinutes()); },
		s: function(n, d) { return num(n, d.getUTCSeconds()); },
		S: function(n, d) { return num(n, d.getMilliseconds()); },
        // TODO: z and Z 
		z: function() { return ""; },
		Z: function() { return ""; }
	};
	formatDateTime = function(format, date) {
		return format.replace(regex,
			function(match, fmt, text, quote) {
				if (fmt) {
					return funs[fmt.charAt(0)](fmt.length, date);
				} else if (text) {
					return text;
				} else if (quote) {
					return "'";
				}
			});
	};
    
    var f = "G+|y+|M+|w+|W+|D+|d+|F+|E+|a+|H+|k+|K+|h+|m+|s+|S+|z+|Z+";
    var pregexStr = "(" + f + ")(?!" + f + ")|(" + f + ")(?=" + f +
        ")|\'(.+?)\'|(\'\')|([$^\\\\.*+?()[\\]{}|])";
    var pregex = new RegExp(pregexStr, "g");
    
    var monthRegex;
    var monthMap;
    function recreateMaps() {
        var names = months.concat(shortMonths);
        for (var i = 0; i < names.length; i++) names[i] = escape(_(names[i]));
        monthRegex = "(" + names.join("|") + ")";
        monthMap = {};
        for (var i = 0; i < months.length; i++) {
            monthMap[_(months[i])] = i;
            monthMap[_(shortMonths[i])] = i;
        }
        weekdays.length = days.length;
        for (var i = 0; i < days.length; i++) weekdays[i] = _(days[i]);
    }
    recreateMaps();
    register("LanguageChangedInternal", recreateMaps);
    
    function escape(rex) {
        return rex.replace(/[$^\\.*+?()[\]{}|]/g, "\\$");
    }

    var numRex = "([+-]?\\d+)";
    function number(n) { return numRex; }
        
    var prexs = {
        G: function(n) {
            return "(" + escape(_("BC")) + "|" + escape(_("AD")) + ")";
        },
        y: number,
        M: function(n) { return n >= 3 ? monthRegex : numRex; },
        w: number, W: number, D: number, d: number, F: number, E: number,
        a: function(n) {
            return "(" + escape(_("AM")) + "|" + escape(_("PM")) + ")";
        },
        H: number, k: number, K: number, h: number, m: number, s: number,
        S: number
        // TODO: z and Z
    };
    
    function mnum(n) {
        return n > 1 ? "([+-]\\d{1," + (n - 1) + "}|\\d{1," + n + "})"
                     :                           "(\\d{1," + n + "})"; }
    
    var mrexs = {
        G: prexs.G, y: mnum,
        M: function(n) { return n >= 3 ? monthRegex : mnum(n); },
        w: mnum, W: mnum, D: mnum, d: mnum, F: mnum, E: prexs.E, a: prexs.a,
        H: mnum, k: mnum, K: mnum, h: mnum, m: mnum, s: mnum, S: mnum
        // TODO: z and Z
    };
    
    var pfuns = {
        G: function(n) { return function(s, d) { d.bc = s == _("BC"); }; },
        y: function(n) {
            return function(s, d) {
                d.century = n <= 2 && s.match(/^\d\d$/);
                d.y = s;
            };
        },
        M: function(n) {
            return n >= 3 ? function (s, d) { d.m = monthMap[s]; }
                          : function(s, d) { d.m = s - 1; };
        },
        w: emptyFunction, W: emptyFunction, D: emptyFunction,
        d: function(n) { return function(s, d) { d.d = s }; },
        F: emptyFunction, E: emptyFunction,
        a: function(n) { return function(s, d) { d.pm = s == _("PM"); }; },
        H: function(n) { return function(s, d) { d.h = s; }; },
        k: function(n) { return function(s, d) { d.h = s == 24 ? 0 : s; }; },
        K: function(n) { return function(s, d) { d.h2 = s; }; },
        h: function(n) { return function(s, d) { d.h2 = s == 12 ? 0 : s; }; },
        m: function(n) { return function(s, d) { d.min = s; }; },
        s: function(n) { return function(s, d) { d.s = s; }; },
        S: function(n) { return function(s, d) { d.ms = s; }; }
        // TODO: z and Z
    };
    
    var threshold = new Date();
    var century = Math.floor((threshold.getUTCFullYear() + 20) / 100) * 100;
    
    parseDateTime = function(formatMatch, string) {
        var handlers = [];
        var rex = formatMatch.replace(pregex,
            function(match, pfmt, mfmt, text, quote, escape) {
                if (pfmt) {
                    handlers.push(pfuns[pfmt.charAt(0)](pfmt.length));
                    return prexs[pfmt.charAt(0)](pfmt.length);
                } else if (mfmt) {
                    handlers.push(pfuns[mfmt.charAt(0)](mfmt.length));
                    return mrexs[mfmt.charAt(0)](mfmt.length);
                } else if (text) {
                    return text;
                } else if (quote) {
                    return "'";
                } else if (escape) {
                    return "\\" + escape;
                }
            });
        var match = string.match(new RegExp("^\\s*" + rex + "\\s*$", "i"));
        if (!match) return null;
        var d = { bc: false, century: false, pm: false,
            y: 1970, m: 0, d: 1, h: 0, h2: 0, min: 0, s: 0, ms: 0 };
        for (var i = 0; i < handlers.length; i++)
            handlers[i](match[i + 1], d);
        if (d.century) {
            d.y = Number(d.y) + century;
            var date = new Date(0);
            date.setUTCFullYear(d.y - 20, d.m, d.d);
            date.setUTCHours(d.h, d.min, d.s, d.ms);
            if (date.getTime() > threshold.getTime()) d.y -= 100;
        }
        if (d.bc) d.y = 1 - d.y;
        if (!d.h) d.h = Number(d.h2) + (d.pm ? 12 : 0);
        var date = new Date(0);
        date.setUTCFullYear(d.y, d.m, d.d);
        date.setUTCHours(d.h, d.min, d.s, d.ms);
        return date;
    };

})();

/**
 * Format UTC into human readable date and time formats
 * @function
 * @param {Date} date The date and time as a Date object.
 * @param {String} format A string which selects one of the following predefined
 * formats: <dl>
 * <dt>date</dt><dd>only the date</dd>
 * <dt>time</dt><dd>only the time</dd>
 * <dt>datetime</dt><dd>date and time</dd>
 * <dt>dateday</dt><dd>date with the day of week</dd>
 * <dt>hour</dt><dd>hour (big font) for timescales in calendar views</dd>
 * <dt>suffix</dt><dd>suffix (small font) for timescales in calendar views</dd>
 * <dt>onlyhour</dt><dd>2-digit hour for timescales in team views</dd></dl>
 * @type String
 * @return The formatted string
 * @ignore
 */
var formatDate;

/**
 * Parse human readable date and time formats
 * @function
 * @param {String} string The string to parse
 * @param {String} format A string which selects one of the following predefined
 * formats:<dl>
 * <dt>date</dt><dd>only the date</dd>
 * <dt>time</dt><dd>only the time</dd></dl>
 * @type Date
 * @return The parsed Date object or null in case of errors.
 * @ignore
 */
var parseDateString;

(function() {
    var formats;
    function updateFormats() {
        var date_def = configGetKey("gui.global.region.date.predefined") != 0;
        var time_def = configGetKey("gui.global.region.time.predefined") != 0;
        var date = date_def ? _("yyyy-MM-dd")
                            : configGetKey("gui.global.region.date.format");
        var time = time_def ? _("HH:mm")
                            : configGetKey("gui.global.region.time.format");
        var hour = configGetKey("gui.global.region.time.format_hour");
        var suffix = configGetKey("gui.global.region.time.format_suffix");
        formats = {
            date: date,
            time: time,
            //#. Short date format (month and day only)
            //#. MM is month, dd is day of the month
            shortdate: _("MM/dd"),
            //#. The relative position of date and time.
            //#. %1$s is the date
            //#. %2$s is the time
            //#, c-format
            datetime: format(pgettext("datetime", "%1$s %2$s"), date, time),
            //#. The date with the day of the week.
            //#. EEEE is the full day of the week,
            //#. EEE is the short day of the week,
            //#. %s is the date.
            //#, c-format
            dateday: format(_("EEEE, %s"), date),
            //#. The date with the day of the week.
            //#. EEEE is the full day of the week,
            //#. EEE is the short day of the week,
            //#. %s is the date.
            //#, c-format
            dateshortday: format(_("EEE, %s"), date),
            dateshortdayreverse: format(_("%s, EEE"), date),
            //#. The format for calendar timescales
            //#. when the interval is at least one hour.
            //#. H is 1-24, HH is 01-24, h is 1-12, hh is 01-12, a is AM/PM,
            //#. mm is minutes.
            hour: time_def ? pgettext("dayview", "HH:mm") : hour,
            //#. The format for hours on calendar timescales
            //#. when the interval is less than one hour.
            prefix: time_def ? pgettext("dayview", "HH") : suffix ? "hh" : "HH",
            //#. The format for minutes on calendar timescales
            //#. when the interval is less than one hour.
            //#. 12h formats should use AM/PM ("a").
            //#. 24h formats should use minutes ("mm").
            suffix: time_def ? pgettext("dayview", "mm") : suffix ? "a" : "mm",
            //#. The format for team view timescales
            //#. HH is 01-24, hh is 01-12, H is 1-24, h 1-12, a is AM/PM
            onlyhour: time_def ? pgettext("teamview", "H") : suffix ? "ha" : "H"
        };
    }
    register("LanguageChangedInternal", updateFormats);
    register("OX_Configuration_Changed", updateFormats);
    register("OX_Configuration_Loaded", updateFormats);
    
    formatDate = function(date, format) {
        return formatDateTime(formats[format], new Date(date));
    };    

    parseDateString = function(string, format) {
        return parseDateTime(formats[format || "date"].replace("yyyy","yy"), string);
    };

})();

function formatNumbers(value,format_language) {
	var val;
	if(!format_language) {
		format_language=configGetKey("language");
	}
	switch(format_language) {
		case "en_US":
			return value;
			break;
		default:
			val = String(value).replace(/\./,"\,");
			return val;
			break;
	}
}

function round(val) {
	val = formatNumbers(Math.round(parseFloat(String(val).replace(/\,/,"\.")) * 100) / 100);
	return val;
}

/**
 * Formats an interval as a string
 * @param {Number} t The interval in milliseconds
 * @param {Boolean} until Specifies whether the returned text should be in
 * objective case (if true) or in nominative case (if false).
 * @type String
 * @return The formatted interval.
 */
function getInterval(t, until) {
    function minutes(m) {
        return format(until
            //#. Reminder (objective case): in X minutes
            //#. %d is the number of minutes
            //#, c-format
            ? npgettext("in", "%d minute", "%d minutes", m)
            //#. General duration (nominative case): X minutes
            //#. %d is the number of minutes
            //#, c-format
            :  ngettext("%d minute", "%d minutes", m),
            m);
    }
    function get_h(h) {
        return format(until
            //#. Reminder (objective case): in X hours
            //#. %d is the number of hours
            //#, c-format
            ? npgettext("in", "%d hour", "%d hours", h)
            //#. General duration (nominative case): X hours
            //#. %d is the number of hours
            //#, c-format
            :  ngettext(      "%d hour", "%d hours", h),
            h);
    }
    function get_hm(h, m) {
        return format(until
            //#. Reminder (objective case): in X hours and Y minutes
            //#. %1$d is the number of hours
            //#. %2$s is the text for the remainder of the last hour
            //#, c-format
            ? npgettext("in", "%1$d hour and %2$s", "%1$d hours and %2$s", h)
            //#. General duration (nominative case): X hours and Y minutes
            //#. %1$d is the number of hours
            //#. %2$s is the text for the remainder of the last hour
            //#, c-format
            :  ngettext("%1$d hour and %2$s", "%1$d hours and %2$s", h),
            h, minutes(m));
    }
    function hours(t) {
        if (t < 60) return minutes(t); // min/h
        var h = Math.floor(t / 60);
        var m = t % 60;
        return m ? get_hm(h, m) : get_h(h);
    }
    function get_d(d) {
        return format(until
            //#. Reminder (objective case): in X days
            //#. %d is the number of days
            //#, c-format
            ? npgettext("in", "%d day", "%d days", d)
            //#. General duration (nominative case): X days
            //#. %d is the number of days
            //#, c-format
            : ngettext("%d day", "%d days", d),
            d);
    }
    function get_dhm(d, t) {
        return format(until
            //#. Reminder (objective case): in X days, Y hours and Z minutes
            //#. %1$d is the number of days
            //#. %2$s is the text for the remainder of the last day
            //#, c-format
            ? npgettext("in", "%1$d day, %2$s", "%1$d days, %2$s", d)
            //#. General duration (nominative case): X days, Y hours and Z minutes
            //#. %1$d is the number of days
            //#. %2$s is the text for the remainder of the last day
            //#, c-format
            : ngettext("%1$d day, %2$s", "%1$d days, %2$s", d),
            d, hours(t));
    }
    function days(t) {
        if (t < 1440) return hours(t); // min/day
        var d = Math.floor(t / 1440);
        t = t % 1440;
        return t ? get_dhm(d, t) : get_d(d); 
    }
    function get_w(w) {
        return format(until
            //#. Reminder (objective case): in X weeks
            //#. %d is the number of weeks
            //#, c-format
            ? npgettext("in", "%d week", "%d weeks", w)
            //#. General duration (nominative case): X weeks
            //#. %d is the number of weeks
            //#, c-format
            : ngettext("%d week", "%d weeks", w),
            w);
    }

    t = Math.round(t / 60000); // ms/min
	if (t >= 10080 && t % 10080 == 0) { // min/week
        return get_w(Math.round(t / 10080));
	} else {
        return days(t);
    }
}

var currencies = [
            { iso: "CAD", name: "Canadian dollar", isoLangCodes: [ "CA" ] },
            { iso: "CHF", name: "Swiss franc", isoLangCodes: [ "CH" ] },
            { iso: "DKK", name: "Danish krone", isoLangCodes: [ "DK" ] },
            { iso: "EUR", name: "Euro", isoLangCodes: [ "AT", "BE", "CY", "FI", "FR", "DE", "GR", "IE", "IT", "LU", "MT", "NL", "PT", "SI", "ES" ] },
            { iso: "GBP", name: "Pound sterling", isoLangCodes: [ "GB" ] },
            { iso: "PLN", name: "Zloty", isoLangCodes: [ "PL" ] },
            { iso: "RUB", name: "Russian rouble", isoLangCodes: [ "RU" ] },
            { iso: "SEK", name: "Swedish krona", isoLangCodes: [ "SE" ] },
            { iso: "USD", name: "US dollar", isoLangCodes: [ "US" ] }
        ];    
/**
 * 
 * All content on this website (including text, images, source
 * code and any other original works), unless otherwise noted,
 * is licensed under a Creative Commons License.
 * 
 * http://creativecommons.org/licenses/by-nc-sa/2.5/
 * 
 * Copyright (C) 2004-2010 Open-Xchange, Inc.
 * Mail: info@open-xchange.com 
 * 
 * @author Viktor Pracht <viktor.pracht@open-xchange.com>
 * 
 */

var ox = {};
var debug = false;  // IMPORTANT!: This var is for debugging and should never set to true.
var debugPlugins = false;
var debugJSON = false;
var bUWAEnabled = true;
var AjaxRoot = "/ajax";
var help_location = "[protocol]://[hostname][path]help/[language{1}]/";
var logout_location = "[protocol]://[hostname][path]";
var sessionExpired_location = "[protocol]://[hostname][path]";
var directLink_location = "[protocol]://[hostname][path]#m=[module]&f=[folder]&i=[object_id]";
//#. The UWA module description at the user options.
//#. The two %s indicate the start and end of a hyperlink.
//#, c-format
var uwaLink = { text: "Please have a look at the %sInteresting UWA modules%s page. It shows a list of widgets and their associated data." /*i18n*/, link: "http://www.open-xchange.com/index.php?id=361&L=[language{0}]" };

/**
 * Object which holds Product-Information
 * Will be changed by the build system, so please don't edit it directly!!!
 */
var oxProductInfo = { 
	id: 	 		"com.openexchange.ox.gui.dhtml", 
	version: 		"6.18.0.0", 
	revision: 		"${revision}",
	pversion:		"6.18.0 Rev20",
	build:	 		"v=MkGgV3L", 
	product_name: 	"Open-Xchange Server", 
	vendor_address:	"Open-Xchange AG\nMaxfeldstra&szlig;e 9\nD-90409 N&uuml;rnberg\nE-Mail: info@open-xchange.com" };

var oxThemeStyle = { name: "Default", path: "default" };

function isOldGecko() {
	if (navigator.appName == "Netscape" && navigator.userAgent.indexOf("rv:") >= 0) {
		var version = Number(navigator.userAgent.match(/rv:(\d+\.\d+)/)[1]);
		if (version <= 1.8)
			return true;
	}
	return false;
}
var oldGecko = isOldGecko(); 

////////////////////////////////
// BEGIN Temporarily entries
var preloadingnewwindows=true;
 
/**
 * @bShared boolean which is true if shared folder functionality is on
 * TODO: Should be replaced with server config parameter later
 */
 
var bShared = true;
var bPublic = true;

// END Temporarily entries 
////////////////////////////////
 
/**
 * @fileoverview Main JavaScript framework.
 */

  ///////////////////////////
 //   Automatic Refresh   //
///////////////////////////

function refreshWindow () {
	triggerEvent("OX_Refresh");
}

register("OX_Refresh", function() { storageCache.update(); });

var autorefresh_var;
register("OX_Configuration_Loaded_Complete",function () {
	if(configGetKey("gui.global.autorefresh") != 0) {
		autorefresh_var=window.setInterval(refreshWindow, (configGetKey("gui.global.autorefresh")*60000));	
	}
	if (!configContainsKey("gui.mail.auto_save_drafts")) {
		configSetKey("gui.mail.auto_save_drafts",3);
	}
});
register("OX_Configuration_Changed",function(param) {
	if(param == "configuration/settings") {
		if(autorefresh_var) {
			window.clearInterval(autorefresh_var);
		}
		if(configGetKey("gui.global.autorefresh") != 0) {
			autorefresh_var=window.setInterval(refreshWindow, (configGetKey("gui.global.autorefresh")*60000));	
		}
	}
});

  /////////////////////////////////
 //   Asynchronous processing   //
/////////////////////////////////

/**
 * Creates an object which calls the specified callback when multiple parallel
 * processes complete.
 * @param {Function} callback The callback which is called as a method of the
 * returned object when all parallel processes complete.
 */
function Join(callback) {
	//alert(callback.constructor.nativeCode);
	this.callback = callback;
	this.count = 0;
}

Join.prototype = {
	/**
	 * Adds a parallel process by specifying a callback function which is called
	 * when that process ends.
	 * @param {Function} callback A callback function which should be called
	 * when the process ends.
	 * @type Function
	 * @return A function which should be specified as callback instead of
	 * the function specified as parameter.
	 */
	add: function(callback) {
		this.count++;
		return this.alt(callback);
	},
	
	/**
	 * Adds an alternative callback function to an existing parallel process.
	 * @param {Function} callback A callback function which should be called
	 * when the process ends.
	 * @type Function
	 * @return A function which should be specified as callback instead of
	 * the function specified as parameter.
	 */
	alt: function(callback) {
		var Self = this;		
		return function() {
			var retval;
            if (callback) retval = callback.apply(this, arguments);
			if (!--Self.count) Self.callback();
            return retval;		
		};
	}
};

  ////////////////////
 //   DOM events   //
////////////////////

/**
 * Stops the processing of a DOM event
 * @param {Event} e The currently processed event.
 */
var stopEvent = (function() {

	function stopNormal(e) {
		e.preventDefault();
		e.stopPropagation();
	}
	
	function stopIE(e) {
		e.returnValue = false;
		e.cancelBubble = true;
	}
	
	return function(e) {
		stopEvent = e.preventDefault ? stopNormal : stopIE;
		stopEvent(e);
	};
})();

/**
 * Prevents the default handler of a DOM event from executing.
 * @param {Event} e The currently processed event.
 */
var cancelDefault = (function() {
	function cancelNormal(e) { e.preventDefault(); }
	function cancelIE(e) { e.returnValue = false; }
	return function(e) {
		cancelDefault = e.preventDefault ? cancelNormal : cancelIE;
		cancelDefault(e);
	};
})();

/**
 * Stops the bubbling of a DOM event.
 * @param {Event} e The currently processed event.
 */
var cancelBubbling = (function() {
	function cancelNormal(e) { e.stopPropagation(); }
	function cancelIE(e) { e.cancelBubble = true; }
	return function(e) {
		cancelBubbling = e.stopPropagation ? cancelNormal : cancelIE;
		cancelBubbling(e);
	};
})();

/**
 * @private
 */
var _IE_Events = {};

/**
 * Adds an event handler for a DOM event.
 * To work around memory leaks in Internet Explorer, this function decouples
 * the callback function from the hooked DOM object. For bookkeeping purposes,
 * the callback function gets a property named &quot;id&quot; with a unique
 * value.
 * @param {Object} node A DOM object which provides an event.
 * @param {String} event The name of the event. E. g. &quot;onclick&quot;.
 * @param {Function} callback The event handler. It is called with
 * the DOM Event as parameter.
 */
var addDOMEvent = (function() {

	var id = 0;
	
	function makeHandler(id, win) {
		var f = function() {
			var list = _IE_Events[id];
			var e = !win ? window.event : win;
			if (!e.currentTarget) e.currentTarget = this;
			for (var i = list.length - 1; i >= 0; i--)
				if (list[i](e) === false) return false;
			return true;
		};
		f.id = id;
		return f;
	}
	
	function addIE(node, event, callback, win) {
		event = "on" + event;
		var oldHandler = node[event];
		var list;
		if (debug && oldHandler && !oldHandler.id) {
			alert(format("Mixing addDOMEvent and DOM 0 events is not allowed!\nid=\"%s\" event=\"%s\"",
				node.id, event));	
		}
		if (oldHandler && oldHandler.id) {
			list = _IE_Events[oldHandler.id];
		} else {
			node[event] = makeHandler(++id, win);
			list = _IE_Events[id] = [];
		}
		list.push(callback);
	}
	
	function addNormal(node, event, callback) {
		node.addEventListener(event, callback, false);
	}
	
	return document.addEventListener ? addNormal : addIE;
	
})();

/**
 * Removes a previously added DOM event handler.
 * @param {Object} node A DOM object which provides an event.
 * @param {String} event The name of the event. E. g. &quot;onclick&quot;.
 * @param {Function} callback The previously added event handler.
 * @see addDOMEvent
 */
var removeDOMEvent = (function() {

	function removeIE(node, event, callback) {
		event = "on" + event;
		var handler = node[event];
		if (!handler || !handler.id)
			return;
		var list = _IE_Events[handler.id];
		for (var i in list) if (list[i] == callback) {
			list.splice(i, 1);
			if (!list.length) {
				delete _IE_Events[handler.id];
				node[event] = "";
			}
		}
	}
	
	function removeNormal(node, event, callback) {
		node.removeEventListener(event, callback, false);
	}
	
	return function(node, event, callback) {
		removeDOMEvent = node.addEventListener ? removeNormal: removeIE;
		removeDOMEvent(node, event, callback);
	};
})();

  //////////////
 //   JSON   //
//////////////

/**
 * @class A queue of JSON requests.
 * This class maintains a queue of asynchronous JSON events. The requests are
 * processed one after another. While one request is pending, the remaining
 * requests can be cancelled with {@link #cancel}.
 * @constructor
 */
function JSON() {
	/**
	 * @private
	 */
	this.first = null;
	
	/**
	 * @private
	 */
	this.last = null;
	
	/**
	 * @private
	 */
	this.processing = false;
}

JSON.serialize = function(data) {
	if (typeof(data) == "string")
		return "\"" + data.replace(/[\x00-\x1f\\"]/g, function(c) {
			var n = Number(c.charCodeAt(0)).toString(16);
			return "\\u00" + (n.length < 2 ? "0" + n : n);
		}) + "\"";
	if (typeof(data) == "function") return "function";
	if (!data || typeof(data) !== "object") return String(data);
	var strings = new Array(data.length);
	if (Object.prototype.toString.call(data) == "[object Array]") {
		for (var i in data) strings[i] = JSON.serialize(data[i]);
		return "[" + strings.join() + "]";
	}
	var j = 0;
	for (var i in data) strings[j++] = "\"" + i + "\":" + JSON.serialize(data[i]);
	return "{" + strings.join() + "}";
};

JSON.ignore404 = function(result, status) { return status == 404; };

JSON.count = 0;

JSON.prototype = {
	/**
	 * Asynchronously requests a JSON object from the server.
	 * This method retrieves a JSON object from the server by issuing an HTTP
	 * GET request to the specified URI and calling the specified callback when
	 * the retrieval is complete. If there is already another request from this
	 * queue object pending, the new request is put at the end of a queue to be
	 * executed after all previous requests have completed.
	 * @param {String} uri The URI for the HTTP GET request.
	 * @param {Function} cb A callback function which is called with the
	 * received JSON object or raw data as parameter. If there was any error
	 * then this function is not called.
	 * @param {Function} errorHandler An optional callback function wihch is
	 * called when the server returns an error. The function takes two
	 * parameters: result and status. If the HTTP status code was 200, then
	 * result is the JSON object and status is not set. If the HTTP status was
	 * not 200, then result is the status string and status is the HTTP status
	 * code. The function should return true when it handles the error.
	 * Otherwise, the default error handler specified by JSON.errorHandler will
	 * be called after this function returns. If this parameter is not specified,
	 * the default error handler is called directly.
	 * @param {Boolean} raw Specifies whether the response data should be
	 * passed to the callback as-is or first parsed as a JSON object. Defaults
	 * to the latter.
	 * @type Object
	 * @return An object which can be used to cancel the request with the
	 * {@link #cancel} method.
	 * @see #cancel
	 */
	get: function(uri, meta, cb, errorHandler, raw) {
		var request = {
			method: "GET",
			uri: uri,
			data: "",
			cb: cb,
			errorHandler: errorHandler,
			raw: raw,
			next: null
		};
        if (debug) request.stack = getStack(3, 2);
		this.add(request);
		return request;
	},
	
	/**
	 * Asynchronously posts url-encoded data and retrieves a JSON object from
	 * the server.
	 * This method posts an object to the server by issuing an HTTP
	 * POST request to the specified URI and calling the specified callback when
	 * the reply arrives. If there is already another request from this
	 * queue object pending, the new request is put at the end of a queue to be
	 * executed after all previous requests have completed.
	 * @param {String} uri The URI for the HTTP POST request.
	 * @param {Object} data An object which is serialized using the
	 * application/x-www-form-urlencoded encoding and sent as the body of the
	 * request.
	 * @param {Function} cb A callback function which is called with the
	 * received JSON object or raw data as parameter. If there was any error
	 * then this function is not called.
	 * @param {Function} errorHandler An optional callback function wihch is
	 * called when the server returns an error. The function takes two
	 * parameters: result and status. If the HTTP status code was 200, then
	 * result is the JSON object and status is not set. If the HTTP status was
	 * not 200, then result is the status string and status is the HTTP status
	 * code. The function should return true when it handles the error.
	 * Otherwise, the default error handler specified by JSON.errorHandler will
	 * be called after this function returns. If this parameter is not specified,
	 * the default error handler is called directly.
	 * @param {Boolean} raw Specifies whether the response data should be
	 * passed to the callback as-is or first parsed as a JSON object. Defaults
	 * to the latter.
	 * @see #get
	 * @see #cancel
	 */
	post: function(uri, data, meta, cb, errorHandler, raw) {
		var encoded = new Array(), n = 0;
		for (var i in data)
			encoded[n++] = i + "=" + encodeURIComponent(data[i]);
		var request = {
			method: "POST",
			uri: uri,
			data: encoded.join("&"),
			contenttype: "application/x-www-form-urlencoded",
			cb: cb,
			errorHandler: errorHandler,
			raw: raw,
			next: null
		};
        if (debug) request.stack = getStack(3, 2);
		this.add(request);
		return request;
	},
	
	/**
	 * Asynchronously sends a JSON object and retrieves a JSON object from
	 * the server.
	 * This method sends a JSON object to the server by issuing an HTTP
	 * PUT request to the specified URI and calling the specified callback when
	 * the reply arrives. If there is already another request from this
	 * queue object pending, the new request is put at the end of a queue to be
	 * executed after all previous requests have completed.
	 * @param {String} uri The URI for the HTTP POST request.
	 * @param {Object} data An object which is serialized using JSON syntax and
	 * sent as the body of the request.
	 * @param {Function} cb A callback function which is called with the
	 * received JSON object or raw data as parameter. If there was any error
	 * then this function is not called.
	 * @param {Function} errorHandler An optional callback function wihch is
	 * called when the server returns an error. The function takes two
	 * parameters: result and status. If the HTTP status code was 200, then
	 * result is the JSON object and status is not set. If the HTTP status was
	 * not 200, then result is the status string and status is the HTTP status
	 * code. The function should return true when it handles the error.
	 * Otherwise, the default error handler specified by JSON.errorHandler will
	 * be called after this function returns. If this parameter is not specified,
	 * the default error handler is called directly.
	 * @param {Boolean} raw Specifies whether the response data should be
	 * passed to the callback as-is or first parsed as a JSON object. Defaults
	 * to the latter.
	 * @see #get
	 * @see #cancel
	 */
	put: function(uri, data, meta, cb, errorHandler, raw) {
		var request = {
			method: "PUT",
			uri: uri,
			contenttype: "text/javascript; charset=UTF-8",
			data: JSON.serialize(data),
			cb: cb,
			errorHandler: errorHandler,
			raw: raw,
			next: null
		};
		if (debug) request.stack = getStack(3, 2);
		this.add(request);
		return request;
	},
	
	/**
	 * Cancels a previously enqueued request.
	 * If the request is already pending, its property
	 * <code>cancelled</code> is set to true, but the callbacks will be called
	 * anyway. Otherwise, the request is removed from the queue.
	 * @param {Object} request An object previously returned by one of
	 * {@link #get}, {@link #post} or {@link #put}.
	 * @type Boolean
	 * @return True if the request was already sent to the server.
	 */
	cancel: function(request) {
		if (request == this.first) {
			request.cancelled = true;
			return false;
		}
		for (var r = this.first; r; r = r.next)
			if (request == r.next) {
				r.next = request.next;
				return true;
			}
		return false;
	},

	/**
	 * @private
	 */
	remove: function() {
		if (this.first) {
			if (this.last == this.first) this.last = null;
			this.first = this.first.next;
		}
	},

	/**
	 * @private
	 */
	add: function(request) {
		if (!this.first)
			this.last = this.first = request;
		else
			this.last = this.last.next = request;
		if (!this.processing) this.process();
	},

	/**
	 * @private
	 */
	process: function() {
		JSON.count++;
		if (!(this.processing = this.first != null)) {
			if (!--JSON.count) triggerEvent("Loading", false);
			return;
		}
		var xmlhttp = this.getXmlHttp();
		var Self = this;
		if (debugJSON) {
            var debug_cb = JSON.debug(this.first.method + " " + this.first.uri,
                [this.first.data].concat(JSON.getHTMLStackTrace(2)),
                callback);
            xmlhttp.onreadystatechange = function() {
                if (xmlhttp.readyState == 4) debug_cb();
            };
        } else {
            xmlhttp.onreadystatechange = callback;
        }
		xmlhttp.open(this.first.method, this.first.uri, true);
		if (this.first.contenttype)
			xmlhttp.setRequestHeader("Content-Type", this.first.contenttype);
		xmlhttp.send(this.first.data);
		if (JSON.count == 1) triggerEvent("Loading", true);
		function callback() {
			if (xmlhttp.readyState != 4) return;
			JSON.count--;
			xmlhttp.onreadystatechange = emptyFunction; // fixes IE memory leak
			var cb = Self.first.cb;
			var originalErrorHandler = Self.first.errorHandler;
			var errorHandler = originalErrorHandler ? function(result, status) {
				if (!originalErrorHandler(result, status))
					JSON.errorHandler(result, status);
			} : JSON.errorHandler;
			var raw = Self.first.raw;
			if (debug) trace = Self.first.stack;
			Self.remove();
			var result = {};
			if (xmlhttp.status != 200) {
				errorHandler(xmlhttp.statusText, xmlhttp.status);
				if (debug) trace = null;
				Self.process();
				return;
			}
			if (raw)
				result = xmlhttp.responseText;
			else {
				var s = "return " + xmlhttp.responseText;
                try {
                    result = Function(s)();
                } catch (e) {
                    //#. %s is the JavaScript error message.
                    //#, c-format
                    alert(format(_("Syntax error in server reply:\n%s"), e.message, s));
                    if (debug) trace = null;
                    Self.process();
                    return;
                }
				if (result && typeof(result) == "object" && result.error) {
				    if (result.category == 13) {
				        newServerError(result);
				    } else {
    					errorHandler(result);
    	                if (debug) trace = null;
    					Self.process();
    					return;
				    }
				}
			}
            if (window.console && console.exception) {
                try {
                    cb(result);
                } catch (e) {
                    console.exception(e);
                }
                if (debug) trace = null;
                Self.process();
            } else {
                try {
                    cb(result);
                } finally {
                    if (debug) trace = null;
                    Self.process();
                }
    		}
		};
	},
	
	/**
	 * @private
	 */
	getXmlHttp: function() {
		alert(_("Your browser does not support AJAX."));
	}
};

JSON.errorHandler = function(result, status) {
	if (status)
		//#. HTTP Errors from the server
		//#. %1$s is the numeric HTTP status code
		//#. %2$s is the corresponding HTTP status text
	    //#, c-format
		alert(format(_("Error: %1$s - %2$s"), status, result));
	else
		alert(formatError(result));
};

(function() {
	var xmlhttp = null;
	try {
		xmlhttp = new XMLHttpRequest();
		if (xmlhttp) {
			xmlhttp = null;
			JSON.prototype.getXmlHttp = function() { return new XMLHttpRequest(); };
		}
	} catch (e) {
		try {
			xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
			if (xmlhttp) {
				xmlhttp = null;
				JSON.prototype.getXmlHttp = function() {
					return new ActiveXObject("Msxml2.XMLHTTP");
				};
			}
		} catch (e) {
			try {
				xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
				if (xmlhttp) {
					xmlhttp = null;
					JSON.prototype.getXmlHttp = function() {
						return new ActiveXObject("Microsoft.XMLHTTP");
					};
				}
			} catch (e) {
				JSON.prototype.getXmlHttp();
			}
		}
	}
    if (debugJSON) {
        var requests = {};
        var id = 0;
        var w = JSON.debugWindow = open("about:blank", "oxJSONDebugger");
        w.document.open();
        w.document.write(
            "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" " +
            "\"http://www.w3.org/TR/html4/strict.dtd\">" +
            "<html><head><title>JSON debugger</title></head>" +
            "<body><button type=\"button\" id=\"toggle\">Enable</button>" +
            "<button type=\"button\" id=\"record\">Record</button>" +
            "<div id=\"recorded\">&#xa0;</div>" +
            "<ul id=\"ul\"></ul></body></html>");
        w.document.close();
        JSON.debugDocument = w.document;
        var ul = w.document.getElementById("ul");
        var toggle = w.document.getElementById("toggle");
        var record = w.document.getElementById("record");
        var recorded = w.document.getElementById("recorded");
        toggle.addEventListener("click", function() {
            debugJSON = !debugJSON;
            record.disabled = debugJSON;
            toggle.firstChild.data = debugJSON ? "Disable" : "Enable";
        }, false);
        record.addEventListener("click", function() {
            if (debugJSON) {
                recorded.firstChild.data = JSON.getRecordedCombination();
                record.firstChild.data = "Record";
            } else {
                JSON.recordCombination();
                record.firstChild.data = "Stop";
            }
        }, false);
        debugJSON = false;
        JSON.debug = JSON.defaultDebug = function(caption, text, callback) {
            var plus = newnode("img", 0, { src: "themes/default/img/plus.gif" },
                0, w.document);
            for (var i = 0; i < text.length; i++) {
                if (typeof text[i] == "string") {
                    text[i] = newtext(text[i], w.document);
                }
            }
            var data = newnode("pre", { display: "none" }, 0, text, w.document);
            var finish = newnode("button", 0,
                { type: "button", disabled: true },
                [newtext("Finish", w.document)], w.document);
            var debugBtn = newnode("button", 0,
                { type: "button", disabled: true },
                [newtext("Debug", w.document)], w.document);
            var li = newnode("li", 0, 0,
                [plus, newtext(caption, w.document), finish, debugBtn, data],
                w.document);
            ul.appendChild(li);
            plus.addEventListener("click", function() {
                if (data.style.display == "none") {
                    plus.src = "themes/default/img/minus.gif";
                    data.style.display = "block";
                } else {
                    plus.src = "themes/default/img/plus.gif";
                    data.style.display = "none";
                }
            }, false);
            finish.addEventListener("click", function() {
                ul.removeChild(li);
                callback();
            }, false);
            debugBtn.addEventListener("click", function() {
                ul.removeChild(li);
                eval("debugger");
                callback();
            }, false);
            var f = function() {
                finish.disabled = false;
                debugBtn.disabled = false;
            };
            f.cancel = function() { ul.removeChild(li); };
            return f;
        };
        var oldSetTimeout = JSON.realSetTimeout = window.setTimeout;
        JSON.setDebugCombination = function(combination) {
            var c = this.debugCombination = combination ? combination.split(",")
                                                        : [];
            for (var i = 0; i < c.length; i++) c[i] = Number(c[i]);
            this.lastChoice = 0;
            var index = 0;
            var pending = [];
            var join = new Join(function() {
                while (!this.count && pending.length) {
                    var choice;
                    if (index >= c.length) {
                        choice = 0;
                    } else if (index == c.length - 1) {
                        choice = c[index] + 1;
                        
                    } else {
                        choice = c[index];
                    }
                    if (choice >= pending.length) {
                        throw new Error("Invalid debug choice " + choice +
                            " in [" + c + "] at " + index + ".");
                    }
                    c[index++] = choice;
                    if (choice < pending.length - 1) JSON.lastChoice = index;
                    var callback = pending[choice];
                    pending.splice(choice, 1);
                    callback();
                }
            });
            this.debug = function(caption, text, callback) {
                pending.push(callback);
                return join.add();
            };
            debugJSON = true;
            JSON.getDebugCombination = function() {
                debugJSON = false;
                return this.debugCombination.slice(0, this.lastChoice).join();
            };
        };
        JSON.recordCombination = function() {
            var recordedCombination = [], pending = [];
            var callID = 0;
            this.debug = function(caption, text, callback) {
                var id = callID++;
                pending.push({ id: id, caption: caption, callback: callback,
                    toString: function() {
                        return this.id + ": " + this.caption;
                    } });
                return function() {
                    console.log("pending", pending);
                    for (var i = 0; i < pending.length; i++) {
                        if (pending[i].id == id) {
                            var cb = pending.splice(i, 1)[0];
                            recordedCombination.push(i);
                            cb.callback();
                            break;
                        }
                    }
                };
            };
            this.getRecordedCombination = function() {
                debugJSON = false;
                this.debug = this.defaultDebug;
                return recordedCombination.join();
            };
            debugJSON = true;
        };
    }
})();

function testdebugging() {
    JSON.recordCombination();
    setTimeout(function() { console.log("a"); }, 200);
    setTimeout(function() { console.log("b"); }, 0);
    JSON.realSetTimeout.call(window, function() {
        console.log("combination", JSON.getRecordedCombination());
    }, 1000);
}

  //////////////////////
 //   Stack traces   //
//////////////////////

(function() {
    if (!debug) return;
    trace = null;
    getStack = function(start, end) {
        return { stack: (new Error).stack, next: trace,
                 start: start, end: end };
    };
    try { getStack(); } catch (e) { return; }
    function wrap(f, stack) {
        return function() {
            trace = stack;
            var retval = f.apply(this, arguments);
            trace = null;
            return retval;
        };
    }
    
    getStackTrace = function() {
        var retval = [];
        for (var s = getStack(3); s; s = s.next) {
            var stack = s.stack.split("\n");
            var end = stack.length - (s.next ? s.next.end : 0);
            for (var i = s.start; i < end; i++) {
                var entry = stack[i];
                if (/function/.test(entry)) {
                    entry = entry.replace(/^(\w*\().*(\)@\S+:\d+)$/, "$1...$2");
                }
                if (/@data:/.test(entry)) {
                    entry = entry.replace(/(@data:)\S+(:\d+)$/, "$1...$2");
                }
                retval.push(entry);
            }
            retval.push("---");
        }
        return { stack: retval.join("\n") };
    };
    
    var sources = {}, sourceID = 0;
    function showSource(file, line) {
        return function(e) {
            e.preventDefault();
            var w = sources[file];
            if (!w || w.closed) {
                var target = "oxJSONDebuggerSource" + sourceID++;
                w = sources[file] = open("about:blank", target);
                w.document.open();
                w.document.write(
                    "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" " +
                    "\"http://www.w3.org/TR/html4/strict.dtd\">" +
                    "<html><head><title>" +
                    file.replace(/^.*\/([^\/]+)$/, "$1") +
                    " - JSON debugger</title><style type=\"text/css\">" +
                    "pre {color:#666} a {color:black}" +
                    "</style></head><body><pre>");
                var xml = new XMLHttpRequest;
                xml.open("GET", file, false);
                xml.send();
                var lines = xml.responseText.split("\n");
                var lineNoLength = String(lines.length).length;
                for (var i = 1; i <= lines.length; i++) {
                    var lineNo = String(i);
                    while (lineNo.length < lineNoLength) lineNo = " " + lineNo;
                    w.document.write(lineNo + " <a id=\"" + i + "\">" +
                        escapeHTML(lines[i - 1]) + "</a>\n");
                }
                w.document.write("</pre></body></html>");
                w.document.close();
            }
            if (w.currentLine) w.currentLine.style.background = "";
            var l = w.currentLine = w.document.getElementById(line);
            l.style.background = "#ccf";
            w.document.documentElement.scrollTop = l.offsetTop -
                w.document.documentElement.clientHeight / 2;
            w.focus();
        };
    }
    
    JSON.getHTMLStackTrace = function(hide) {
        var doc = this.debugDocument;
        var retval = [];
        for (var s = getStack(hide + 4); s; s = s.next) {
            var stack = s.stack.split("\n");
            var end = stack.length - (s.next ? s.next.end : 0);
            for (var i = s.start; i < end; i++) {
                var entry = stack[i];
                if (/function/.test(entry)) {
                    entry = entry.replace(/^(\w*\().*(\)@\S+:\d+)$/, "$1...$2");
                }
                var match = /^((?:\w*\(.*\))?@)((\S+):(\d+))$/.exec(entry);
                if (match) {
                    var link = newnode("a", 0, {
                        href: match[3] + "#" + match[4],
                        target: "oxJSONDebuggerSource"
                    }, [newtext(match[2], doc)], doc);
                    link.addEventListener("click",
                        showSource(match[3], match[4]), false);
                    retval.push(newnode("div", 0, 0,
                        [newtext(match[1], doc), link], doc));
                } else {
                    retval.push(newnode("div", 0, 0, [newtext(entry, doc)],
                                        doc));
                }
            }
            retval.push(newtext("---", doc));
            hide = 0;
        }
        return retval;
    };
    
    var oldSetTimeout = window.setTimeout;
    window.setTimeout = function() {
        if (typeof arguments[0] == "function") {
            arguments[0] = wrap(arguments[0], getStack(3, debugJSON ? 3 : 2));
            if (debugJSON) {
                arguments[0] = JSON.debug("setTimeout " + arguments[1],
                    JSON.getHTMLStackTrace(0), arguments[0]);
            }
        }
        if (debugJSON && JSON.debugCombination) arguments[1] = 0;
        return oldSetTimeout.apply(this, arguments);
    };
    
    var oldSetInterval = window.setInterval;
    window.setInterval = function() {
        if (typeof arguments[0] == "function") {
            arguments[0] = wrap(arguments[0], getStack(3, 2));
        }
        return oldSetInterval.apply(this, arguments);
    };
    
})();

function traceVariable(object, name) {
    var variable = object[name];
    delete object[name];
    object.__defineGetter__(name, function() { return variable; });
    object.__defineSetter__(name, function(value) {
        console.log("variable trace", name, value, getStackTrace());
        variable = value;
    });
}

  //////////////
 //  Login   //
//////////////
var bClickedLogin = false;
/**
 * @private
 */
var myjoin = new Join(function () {
	loadMessage("Rebuild Tree...", /*i18n*/
			"50");
	triggerEvent("RebuildTree");
	initAll2(login);
});

var loginready=myjoin.add();
var htmlload = new Join(myjoin.add());				
var htmljsload = new Join(myjoin.add(function() {
	fillInitObject();
}));
//var externalJoin = new Join(myjoin.add());

var jsload = new Join(myjoin.add(function() {
	preloadJSFiles();
}));
var cssload = new Join(myjoin.add(function() {
	preloadCSSFiles();
}));
var cacheload = new Join(myjoin.add());
var fileloaded = function() {};
var wholeelement=new Object();
var rootebene=this;
var htmljsarray=new Array();
var jsarray=new Array();
var cssarray=new Array();

// build proper URLs (e.g. injection of version number)
function urlify (path) {
	return (path.substr(0,1) !== "/" && path.substr(0,4) !== "http" && corewindow == window && oxProductInfo.build !== null) ? oxProductInfo.build + "/" + path : path;
}

// Escape string for sizzle
function jEsc (str) {
    return str.replace(/([#;&,.+*~':"!\^$\[\]()=>|\/])/g, "\\$1");
};

// preload javascript files
function preloadJSFiles () {
	var i, code, node;
	for (i = 0; i < jsarray.length; i++) {
		code = wholeelement["js"][jsarray[i]];
		node = newnode("script", null, { type : "text/javascript", text: code });
		document.getElementsByTagName("head")[0].appendChild(node);
	}
}

//preload CSS files
function preloadCSSFiles () {
	var i, code, node;
	for(i = 0; i < cssarray.length; i++) {
		code = wholeelement["css"][cssarray[i]];
		node = newnode("style", null, { type : "text/css" });
		node.appendChild(document.createTextNode(code));
	}

}
var searchedsizeids=new Object();
function fillInitObject() {
	function searchSizeNode(id) {
		if(searchedsizeids[id]) {return searchedsizeids[id]; }
		var searchobjects=new Array();
		searchobjects.push(init.size);
		for(var zaehler=0;zaehler<searchobjects.length;zaehler++) {
			var myel=searchobjects[zaehler];
			myel.length;
			for (var i1=0;i1<myel.length;i1++) {
				if(myel[i1]) {
					if(myel[i1].id == id) {
						searchedsizeids[id]=myel[i1].children;
						return searchedsizeids[id];
					} else {
						if(myel[i1].children.length) {
							searchobjects.push(myel[i1].children);
						}
					}
				}
			}
		}
		return null;
	}
	for(var i3=0;i3<htmljsarray.length;i3++) {
		var node=wholeelement["htmljs"][htmljsarray[i3]];
		eval(node.node);
		if(!preload) { alert("Failure no preload var available"); }
		for(var i in preload) {
			switch (i) {
				case "size" :
					if(preload[i].length>0) {
						var element;
						if(node.parent) {
							//Hier children object
							element=searchSizeNode(node.parent);						
						} else {
							element=init.size;
						}
						if(!element) {
							alert("Error in Fill size: parent id -" +node.parent);
						}
						for(var i2 in preload[i]) {
							element.push(preload[i][i2]);
						}
					}
					break;
				default	:
					if (init[i] instanceof Array) {
						for(var i2 in preload[i]) {
							init[i].push(preload[i][i2]);
						}
					} else {
						for(var i2 in preload[i]) {
							init[i][i2]=preload[i][i2];
						}
					}
					break;
			}	
		}
		delete preload;
	}
}
function loadFile(page,cb,params,sizetreeid) {
    (new JSON).get(urlify(page), null, function(s) {
        cb(s, params, sizetreeid);
    }, null, true);
}

function loadCSSFileForNewWindow(url) {
	// create <link> element
	var node = newnode("link", null, { type : "text/css", rel: "stylesheet", href: urlify(url) });
	// add to head
	document.getElementsByTagName("head")[0].appendChild(node);
}

function removeCSSFile(url) {
	// remove stylesheet (<link> element)
	jQuery("link[rel=stylesheet][href=" + jEsc(urlify(url)) + "]").remove();
//    var d = body.parentNode.getElementsByTagName("link");
//    // loop over
//    for (var i=0; i < d.length; i++) {
//        if (d[i] && d[i].getAttribute("REL") && d[i].getAttribute("HREF") == page) {
//            d[i].parentNode.removeChild(d[i]);
//        }
//    }
}

function removeCSSFilesForNewWindow(path) {
    if (path != "default") {
        removeCSSFile("themes/default/css/global.css");
        removeCSSFile("themes/default/css/bgimages.css");
        loadCSSFileForNewWindow("themes/" + path + "/css/global.css");
        loadCSSFileForNewWindow("themes/" + path + "/css/bgimages.css");
    }
}

// boot
ox.boot = (function () {
	
	// locals
	var fileCache = {};
	var jsNodes = {};
	wholeelement.js = {};
	var loadingJS = {};
	var bundleParts = {
		"mainpage": { alias: "core", place: undefined },
		"mail-core": { alias: "mail_core", place: "contentarea" },
		"calendar-mini": { alias: "cal_mini", place: "below_folders" },
		"calendar-core": { alias: "cal_core", place: "contentarea" },
		"contacts-core": { alias: "cont_core", place: "contentarea" },
		"infostore-core": { alias: "info_core", place: "contentarea" },
		"mailcheck": { alias: "mail_check", place: "below_folders" },
		"tasks-core": { alias: "tasks_core", place: "contentarea" },
		"messaging": { alias: "messaging", place: "contentarea" }
	};

	return {
		
		// cache HTML file
		cacheHTML: function (opt) {
			// defaults
			var options = jQuery.extend({
				file: "",
				success: jQuery.noop,
				error: jQuery.noop
			}, opt);
			// in cache?
			if (fileCache[options.file] === undefined) {
				// cache miss
				jQuery.ajax({
					url: oxProductInfo.build + "/" + options.file,
					dataType: "xml", // allows preprocessing
					success: function (data) {
						// cache data
						var node = document.createElement("DIV");
						node.innerHTML = jQuery(data).text() || "";
						// get all nodes with an id at once
						var nodes = jQuery("*[id]", node), i = 0, $l = nodes.length, n, id;
						for (; i < $l; i++) {
							n = nodes[i];
							id = n.getAttribute("id") || ""; 
							if(allnodes[id] !== undefined) {
								alert("Internal Error: ID \"" + id + "\" used multiple times!");
								allnodes[id] = "Duplicate ID";
							} else {
								// node type?
								if (id.substr(id.length-11) == "scaffold-js") {
									// JS node
									jsNodes[id] = n;
								} else {
									// DOM node
									allnodes[id] = n;
								}
							}
						}
						// mark as loaded
						fileCache[options.file] = true;
						// callback
						options.success(data);
					},
					error: function (xhr) {
						if (debug) console.log("Error", options.file, options.id, xhr);
						options.error();
					}
				});
			}
		},
	
		loadHTML: function (opt) {
			// defaults
			var options = jQuery.extend({
				file: "",
				id: "",
				success: jQuery.noop,
				error: jQuery.noop
			}, opt);
			// in cache?
			if (fileCache[options.file] !== undefined) {
				// callback
				options.success(allnodes[options.id]);
				return;
			}
		},
	
		loadJS: function (opt) {
			// defaults
			var options = jQuery.extend({
				file: "",
				id: "",
				success: jQuery.noop,
				error: jQuery.noop,
				params: "",
				tree: undefined
			}, opt);
			// in cache?
			if (fileCache[options.file] !== undefined) {
				// get nodes
				var node = jsNodes[options.id];
				// init
				htmljsarray.push(options.params);
				// callback
				options.success(jQuery(node).text(), options.params, options.tree);
			}
		},
	
		loadJSFile: function (page) {
			if (loadingJS[page] === undefined) {
			    // debug?
			    if (debug) {
			        fileloaded = jsload.add();
			        var d = newnode("script", null, { type: "text/javascript", src: urlify(page) });    
			        document.getElementsByTagName("head")[0].appendChild(d);
			        loadingJS[page] = true;
			    } else {
    				// add to list
    				jsarray.push(page);
    				// callback
    				var callback = function (node, param) {
    					loadingJS[page] = true;
    					wholeelement.js[param] = node;
    				};
    				// load
    				loadFile(page, jsload.add(callback), page);
			    }
			}
		},
		
		// bundles
		bundles: {},
		getBundles: function () {
			var b = ox.boot.bundles;
			// just mail
			b.MAIL = configGetKey("modules.mail.module") &&
				configGetKey("modules.contacts.module");
			// PIM
			b.PIM = b.MAIL && 
				configGetKey("modules.portal.module") &&
				configGetKey("modules.calendar.module") &&
				configGetKey("modules.tasks.module");
			// Full groupware
			b.FULL = b.PIM && 
				configGetKey("modules.infostore.module");
			// All available modules
			b.ALL = b.FULL && 
				configGetKey("modules.messaging.module");
			return b;
		},
		
		loadBundle: function (opt) {
			var options = jQuery.extend({
				parts: [],
				name: "",
				success: jQuery.noop,
				htmlStore: jQuery.noop
			}, opt);
			// load JS
			this.loadJSFile(options.name + ".jsz");
			// load layout files
			var layoutFile = options.name + ".xmlz";
			var self = this;
			this.cacheHTML({ 
				file: layoutFile, 
				success: htmlload.add(function () {
					// progress
					loadMessage("Load Contents..." /*i18n*/, "30");
					// load JS
					var handler = function () {
						return htmljsload.add(options.htmlStore);
					};
					// loop over parts
					var p = options.parts, i = 0, $l = p.length, id, part;
					for (; i < $l; i++) {
						id = p[i];
						part = bundleParts[id];
						self.loadJS({ file: layoutFile, id: id + "-scaffold-js", params: part.alias, tree: part.place, success: handler() });
						self.loadHTML({ file: layoutFile, id: id + "-scaffold", success: htmlload.add(jQuery.noop) });
					}
					// callback
					options.success();
				})
			});
		}
	};
})();

function loadContents() {
	
	function storeHTMLFile(node) {
		function getIDs(obj) {
			function recursive(obj) {
				for(var i=obj.firstChild; i!=null; i=i.nextSibling) {
					if(i.nodeType==1) { 
						idValue=i.id;
						if(idValue) { 
							if(allnodes[i.id]) {
								alert("Internal Error: ID \"" + i.id + "\" used multiple times!");
								allnodes[i.id] = "Duplicate ID";
							} else {
								allnodes[i.id]=i; 
							}
						}
						recursive(i);
					}
				}
			}
			if(obj.id) { allnodes[obj.id]=obj; } 
			recursive(obj);
		}
		var el=document.createElement("div");
		el.innerHTML=node;	
		getIDs(el);
	}
	
	function storeHTMLJSFile(node,param,sizetreeid) {
		if(!wholeelement["htmljs"]) wholeelement["htmljs"]=new Object();
		wholeelement["htmljs"][param]= new Object();
		if(sizetreeid) { wholeelement["htmljs"][param].parent=sizetreeid; }
		wholeelement["htmljs"][param].node=node;
	}
	
	function storeJSFile(node,param) {
		if(!wholeelement["js"]) wholeelement["js"]=new Object();
		wholeelement["js"][param]=node;
	}
	
	function storeCSSFile(node,param) {
		if(!wholeelement["css"]) wholeelement["css"]=new Object();
		wholeelement["css"][param]=node;
	}

	function loadFileForCache(file) {
		//setTimeout(function() {
			loadFile(file,emptyFunction,file);
		//}, 2000);
	}
	
	function loadHTMLFile(page) {
		loadFile(page,htmlload.add(storeHTMLFile),null);
	}
	
	function loadHTMLJSFile(page,params,sizetreeid) {
		htmljsarray[htmljsarray.length]=params;
		loadFile(page,htmljsload.add(storeHTMLJSFile),params,sizetreeid);
	}
	
//	function loadJSFile(page) {
//		if(debug) { debugJSLoad(page); }  
//		else {
//			jsarray[jsarray.length]=page;
//			loadFile(page,jsload.add(storeJSFile),page);
//		}
//	}
	
	function loadCSSFile(page) {
		debugCSSLoad(page);
		return;
		if(debug) { debugCSSLoad(page); }  
		else {
			cssarray[cssarray.length]=page;
			loadFile(page,cssload.add(storeCSSFile),page);
		}
	}
	
	function debugJSLoad(page) {
		fileloaded=jsload.add();
		var d=newnode("script", null,
		    { type: "text/javascript", src: urlify(page) });	
		document.getElementsByTagName("head")[0].appendChild(d);
	}	
	
	function debugCSSLoad(url) {
		// exists?
		var links = jQuery("link[rel=stylesheet][href=" + jEsc(urlify(url)) + "]");
		if (links.length === 0) {
			// add CSS file
			var node = newnode("link", null, { type : "text/css", rel: "stylesheet", href: urlify(url) });	
			document.getElementsByTagName("head")[0].appendChild(node);
		}
	}
	
	loadMessage("Load Contents...", /*i18n*/
			"20");
	/* Prefill Joins */
	var tmparray = new Array();
	tmparray.push(htmlload.add());
	tmparray.push(htmljsload.add());
	tmparray.push(jsload.add());
	tmparray.push(htmlload.add());
	tmparray.push(cacheload.add());
	tmparray.push(cssload.add());
//	tmparray.push(externalJoin.add());

	register("OX_Configuration_Loaded", function() {
		if (configContainsKey("gui.theme") && configContainsKey("modules.themes." + configGetKey("gui.theme").path)) {
			oxThemeStyle = configGetKey("gui.theme");
		} else {
			configSetKey("gui.theme", oxThemeStyle);
		}
		if (oxThemeStyle.path == "default") {
			// load concatenated css
			loadCSSFile("themes/default/css/concat.cssz");
		} else {
			loadCSSFile("themes/"+oxThemeStyle.path+"/css/calendar.css");
			loadCSSFile("themes/"+oxThemeStyle.path+"/css/global.css");
			loadCSSFile("themes/"+oxThemeStyle.path+"/css/popup.css");
			loadCSSFile("themes/"+oxThemeStyle.path+"/css/bgimages.css");
		}
        // remove some of the default styleheets to prevent duplicate entries
	    if (oxThemeStyle.path != "default") {
            removeCSSFile("themes/default/css/global.css");
            removeCSSFile("themes/default/css/bgimages.css");
	    }
	});
	
	/* Which bundle? */
	var bundle = ox.boot.getBundles();
	
	var finalize = function () {
		if (!bundle.ALL) {
			/* Messaging */
		    if (configGetKey("modules.messaging.module")) {
		        loadHTMLFile("messaging.html");
		        loadHTMLJSFile("messaging.js", "messaging", "contentarea");
		        loadJSFile("concat_messaging.js");
		    }
		}
	    // go
		for (var i=0;i<tmparray.length;i++) {
			tmparray[i]();
		}
		// progress
		loadMessage("Load Contents..." /*i18n*/, "40");
	};
	
	// refs
	var loadJS = ox.boot.loadJS;
	var loadJSFile = ox.boot.loadJSFile;
	var loadHTML = ox.boot.loadHTML;
	var cacheHTML = ox.boot.cacheHTML;
	
    if (bundle.FULL || bundle.ALL) {
		// load bundle
    	var name = "bundle_" + (bundle.ALL ? "all" : "full");
    	var parts = ["mainpage", "mail-core", "calendar-mini", "calendar-core", "contacts-core", "infostore-core", "mailcheck", "tasks-core"];
    	if (bundle.ALL) {
    		parts.push("messaging");
    	}
		// load bundle
    	ox.boot.loadBundle({
    		name: name,
    		parts: parts,
    		htmlStore: storeHTMLJSFile,
    		success: finalize
    	});
	}
    else if (bundle.PIM) {
    	// load bundle
    	var name = "bundle_pim";
    	var parts = ["mainpage", "mail-core", "calendar-mini", "calendar-core", "contacts-core", "mailcheck", "tasks-core"];
		// load bundle
    	ox.boot.loadBundle({
    		name: name,
    		parts: parts,
    		htmlStore: storeHTMLJSFile,
    		success: finalize
    	});
    } else if (bundle.MAIL) {
    	// load bundle
    	var name = "bundle_mail";
    	var parts = ["mainpage", "mail-core", "calendar-mini", "contacts-core", "mailcheck"];
		// load bundle
    	ox.boot.loadBundle({
    		name: name,
    		parts: parts,
    		htmlStore: storeHTMLJSFile,
    		success: finalize
    	});
    }
	else {
		
		/*Load always needed contents */
		loadHTMLFile("mainpage.html");
		loadHTMLJSFile("mainpage.js","core");
		loadJSFile("concat_core.jsz");
		
	    /* Messaging */
	    if (configGetKey("modules.messaging.module")) {
	        loadHTMLFile("messaging.html");
	        loadHTMLJSFile("messaging.js", "messaging", "contentarea");
	        loadJSFile("concat_messaging.js");
	    }
	    
		/* Load portal contents */
		if(configGetKey("modules.portal.module")) {
			loadJSFile("concat_portal.js");				
		}
	
		/*Load mail contents*/
		if(configGetKey("modules.mail.module")) {
			loadHTMLFile("mail_core.html");
			loadHTMLJSFile("mail_core.js","mail_core","contentarea");
			loadJSFile("concat_mail.js");
			if(configGetKey("fastgui.preload.mail")) {
				loadFileForCache("concat_mailnew.jsz");
				loadFileForCache("concat_maildetail.jsz");
				loadFileForCache("newMail.html");
				loadFileForCache("3rdparty/tinymce/jscripts/tiny_mce/tiny_mce.js");
				loadFileForCache("3rdparty/tinymce/jscripts/tiny_mce/themes/advanced/editor_template.js");
			}
		}
		/* Load mini CalendarContents */
		// ===
		loadJSFile("concat_minicalendar.js");
		// ===
		loadHTMLFile("calendarmini.html");
		loadHTMLJSFile("calendarmini.js","cal_mini","below_folders");	 	
	
		/*Load calendar contents*/
		if(configGetKey("modules.calendar.module")) {
			loadHTMLFile("calendar_core.html");
			loadHTMLJSFile("calendar_core.js","cal_core","contentarea");
			loadJSFile("concat_calendar.jsz");
			if(configGetKey("fastgui.preload.calendar")) {
				loadFileForCache("concat_calendarnew.jsz");
				loadFileForCache("newappointment.css");
				loadFileForCache("newAppointment.html");
			}
		}
		if(configGetKey("modules.contacts.module")) {
			loadHTMLFile("contacts_core.html");
			loadHTMLJSFile("contacts_core.js","cont_core","contentarea");
			loadJSFile("concat_contacts.js");
			if(configGetKey("fastgui.preload.contacts")) {
				loadFileForCache("concat_contactsnew.jsz");
				loadFileForCache("newcontact.css");
				loadFileForCache("newContact.html");
			}
			if(configGetKey("fastgui.preload.distributionlist")) {
				loadFileForCache("concat_distributionnew.jsz");
				loadFileForCache("newDistributionList.html");
				loadFileForCache("newcontact.css");
			}
	
		}
		if(configGetKey("modules.infostore.module")) {
			loadHTMLFile("infostore_core.html");
			loadHTMLJSFile("infostore_core.js","info_core","contentarea");
			loadJSFile("concat_infostore.js");	
			if(configGetKey("fastgui.preload.infostore")) {
				loadFileForCache("concat_infostorenew.jsz");
				loadFileForCache("newInfoItemMain.html");
				loadFileForCache("newtask.css");
			}
	
		}
		if(configGetKey("modules.mail.module") ||
		   configGetKey("modules.infostore.module") || 
	   	   configGetKey("modules.calendar.module") || 
	   	   configGetKey("modules.contacts.module") || 
	   	   configGetKey("modules.tasks.module")) {
			loadHTMLFile("mailcheck.html");
			loadHTMLJSFile("mailcheck.js","mail_check","below_folders");
			loadJSFile("quickinfo.js");	
		}
	
		if (configGetKey("modules.tasks.module")) {
			loadHTMLFile("tasks_core.html");
			loadHTMLJSFile("tasks_core.js","tasks_core","contentarea");
			loadJSFile("tasks.js");
			if (configGetKey("fastgui.preload.tasks")) {
				loadFileForCache("concat_tasksnew.js");
				loadFileForCache("newTask.html");
				loadFileForCache("newtask.css");
			}
		}
		
		/* Portal Classes */
		loadJSFile("concat_sidepanel.js");
		loadJSFile("concat_config.jsz"); // <==
		
		for (var i=0;i<tmparray.length;i++) {
			tmparray[i]();
		}
		loadFileForCache("concat_all.jsz");
	}
}

function dologin(result) {
	session = result.session;
	loginready();
	var tmpJoin=new Join(loadContents);
	(new JSON()).get(AjaxRoot + "/config/modules?session=" + session, null,tmpJoin.add(function(obj) {
		if(!config) { config = new Object(); }
		if (obj) {
			config.modules = obj.data;
		}
	}));	
	/*(new JSON()).get(AjaxRoot + "/config/fastgui?session=" + session, null,tmpJoin.add(function(obj) {
		if(!config) { config = new Object(); }
		if (obj) {
			config.fastgui = obj.data;
		}
	}));*/	
	if(!config) { config = new Object(); }
	config.fastgui=new Object();
	config.fastgui.preload=new Object();
	config.fastgui.preload.mail=true;
	config.fastgui.preload.calendar=true;
	config.fastgui.preload.tasks=true;
	config.fastgui.preload.contacts=true;
	config.fastgui.preload.distributionlist=true;
	config.fastgui.preload.infostore=true;
}  

function uuid() {
    function hex(len, x) {
        if (x === undefined) x = Math.random();
        var s = new Array(len);
        for (var i = 0; i < len; i++) {
            x *= 16;
            var digit = x & 15;
            s[i] = digit + (digit < 10 ? 48 : 87); // '0' and 'a' - 10
        }
        return String.fromCharCode.apply(String, s);
    }
    return [hex(8), "-", hex(4), "-4", hex(3), "-",
            hex(4, 0.5 + Math.random() / 4), "-", hex(12)].join("");
}

function storeSession(session) {
    (new JSON).get(AjaxRoot + "/login?action=store&session=" +
     session, null, emptyFunction);
}


function login() {

	/*avoid double login*/
	if(bClickedLogin)
		return;
	bClickedLogin=true;
	/*avoid double login end*/	

	// hide form
	loadMessage("Login" /*i18n*/ + "...", "10");
	jQuery("#loading_data_complete").hide();
	jQuery("#loading_data").show();
	
	var commitLogin = function () {
		var form = document.getElementById("login");	
    	new JSON().post(AjaxRoot +"/login?action=login&client=" +
        	encodeURIComponent(oxProductInfo.id || "") + "&version=" +
        	encodeURIComponent(oxProductInfo.pversion || "") + "&authId=" + uuid(),
			{ name: form.username.value, password: form.password.value },
			null,
			function(result) {
			    dologin(result);
			   
			    //var rememberme = document.getElementById("rememberme");
        	    var rememberme = jQuery("#loading_data_complete input:radio:checked").val();
        	    
        	    //if (!rememberme.disabled && rememberme.checked) {
       	        if(rememberme === "true"){
        	    	storeSession(session);
     	        }
	  	    },
			function(result, status) {
				triggerEvent("OX_Login_Failed");
				jQuery("#loading_data_complete").show();
				jQuery("#loading_data").hide();
				if (!status) {
					if (result.code == "LGI-0006")
						alert(_("Login failed. Please check your user name and password and try again."));
					else
						alert(formatError(result));
				} else {
	                //#. HTTP Errors from the server
	                //#. %1$s is the numeric HTTP status code
	                //#. %2$s is the corresponding HTTP status text
				    //#, c-format
					alert(format(_("Error: %1$s - %2$s"), status, result));
				}
				bClickedLogin = false;
				// refocus form
				jQuery("#username").focus();
				return true;
			},
			null
		);
	};
	
	setTimeout(commitLogin, 10);
	
	return false;
}

function loggedIn() {
	//session = session_id;
	register("OX_Configuration_Loaded_Complete", function() {
		// change cached img path according to theme location
		changeImgPath(oxThemeStyle.path);
		appendNode("everything");		
		register("Logout", logout);
		/*
         * remove login screen from DOM because we don't need it anymore
         */
        $("body").removeChild($("loginScreen"));
        loadingComplete(true);
        
        setTimeout(function() {
        	// it's a direct link
        	if (location.hash.match(/[#&]m=([^#&]+)/) != null && location.hash.match(/[#&]f=([^#&]+)/) != null) {
        		return;
        	}
        		
			var module = currentpath2.join("/");
			var view = currentpath2.join("/");
			if (configGetKey("gui.global.landing_page") != null) {
				module = configGetKey("gui.global.landing_page").module;
				view = configGetKey("gui." + configGetKey("gui.global.landing_page").module + ".view") || module;
			}	
			// getting default module folder
			switch (module) {
			    case "mail":
			    	// we have to wait for system folder to continue
                    function loadFolder() {
                        if (isEmpty(oMainFolderTree.cache.fast_access)) {
                            setTimeout(loadFolder, 250);
                        } else if (configGetKey("modules.folder.tree") == 1) {
                            loadedFolder();
                        } else {
                            oMainFolderTree.cache.do_load_subfolders("1",
                                oMainFolderTree.cache.find_folder("1"), {},
                                loadedFolder);
                        }
                    }
                    function loadedFolder() {
                        var ids = [];
                        mail_accounts.newIterate(mail_accounts.ids,
                            emptyFunction, addAccount, findAccount);
                        function addAccount(index, data) { ids.push(data.id); }
                        function findAccount() {
                            ids.sort();
                            var separator =
                                configGetKey("modules.mail.defaulteparator") ||
                                "/";
                            for (var i = 0; i < ids.length; i++) {
                                var id = "default" + ids[i];
                                var fid = id + separator + "INBOX";
                                if (   oMainFolderTree.cache.fast_access[id]
                                    || oMainFolderTree.cache.fast_access[fid])
                                {
                                    setActiveFolder(fid, null, true);
                                    triggerEvent("OX_Switch_View", view);
                                    break;
                                }
                            }
                        }
                    }
			    	loadFolder();
				 	break;
				 case "configuration": 
				     activefolder = configGetKey("folder.configuration");
				     triggerEvent("OX_Switch_View", view);
				     break;
				 case "portal":
				     activefolder = undefined;
				     triggerEvent("OX_Switch_View", view);
				     break;
				 default:
				     setActiveFolder(configGetKey("folder")[module], null, true);
				 	 triggerEvent("OX_Switch_View", view);
	        }
        }, 0);
	});
	
	triggerEvent("OX_Login");
}

/**
 * Returns the img path, required for themes when adding runtime images
 * @param String The image location, anything behind the theme location
 * (e.g. themes/default/)
 */
function getFullImgSrc(img) {
	return urlify("themes/" + (corewindow.oxThemeStyle.path || "default") + "/" + img);
}

/**
 * Popup windows call this to disable popup error messages.
 * @param Boolean main True if the function is called by the main window.
 * False or omitted for popup windows.
 */
function loadingComplete(main) {
	var loggingOut = false;
	JSON.errorHandler = function(result, status) {
		if(typeof savePending != "undefined" && savePending) savePending = false;
		if (status) {
			triggerEvent("OX_New_Error", 2,
                //#. HTTP Errors from the server
                //#. %1$s is the numeric HTTP status code
                //#. %2$s is the corresponding HTTP status text
			    //#, c-format
                format(_("Error: %1$s - %2$s"), status, result));
		} else if (result.code.match(/^SES-02..$/)) {
			if (!loggingOut) {
				if (main) {
					loggingOut = true;
					newAlert(_("Session has expired"), _("Your session has expired. Please log in again."), 
					   function() {
						   	window.onbeforeunload = null;
	                        setTimeout( function() { window.location.replace(sessionExpired_location.format()); },0);	   	
					   });

				} else
				    newAlert(_("Session has expired"), _("Your session has expired. You have to close this window."));
			}
		} else {
			newServerError(result,4,window.opener);
		}
	};
}
  ///////////////
 //  Logout   //
///////////////

/**
 * @private
 */
function logout() {
	switch (configGetKey("gui.global.save") || 1) {
		case 0:
			LogoutPopup.commitno();
			break;
		case 2:
			configSetKey("gui.global.save", 1);
		case 1:
			LogoutPopup.commityes();
			break;			
	}
}

  ///////////////////////
 //   Configuration   //
///////////////////////

register("OX_Login", function() {
	(new JSON()).get(AjaxRoot + "/config/?session=" + session, null, function(obj) {
		if (obj) {
			config = obj.data;
		} else {
			config = {};
		}
		triggerEvent('OX_Configuration_Load_Foldertree');
		triggerEvent('OX_Configuration_Loaded');
		triggerEvent('OX_Configuration_Loaded_Complete');
		triggerSingleton("Ready");
	});	
});


register("OX_Configuration_Loaded_Complete", function() {
	internalCache.getUsers([configGetKey("identifier")],
	function(cbObj){
		$("loggedinUser").firstChild.data =
		    cbObj[configGetKey("identifier")].display_name;
	});
	
	// if set by hash parameter, activate embedded mode
	// @todo: this is a quick&dirty hack. instead we should allow plug-ins to
	// set such parameters earlier!
	if (url.embedded && new Boolean(url.embedded) == true) {	
		corewindow.embedd = true;
		$("logout_button").style.display = "none";
	}
});

  //////////////////////
 //   Current time   //
//////////////////////

/**
 * @type Number
 * @return the current time in milliseconds since 1970-01-01 00:00,
 * in the timezone of the logged in user.
 */
var now = function() {
    now = function() { return (new Date()).getTime() + now.offset; };
    now.offset = corewindow.now.offset;
    return now();
};

register("OX_Configuration_Loaded", function() {
    now();
    now.offset = configGetKey("currentTime") - (new Date()).getTime();
});

  ///////////////////////////
 //   Secondary windows   //
///////////////////////////

/**
 * Opens a new window with a specified URI.
 * Windows must have a deterministic title for automated tests to find them.
 * this functions automatically assigns them.
 * @param {String} uri The uri to open in the window.
 * @param {String} options The optional third parameter to window.open().
 * @type Object
 * @param Object a reference to a window object (optional)
 * @return The new window object.
 */
var newWindow = (function() {
	var popupBlockerTitle = "Error"; /*i18n*/
	var popupBlockerText = "The window could not be opened. Most likely it has been blocked by a pop-up or advertisement blocker. Please check your browser settings and make sure pop-ups are allowed for this domain."; /*i18n*/
	var windows = {};
	setInterval(function() {
		for (var i in windows) {
		    if (windows[i] && windows[i].closed) delete windows[i];
		}
	}, 10000);
	var counter = 1;
	function serialize(id) {
		var parts = [];
		for (var i in id) parts.push(i + "=" + id[i]);
		return parts.sort().join("&");
	}
	return function newWindow(uri, options, id, nwin) {
		var nwin = nwin || window;
        // absolute or relative path?
        uri = urlify(uri);
		if (id) {
			var sid = serialize(id);
			var win = windows[sid];
			if (win && win.closed) {
				delete windows[sid];
				win = null;
			}
			if (win) {
				setTimeout(function() { win.focus(); }, 0);
			} else {
                win = windows[sid] = nwin.open(uri, "OX" + counter++, options);
			}
			if (!win) {
			    delete windows[sid];
                newAlert(_(popupBlockerTitle), _(popupBlockerText));
            }	
		} else {
            win = nwin.open(uri, "OX" + counter++, options);
			if (!win) {
				newAlert(_(popupBlockerTitle), _(popupBlockerText));
			}
		}
		return win;
	};
})();

  /////////////////////////////
 //   Module Registration   //
/////////////////////////////

/**
 * Name of the currently displayed module.
 */
var activemodule = "portal";

var modules = new Array();
var moduleshash=new Object();
var modulesview =[];

/**
 * A map from module names to callback functions. Each callback function
 * is called with a folder object as parameter and returns an object with
 * the fields "open" and "closed", which contain the paths to 16px*16px icons
 * for the open and closed folder, respectively.
 */
var customFolderIcons = {};

var addModuleIcon = emptyFunction; // see sidepanel/sidepanel.js

function registerModule(name, text, priority) {
	addModuleIcon({ name: name, text: text, prio: priority });
	if(!moduleshash[name]) {
		var obj={name: name, text: text, disable: false, priority: priority};
		modules.push(obj);
		modules.sort(function(a,b) { return a.priority-b.priority; });
		moduleshash[name]=obj;		
	} else {
		for(var i=0;i<modules.length;i++) {
			if(modules[i].name == name) {
				var obj={name: name, text: text, disable: false, priority: priority};
				modules[i]=obj;
				moduleshash[name]=obj;				
			}
		}
	}
}
function registerModuleStatic(name, text,priority) {
	addModuleIcon({ name: name, text: text, prio: priority, disable: true });
	if(!moduleshash[name]) {
		var obj={name: name, text: text, disable: true, priority: priority};
		modules.push(obj);
		modules.sort(function(a,b) { return a.priority-b.priority; });
		moduleshash[name]=obj;		
	} else {
		for(var i=0;i<modules.length;i++) {
			if(modules[i].name == name) {
				var obj={name: name, text: text, disable: true, priority: priority};
				modules[i]=obj;
				moduleshash[name]=obj;				
			}
		}
	}	
}
function registerModuleView(name, text, limit,adj) {
	modulesview.push({name: name,text: text, limit: limit,adj:adj});
}

var views = {name: "root", level: -1, children: {}};

var currentview = views;
var currentfullpath;
var currentpath2 = [];
var currentpath = [];

function registerView(name, show, enter, leave, hide, change) {
	var names = name.split("/");
	var view = views;
	for (var i = 0; i < names.length; i++) {
		var viewname = names[i];
		var nextview = view.children[viewname];
		if (!nextview)
			nextview = view.children[viewname] =
				{name: view.name + "/" + names[i], parent: view, level: i, children: {}};
		view = nextview;
	}
	if (!view.show) view.show=new Array();
	if (!view.enter) view.enter=new Array();
	if (!view.change) view.change=new Array();		
	if (!view.leave) view.leave=new Array();
	if (!view.hide) view.hide=new Array();
	view.show.push(show);
	view.enter.push(enter);
	view.change.push(change);
	view.leave.push(leave);
	view.hide.push(hide);
	return view;
}

function changeView(name,param) {
	var names = name.split("/");
	currentpath2 = name.split("/");
	for (var i = 0; i < names.length && i < currentpath.length && names[i] == currentpath[i]; i++);
	var kview = currentview;
	for (var j = currentview.level; j >= i; j--) {
		if(currentview.leave) {
			for (var n=0;n<currentview.leave.length;n++) {
				if (currentview.leave[n]) {
					currentview.leave[n](param);
				}
			}
		}
		currentview = currentview.parent;
	}
	for (var k = kview.level; k >= i; k--) {
		if(kview.hide) {
			for (var n=0;n<kview.hide.length;n++) {
				if (kview.hide[n]) {
					kview.hide[n](param);
				}
			}
		} 
		kview = kview.parent;
	}
	activemodule=names[0];
	currentfullpath = name;
	var tmpview=views;
	var issame=true;
	for (var z=0 ;z <= k ; z++) {
		tmpview = tmpview.children[currentpath[z]];
		if(tmpview.change)
		{
			for (var n=0;n<tmpview.change.length;n++) {
				if (tmpview.change[n]) {
					tmpview.change[n](param);
				} 
			}
		}
	}		
	
	for (k++; k < names.length; k++) {
		kview = kview.children[names[k]];
		if (!kview)	{
			
			while(names.length>k) {
				names.pop();
			}
			break;
		}
		else
		{
			if(kview.show) {
				for (var n=0;n<kview.show.length;n++) {
					if (kview.show[n]) {
						kview.show[n](param);
					} 
				}
			}
		}
	}
	resizeHandler();
	for (j++; j < names.length; j++) {
		currentview = currentview.children[names[j]];
		if(currentview) {
			oldcurrentview=currentview;
			if(currentview.enter) {
				for (var n=0;n<currentview.enter.length;n++) {
					if (currentview.enter[n]) {
						currentview.enter[n](param);
					} 
				}
			}
		}
	}
	
	currentpath = names;
	
	// trigger event
	triggerEvent("OX_View_Changed", {
	    module: activemodule,
	    path: currentpath
	});
	
	return currentview;
}

  /////////////////////
 //   Linked list   //
/////////////////////

/**
 * Creates a linked list.
 * @class Double-linked list.
 * Not all methods of a typical linked list are implemented. The links are
 * stored in the properties <code>next</code> and <code>prev</code> of each
 * item.
 */
function LinkedList() {
    /**
     * The first item. Null if the list is empty.
     */
    this.first = null;

    /**
     * The last item. Null if the list is empty.
     */
    this.last = null;
}

LinkedList.prototype = {
    /**
     * Adds an item to the end of the list.
     * @param {Object} item The item to add. Two properties named
     * <code>prev</code> and <code>next</code> will be added to the item.
     * @type Boolean
     * @return True if the list was empty before the call.
     */
    addLast: function(item) {
        item.next = null;
        item.prev = this.last;
        if (this.last)
            this.last.next = item;
        else
            this.first = item;
        this.last = item;
        return this.first === item;
    },
    
    /**
     * Removes and returns the first item from the list.
     * @return The removed item or null if the list was already empty.
     */
    removeFirst: function() {
        var item = this.first;
        if (item) {
            this.first = item.next;
            if (this.first)
                this.first.prev = null;
            else
                this.last = null;
        }
        return item;
    },
    
    /**
     * Removes and returns the last item from the list.
     * @return The removed item or null if the list was already empty.
     */
    removeLast: function() {
        var item = this.last;
        if (item) {
            this.last = item.prev;
            if (this.last)
                this.last.next = null;
            else
                this.first = null;
        }
        return item;
    },
    
    /**
     * Removes an arbitrary item from the list.
     * @param {Object} item The item to be removed from the list.
     */
    remove: function(item) {
        if (item.next) item.next.prev = item.prev;
        if (item.prev) item.prev.next = item.next;
        if (item === this.first) this.first = item.next;
        if (item === this.last) this.last = item.prev;
    },
    
    /**
     * Cuts off all nodes previous to the specified node.
     * Specifying null clears the list.
     * @param {Object} newfirst The new first node of the list.
     */
    setFirst: function(newfirst) {
        if (newfirst) newfirst.prev = null; else this.last = null;
        this.first = newfirst;
    },
    
    /**
     * Inserts a node before another node.
     * @param {Object} newitem The item to be inserted.
     * @param {Object} nextitem The item before which the new item should be
     * inserted.
     */
    insertBefore: function(newitem, nextitem) {
        if (nextitem === this.first) {
            this.first = newitem;
            newitem.prev = null;
        } else {
            nextitem.prev.next = newitem;
            newitem.prev = nextitem.prev;
        }
        newitem.next = nextitem;
        nextitem.prev = newitem;
    }
};

  ///////////////////////////////////////
 //   Global mouse cursor functions   //
///////////////////////////////////////

/**
 * @function Sets the mouse cursor for the entire aplication. Must be followed
 * by a call to {@link #removeMouseCursor}.
 * @param {String} cursor The CSS cursor value to set.
 * @type GlobalCursor
 * @return An object which must be passed to {@link #removeMouseCursor} to revert
 * the cursor change.
 */
var setMouseCursor;

/**
 * @function Removes a mouse cursor which was previously set with
 * {@link #setMouseCursor}.
 * @param {GlobalCursor} item the return value of a previous call to
 * {@link #setMouseCursor}.
 */
var removeMouseCursor;

(function() {
    var cursorIndex = -1;
	var cursors = new LinkedList();
	cursors.addLast({cursor: ""});

    var setCursorCSS = emptyFunction;
    register("Loaded", function() {
        var stylesheet = document.styleSheets[0];
        var rules = stylesheet.cssRules || stylesheet.rules;
        var insertRule = stylesheet.insertRule ?
            function(cursor, index) {
                stylesheet.insertRule("* { cursor: " + cursor
                    + " !important; }", cursorIndex);
            } : function(cursor, index) {
                stylesheet.addRule("*", "cursor: " + cursor + " !important");
            };
        var deleteRule = stylesheet.deleteRule  
            ? function(index) { stylesheet.deleteRule(index); }
            : function(index) { stylesheet.removeRule(index); };

        setCursorCSS = function(cursor) {
            if (cursorIndex >= 0) {
                deleteRule(cursorIndex);
                cursorIndex = -1;
            }
            if (cursor) {
                cursorIndex = rules.length;
                insertRule(cursor, cursorIndex);
            }
        };
    });
    
	setMouseCursor = function(cursor) {
		var item = {cursor: cursor};
		cursors.addLast(item);
        setCursorCSS(cursor);
		return item;
	};

	removeMouseCursor = function(item) {
		if (item === cursors.last) {
			cursors.remove(item);
            setCursorCSS(cursors.last.cursor);
		} else
			cursors.remove(item);
	};
})();

  /////////////////////////////////////////
 //   "Loading" Icon and mouse cursor  //
/////////////////////////////////////////

register("Loading", (function() {
	var cursor = null;
	return function(loading) {
		var img = $("loading");
		if(img){
			img.src = getFullImgSrc(loading ? "img/toolbar/ox_animated.gif" : "img/toolbar/tb_loading.gif");
			img.alt = loading ? _("Loading...") : _("No activity");
			img.title = img.alt;
		}
		if (!document.all) { // Changing the cursor triggers mouseup in IE
			if (loading) {
				if (!cursor) cursor = setMouseCursor("wait");
			} else if (cursor) {
				removeMouseCursor(cursor);
				cursor = null;
			}
		}
	};
})());

  /////////////////////////////////
 //   Most-Recently-Used list   //
/////////////////////////////////

/**
 * Creates an MRU list.
 * @class Most-recently-used list (MRU list).
 * An MRU list limits the number of stored items by discarding
 * the least-recently referenced item, when storing a new item would otherwise
 * exceed the limit.
 * @param {Int} size The maximum size of the list.
 */
function MRUList(size) {
    if (!size) return new SimpleStorage();
    this.free = size;
    this.cache = {};
    this.list = new LinkedList();
}

MRUList.prototype = {
    /**
     * Retrieves an item from the list.loadconfig();
     * @param key The key of the item to retrieve.
     * @return The retrieved item or <code>undefined</code> if the item was not
     * found.
     */
    get: function(key) {
        var item = this.cache[key];
        if (!item) return undefined;
        this.list.remove(item);
        this.list.addLast(item);
        return item.data;
    },

    /**
     * Stores an item in the list.
     * @param key The key under which to store the item.
     * @param value The item to store in the list.
     * @return A removed item if cache is full or <code>undefined</code>
     * found.
     */
    set: function(key, value) {
        var item = this.cache[key];
        if (item)
            item.data = value;
        else {
            item = {key: key, data: value};
            this.list.addLast(item);
            this.cache[key] = item;
            if (this.free) {
                this.free--;
            } else {
                var key1=this.list.removeFirst().key;
                var ret=this.cache[key1].data;
                delete this.cache[key1];
                return ret;
            } 
        }
    },

    /**
     * Removes an item from the list.
     * @param key The key of the item to remove.
     */
    remove: function(key) {
        var item = this.cache[key];
        if (!item) return;
        this.list.remove(item);
        delete this.cache[key];
        this.free++;
    }
};

  /////////////////
 //   Storage   //
/////////////////

function SimpleStorage() {
	this.data = {};
}

SimpleStorage.prototype = {
	get: function(key) { return this.data[key]; },
	set: function(key, value) { this.data[key] = value; },
	remove: function(key) { delete this.data[key]; }
};

/**
 * @param {Number} timestamp The timestamp of the current storage content.
 * @param {Array} ids An array with object IDs of all items.
 * @param {String} uri The URI from which items will be fetched on-demand.
 * @param {Int} prefetch Number of additional items to fetch before the first
 * and after the last explicitly requested item. Defaults to 0.
 * @param {Int} cacheSize Maximum number of items to cache. Unlimited
 * if zero or not specified.
 * @param {Function} serialize Optional function to convert object IDs to
 * strings which can be used as keys in hash tables. Defaults to identity.
 * @param {Function} makeID Optional function to convert an item to
 * an object ID. Defaults to returning the first array entry.
 * @param {Object} extraData Optional object containing extra data indexed by
 * object ID. Extra data handling is disabled if this parameter is not specified.
 * @param {Function} updateItem Optional function to update a dynamically
 * retrieved item with extra data. It takes an extra data item and an item as
 * parameters and returns the updated item, which may be the same instance as
 * the second parameter. Must be specified whenever extraData is specified.
 */
function Storage(timestamp, ids, uri, prefetch, cacheSize, serialize, makeID, extraData, updateItem) {
	/**
	 * Timestamp of the storage content.
	 */
	this.timestamp = timestamp;
	
	/**
	 * Array with object IDs.
	 */
	this.ids = ids;
	
	/**
	 * A mapping from serialized object IDs to their indices in the storage.
	 */
	this.indices = {};
	
	/**
	 * URI used to fetch data.
	 */
	this.uri = uri;
	
	/**
	 * @private
	 */
	this.prefetch = prefetch;

	/**
	 * @private
	 */
	this.cacheSize = cacheSize;
	
	/**
	 * @private
	 */
	this.serialize = serialize || identity;
	
	/**
	 * @private
	 */
	this.makeID = makeID || function(x) { return x[0]; };
	
	/**
	 * @private
	 */
	this.extraData = extraData;

	/**
	 * @private
	 */
	this.updateItem = updateItem;
	
	/**
	 * A callback function to call instead of the built-in update in
	 * {@link StorageCache.update}.
	 */
	this.serverUpdates = null;

	/**
	 * @private
	 */
	this.data = new MRUList(cacheSize);

	/**
	 * Events triggered by this storage.
	 * <dl><dt>Changed</dt><dd>The contents were changed using one of
	 * the methods of this object.<br>Parameters:
	 * <ul><li>from - Lower inclusive limit of the changed range.</li>
	 * <li>to - Upper exlcusive limit of the changed range.</li></ul></dd></dl>
	 */
	this.events = new Events();

	/**
	 * @private
	 */
	this.from = 0x7fffffff;

	/**
	 * @private
	 */
	this.to = 0;

	/**
	 * @private
	 */
	this.json = new JSON();
	
	this.requests = new LinkedList();
	
	this.processing = false;
	
	this.uid = Storage.uid++;

	for (var i = 0; i < ids.length; i++)
		this.indices[this.serialize(ids[i])] = i;
}

Storage.uid = 0;

Storage.prototype = {
	/**
	 * Iterates over items of the storage.
	 * Data is fetched from the server on demand.
	 * @param {Array} ids An array with object IDs of iterated items.
	 * @param {Function} clear A callback which is called with the index of
	 * a missing item if the data for that item needs to be fetched from
	 * the server.
	 * @param {Function} set A callback function which is called with the index
	 * and the data of each item specified in <code>ids</code>.
	 * @param {Function} final_cb A callback function which is called after
	 * the iteration completes.
	 * @param {Int} prefetch Number of items to speculatively fetch before
	 * the first and after the last explicitly requested item. Defaults to 0.
	 * @type Object
	 * @return An object which can be used to cancel the pending request, or
	 * <code>undefined</code> if the request could be processed synchronously.
	 */
	newIterate: function(ids, clear, set, final_cb, prefetch) {
		var newids = {};
		var min = Infinity, max = -1;
		for (var i = 0; i < ids.length; i++) {
			var id = this.serialize(ids[i]);
			var ix = this.indices[id];
			min = Math.min(min, ix);
			max = Math.max(max, ix);
			var d = this.data.get(id);
			if (d)
				set(ix, d);
			else {
				newids[id] = ix;
				clear(ix);
			}
		}
		if (isEmpty(newids)) {
			if (final_cb) final_cb();
			return;
		}
		for (var i = Math.max(0, min - prefetch); i < min; i++) {
			var id = this.serialize(this.ids[i]);
			var d = this.data.get(id);
			if (!d) newids[id] = i;
		}
		var last = Math.min(this.ids.length - 1, max + prefetch);
		for (var i = max + 1; i <= last; i++) {
			var id = this.serialize(this.ids[i]);
			var d = this.data.get(id);
			if (!d) newids[id] = i;
		}
		var newrequest = {ids: newids, set: set, final_cb: final_cb};
		if (this.requests.addLast(newrequest) && !this.processing)
			this.process();
		return newrequest;
	},
	
	/**
	 * @private
	 */
	process: function() {
		var idset = {};
		for (var r = this.requests.first; r; r = r.next)
			for (var id in r.ids)
				if (!(id in idset)) idset[id] = this.ids[r.ids[id]];
		var oldrequests = this.requests;
		this.requests = new LinkedList();
		this.processing = true;
		var ids = [];
		for (var id in idset) ids.push(idset[id]);
		var Self = this;
		this.json.put(this.uri, ids, null, function(obj) {
			var items = obj.data;
			var id, item;
			var makeIdItem = Self.extraData ? function(i) {
				id = Self.serialize(Self.makeID(items[i]));
				item = Self.updateItem(Self.extraData[id], items[i]);
			} : function (i) {
				item = items[i];
				id = Self.serialize(Self.makeID(item));
			};
			for (var i in items) {
				makeIdItem(i);
				Self.data.set(id, item);
				for (var r = oldrequests.first; r; r = r.next)
					if (id in r.ids) {
						r.set(r.ids[id], item);
						delete(r.ids[id]);
					}
				for (var r = Self.requests.first; r; r = r.next)
					if (id in r.ids) {
						r.set(r.ids[id], item);
						delete(r.ids[id]);
					}
			}
			for (var r = oldrequests.first; r; r = r.next)
				if (r.final_cb) r.final_cb();
			for (var r = Self.requests.first; r; r = r.next) {
				if (isEmpty(r.ids)) {
					if (r.final_cb) r.final_cb();
					Self.requests.remove(r);
				}
			}
			if (Self.requests.first)
				Self.process();
			else
				Self.processing = false;
		});
	},
	purge : function (removeids) { 
		for (var i= 0; i< removeids.length; i++) {
			var serid=this.serialize(removeids[i]);
			this.data.remove(serid);
		}
	},
	/**
	 * Cancels a previously started iteration request.
	 * @param {Object} request An iteration request previously returned by
	 * a call to {@link #newIterate}.
	 */
	cancel: function(request) { this.requests.remove(request); },

	/**
	 * Updates a set of entries. Only entries in the local cache are updated.
	 * @param {Array} indices An array with object IDs of items to update.
	 * @param {Function} callback A function called with the index and
	 * the current data element as parameters and which should return
	 * the updated data element.
 	 */
	localUpdate: function(ids, callback) {
		var Self = this;
		var min = Infinity;
		var max = -1;
		for (var i in ids) {
			var id = this.serialize(ids[i]);
			var index = this.indices[id];
			var data = this.data.get(id);
			if (!data) continue;
			var newdata = callback(index, data);
			if (!newdata) continue;
			this.data.set(id, newdata);
			min = Math.min(min, index);
			max = Math.max(max, index);
		}
		if (min <= max) this.postChanged(min, max + 1);
	},
	
	/**
	 * @param {Number} timestamp The timestamp of the updated data.
	 * @param {Array} ids New array of object IDs. Each array element is itself
	 * an array with the columns required to represent the object ID.
	 * @param {Array} updates Array with updated items. In each item, the field
	 * or element specified by {@link #idField} must contain the object ID of
	 * the item.
	 * @param {Object} extraData Optional updated extra data. If not specified,
	 * the extra data is not modified. Extra data handling is not applied to
	 * the content of updates.
	 */
	update: function(timestamp, ids, updates, extraData) {
		this.timestamp = timestamp;
		if (extraData) this.extraData = extraData;
		var modified = {}, data = new MRUList(this.cacheSize);
		for (var i in updates)
			modified[this.serialize(this.makeID(updates[i]))] = updates[i];
		var maxLength = Math.max(this.ids.length, ids.length);
		this.ids.length = ids.length;
		this.indices = {};
		for (var i = 0; i < ids.length; i++) {
			var id = this.serialize(this.ids[i] = this.makeID(ids[i]));
			var item = modified[id] || this.data.get(id);
			this.indices[id] = i;
			if (item) data.set(id, item);
		}
		this.data = data;
		this.postChanged(0, maxLength);
	},
	
	/**
	 * Appends items at the end of the list.
	 * @param {Array} items An array with the items to append.
	 * @param {Object} extraData An optional object with additional extra data.
	 */
	append: function(items, extraData) {
		var from = this.ids.length;
		var len = items.length;
		for (var i in items) {
			var id = this.makeID(items[i]);
			var sid = this.serialize(id);
			this.indices[sid] = this.ids.length;
			this.ids.push(id);
			this.data.set(sid, items[i]);
		}
		if (extraData)
			for (var i in extraData) this.extraData[i] = extraData[i];
		this.postChanged(from, this.ids.length);
	},
	
	/**
	 * Removes a range of items from the list.
	 * @param from Lower inclusive limit of the range.
	 * @param to Upper exclusive limit of the range. Defaults to from + 1.
	 */
	remove: function(from, to) {
		if (to == undefined) to = from + 1;
		for (var i = from; i < to; i++) {
			var id = this.serialize(this.ids[i]);
			this.data.remove(id);
			delete this.indices[id];
			if (this.extraData) delete this.extraData[id];
		}
		var oldlen = this.ids.length;
		this.ids.splice(from, to - from);
		for (var i = to; i < this.ids.length; i++)
			this.indices[this.serialize(this.ids[i])] = i;
		this.postChanged(from, oldlen);
	},
	
	/**
	 * Removes a set of IDs and the corresponding items from the storage.
	 * @param {Array} ids An array with object ID of items to be removed.
	 */
	removeIDs: function(ids) {
		var indices = new Array(ids.length);
		for (var i = 0, j = 0; i < ids.length; i++) {
			var ix = this.indices[this.serialize(ids[i])];
			if (ix !== undefined) indices[j++] = ix;
		}
		indices.length = j;
		this.removeIndices(indices);
	},
	
	/**
	 * Removes a set of items specified by an array of indices.
	 * The array is modified!
	 * @param {Array} indices An array with indices of items to be removed.
	 */
	removeIndices: function(indices) {
		if (!indices.length) return;
		indices.sort(function(a, b) { return a - b; });
		indices.push(Infinity);
		var n = 0, next = indices[0], dest = next;
		this.postChanged(next, this.ids.length);
		for (var src = dest; src < this.ids.length; src++) {
			if (src < next) {
				var id = this.ids[src];
				this.ids[dest] = id;
				this.indices[this.serialize(id)] = dest++;
			} else {
				var id = this.serialize(this.ids[src]);
				this.data.remove(id);
				delete this.indices[id];
				if (this.extraData) delete this.extraData[id];
				next = indices[++n];
			}
		}
		this.ids.length = dest;
	},
	
	getIndex: function(id) { return this.indices[this.serialize(id)]; },
	
	getSID: function(index) {
		var id = this.ids[index];
		if (id) return this.serialize(id);
	},
	
	/**
	 * @private
	 */
	postChanged: function(from, to) {
		this.from = Math.min(this.from, from);
		this.to = Math.max(this.to, to);
		var Self = this;
		this.events.post("Changed",
			function() {
				var from = Self.from;
				Self.from = 0x7fffffff;
				return from;
			},
			function() {
				var to = Self.to;
				Self.to = 0;
				return to;
			}
		);
	}
};

  ///////////////////
 //   Selection   //
///////////////////

/**
 * Selection as a set of object IDs.
 */
function Selection() {
    /**
     * Number of selected items.
     */
    this.count = 0;

    /**
     * A map from serialized object IDs to object IDs.
     * @private
     */
    this.data = {};
    
    /**
     * Index of the selection anchor.
     * The anchor is used for range selects with the Shift key.
     * @private
     */
    this.anchor = 0;
    
    /**
     * Events triggered by this Selection.
     * <dl><dt>Selected</dt><dd>The selection has changed. Parameters:
     * <ul><li>Number of selected elements.</li></ul></dd></dl>
     */
    this.events = new Events();
    
    var Self = this;
    this.changed_cb = function() {
        var oldcount = Self.count;
        for (var id in Self.data) {
            if (!(id in Self.storage.indices)) {
                delete Self.data[id];
                Self.count--;
            }
        }
        if (Self.count != oldcount)
            Self.events.post("Selected", Self.count);
    };
}

Selection.prototype = {
    /**
     * Returns the selection status of a single item.
     * @param {Int} index The index of the queried item.
     * @return Boolean
     */
    get: function(index) {
        if (!this.storage) console.error("Selection.get without storage");
//      return this.serialize(this.storage.ids[index]) in this.data;
        if (index in this.storage.ids)
            return this.serialize(this.storage.ids[index]) in this.data;
        return false;
    },
    
    /**
     * Returns the selection status of a single item. returns false if ths index
     * is out of range.
     * @param {Int} index The index of the queried item.
     * @return Boolean
     */
    get2: function(index) {
        if (!this.storage) console.error("Selection.get without storage");
        var id = this.storage.ids[index];
        if (!id) return false;
        return this.serialize(id) in this.data;
    },
    
    /**
     * Toggles the selection status of a single item.
     * @param {Int} index The index of the toggled item.
     * @return the new selection status of the toggled item.
     * @private
     */
    toggle: function(index) {
        var id = this.storage.ids[index];
        var sid = this.serialize(id);
        var oldval = sid in this.data;
        if (oldval) delete this.data[sid]; else this.data[sid] = id;
        this.count += oldval ? -1 : 1;
        this.events.post("Selected", this.count);
        return !oldval;
    },
    
    /**
     * Deselects the specified serialized object IDs.
     * @param {Object} sids An object with serialized object IDs to deselect
     * as keys.
     */
    deselectSIDs: function(sids) {
        for (var sid in sids) {
            this.count -= (sid in this.data) ? 1 : 0;
            delete this.data[sid];
        }
        this.events.post("Selected", this.count);
    },

    /**
     * Clears the entire selection.
     * @private
     */
    reset: function() {
        this.data = {};
        this.count = 0;
        this.events.post("Selected", this.count);
    },
    
    /**
     * Selects multiple items.
     * @param {Int} from Lower inclusive limit of the selected range.
     * @param {Int} to Upper exclusive limit of the selected range.
     * @private
     */
    select: function(from, to) {
        for (var i = from; i < to; i++) {
            var id = this.storage.ids[i];
            var sid = this.serialize(id);
            if (!(sid in this.data)) {
                this.count++;
                this.data[sid] = id;
            }
        }
        this.events.post("Selected", this.count);
    },

    /**
     * Returns an array with object IDs of selected items.
     * @type Array
     * @return an array with object IDs of selected items.
     */
    getSelected: function() {
        var ids = [];
        for (var id in this.data) ids.push(this.data[id]);
        return ids;
    },
    
    /**
     * Handles a mouse click.
     * @param {Number} index Index of the clicked item.
     * @param {Event} e An optional event object which specifies which modifier
     * keys were held down at the time of the click.
     */
    click: function(index, e) {
        if (!this.storage) console.error("Selection.click without storage");
        if (index < 0 || index >= this.storage.ids.length) {
            this.reset();
            return;
        }
        if (!e || !(Mac ? e.metaKey : e.ctrlKey)) this.reset();
        if (e && e.shiftKey)
            this.select(Math.min(this.anchor, index),
                Math.max(this.anchor, index) + 1);
        else {
            this.toggle(index);
            this.anchor = index;
        }
    },
    
    /**
     * Sets the storage which is used to convert indices to object IDs.
     * @param {Storage} storage The storage, or null.
     */
    setStorage: function(storage) {
        if (this.storage)
            this.storage.events.unregister("Changed", this.changed_cb);
        this.storage = storage;
        if (storage) {
            this.serialize = storage.serialize;
            storage.events.register("Changed", this.changed_cb);
            this.changed_cb();
//          this.click(0);
        } else {
            this.serialize = null;
//          this.reset();
        }
    },
    
    /**
     * @deprecated
     */
    getID: function() {
        for (var i in this.data) return this.data[i];
    }
};



   ///////////////////////
 //   Storage cache   //
///////////////////////

/**
 * A cache of Storage containers.
 * TODO
 */
function StorageCache(maxCount) {
	/**
	 * @private
	 */
	this.cache = new MRUList(maxCount);
	
	/**
	 * @private
	 */
	this.json = new JSON();
	
	/**
	 * @private
	 */
	this.storage = null;
	
	/**
	 * The current storage.
	 * The last retrieved storage becomes the current storage.
	 * If it was retrieved using {@link #get} and not {@link #setCurrent} then
	 * it can be updated by calling {@link #update}.
	 */
	this.current = null;
}

// TODO: read from user 
StorageCache.prefetch = 20;
StorageCache.cacheSize = 1000;

(function() {
	function makeURI(uri, params) {
		var paramArray = [];
		for (var i in params) paramArray.push(i);
		paramArray.sort();
		var uriArray = [uri, "?"];
		for (var i = 0; i < paramArray.length; i++) {
			uriArray.push(paramArray[i] + "=" +
				encodeURIComponent(params[paramArray[i]]));
			uriArray.push("&");
		}
		uriArray.pop();
		return uriArray.join("");
	}

	StorageCache.prototype = {
		/**
		 * Asynchronously switch to a new storage.
		 * @param {String} uri the base URI of the storage,
		 * e.&nbsp;g. &quot;/ajax/tasks&quot;.
		 * @param {Object} params URI parameters as fields of an object.
		 * <code>params.session</code> is added automatically.
		 * <code>params.columns</code> must contain a comma-separated list of
		 * column(s) required for the object ID and defaults to &quot;1,20&quot;.
		 * @param {Function} callback A function which is called with the new
		 * storage as parameter.
		 * @param {String} columns A comma-separated list of additional columns to
		 * store after the ones specified in <code>params.columns</code>.
		 * Defaults to no additional columns.
		 * @param {Boolean} cached Specifies whether this object should be cached
		 * and/or retrieved from the cache. Defaults to <code>true</code>.
		 * @param {Function} serialize A function for converting complex object IDs
		 * (e.&nbsp;g. for the calendar) to strings. Defaults to concatenating
		 * the first two array elements with a dot.
		 * @param {Function} makeID A function for converting items to
		 * object IDs. Defaults to returning the first two array elements as
		 * an object.
		 * @param {Object} putBody An object which is serialized and used in
		 * the body of an HTTP PUT request. If not specified, an HTTP GET
		 * request is performed instead.
		 * @param {Function} makeExtra A function for converting object IDs from
		 * the initial request to extra data items as required by the updateItem
		 * function. Extra data handling is disabled if this parameter is
		 * not specified.
		 * @param {Function} updateItem A function for copying extra data from
		 * the initial request into items which are retrieved on demand. this is
		 * useful when some columns are available in the initial request, but
		 * not from the &quot;list&quot; action, e.&nbsp;g. indentation level in
		 * the threaded mail view. This function takes the extra data and
		 * an item as parameters and returns the updated item, which may be
		 * the same instance as the second parameter. Must be specified whenever
		 * makeExtra is specified.
		 */
		get: function(uri, params, callback, columns, cached, serialize, makeID, putBody, makeExtra, updateItem) {
			this.putBody = putBody;
			this.uri = uri;
			if (!params.columns) params.columns = "1,20";
			params.session = session;
			this.params = {};
			for (var i in params) this.params[i] = params[i];
			var completeURI = makeURI(uri, params);
			this.columns = columns;
			params.columns = params.columns + "," + columns;
			var key  = makeURI(uri, params);
			if (cached == undefined) cached = true;
			this.serialize = serialize || function(x) { return x.folder + "." + x.id; };
			this.makeID = makeID || function(x) { return {id: x[0], folder: x[1]}; };
			this.storage = cached ? this.cache.get(key) : null;
			if (!this.storage) {
				var Self = this;
				function cb_json(obj) {
					if (obj.error) return;
					var ids = new Array(obj.data.length);
					if (makeExtra) {
						Self.extraData = {};
						for (var i = 0; i < obj.data.length; i++) {
							var id = ids[i] = Self.makeID(obj.data[i]);
							Self.extraData[Self.serialize(id)] = makeExtra(obj.data[i]);
						}
					} else {
						Self.extraData = null;
						for (var i = 0; i < obj.data.length; i++)
							ids[i] = Self.makeID(obj.data[i]);
					}
					Self.current = Self.storage = new Storage(obj.timestamp, ids,
						makeURI(uri, {action: "list", session: session,
						              columns: columns ? params.columns : this.params.columns}),
						StorageCache.prefetch, StorageCache.cacheSize,
						Self.serialize, Self.makeID, Self.extraData, updateItem);
					if (cached) Self.cache.set(key, Self.storage);
					callback(Self.storage);
				}
				if(this.putBody) {
					this.json.put(completeURI, this.putBody, null, cb_json);
				} else {
					this.json.get(completeURI, null, cb_json);
				}
			} else {
				this.current = this.storage;
				callback(this.storage);
			}
		},
		
		/**
		 * Updates the current storage.
		 * @param {Function} callback Optional callback which is called after
		 * the update completes. Not called if there is no current storage.
		 */
		update: function(callback) {
			if (!this.storage){
				if (callback) callback();
				return;
			}
			if (this.storage.serverUpdates) {
				this.storage.serverUpdates();
				if (callback) callback();
				return;
			}
			var storage = this.storage;
			var ids, updateObj;
			var join = new Join(cb_forUpdate);
			var all_cb = join.add(function(obj) { ids = obj.data; });
			var update_cb = join.add (function(obj) { updateObj = obj; });
			
			if(this.putBody) {
				this.json.put(makeURI(this.uri, this.params), this.putBody, null, all_cb);
			} else {
				this.json.get(makeURI(this.uri, this.params), null, all_cb);
			}
			
			var p = {};
			for (var i in this.params) p[i] = this.params[i];
			p.action = "updates";
			p.columns = this.columns ? p.columns + "," + this.columns : p.columns;
			p.ignore = "deleted";
			p.timestamp = this.storage.timestamp;
			var Self = this;
			if(this.putBody) {
				this.json.put(makeURI(this.uri, p), this.putBody, null, update_cb);
			} else {
				this.json.get(makeURI(this.uri, p), null, update_cb);
			}

			function cb_forUpdate() {
				if (Self.makeExtra) {
					var extraData = {};
					for (var i in ids)
						extraData[Self.serialize(Self.makeID(ids[i]))] = Self.makeExtra(ids[i]);
				}
				storage.update(updateObj.timestamp, ids, updateObj.data, extraData);
				if (callback) callback();
			}
		},
		
		setCurrent: function(storage) {
			this.storage = null;
			this.current = storage;
		}
	};
})();


  //////////////////
 //   Quickinfo  //
//////////////////

/**
 * Events object for the help panel event.<b> 
 * The event is called "help" and has a single parameter:
 * the string to display (already translated).
 */
var helpevents = new Events();

register("OX_Show_Help_Panel",function(active) {
	(active ? addDOMEvent : removeDOMEvent)(body, "mouseover", handler);
	var currentnode = null;
	function handler(e) {
		var node = e.target || e.srcElement;
		var text;
		while (node) {
			if (node.quickinfo) {
				text = node.quickinfo;
				break;
			}
			if (node.id) {
				text = init.help[node.id];
				if (text) break;
			}
			node = node.parentNode;
		}
		if (node == currentnode) return;
		helpevents.post("help", node ? _(text) : "");
		currentnode = node;
	}
});

  //////////////////////
 //   Tab function   //
//////////////////////

/**
 * Register the Tabs
 * @param {Array} the tablists
 * @param {Array} the panellist
 * @param {Array} the ventlist
 */
function setTabLists(tabArray, panelArray, eventArray) {
	tabsList = tabArray;
	panelsList = panelArray;
	eventList = eventArray;
}

/**
 * change Content und Highlight Tab
 * @param {String} id from Div
 * @param {String} id from Tab
 */
function changeTab(tab, panel, disable) {
	if(!$(tab)) return;
	if(disable) return
	for (tabElementNr in tabsList) {
		tabElement = tabsList[tabElementNr];
		panelElement = panelsList[tabElementNr];
		if(eventList != null)
			eventElement = eventList[tabElementNr];
		if (tabElement != tab) {
			if ($(tabElement)) {
				$(tabElement).style.display = 'none';				
			}
			if($(panelElement)) {
				if ($(panelElement).nodeName == "TD" || $(panelElement).className.match(/^tabbing_tab/g)) {					
					$(panelElement).className = "tabbing_tab_inactive";
				} else {
				    classNameNew = (tabElementNr == 0)? 'tabPanelFirst tabPanelColors font-color-disabled background-color-additional-content border-color-design font-weight-default'
                                                           : 'tabPanel tabPanelColors font-color-disabled background-color-additional-content border-color-design font-weight-default';
				    $(panelElement).className = classNameNew;
				}
			}						
		} else {
			if ($(tabElement))
				$(tabElement).style.display = 'block';
				if(eventList != null)
					triggerEvent(eventElement[0], eventElement[1]);			
				
			if ($(panelElement)) {
				if ($(panelElement).nodeName == "TD" || $(panelElement).className.match(/^tabbing_tab/g)) {
					if (tabElementNr > 0) $(panelsList[tabElementNr-1]).className = "tabbing_tab_inactive_left";
                    $(panelElement).className = "tabbing_tab_active";
                } else {
				    classNameNew = (tabElementNr == 0)? 'tabPanelFirstHi tabPanelHiColor background-color-content font-style-lable border-color-content-default'
                                                           : 'tabPanelHi tabPanelHiColor background-color-content font-style-lable border-color-content-default';
				    $(panelElement).className = classNameNew;
                }
			}
		}
	}
}

  //////////////////////////
 //   Set Tag Function   //
//////////////////////////

/**
 * changes the tag (color_label) of an object
 * @param {Number} the tag to set
 * @param {Array} ids of tasks, appointment or contact to change
 * @param {String} timestamp of last sync
 * @param {Function} callback when update is finished
 */
function setTag(tag, ids, timestamp,callback,newlivegrid){
    var tmpObject = {};
    if(ids.length == 1){
        tmpObject.color_label = tag;
        var servletUrl = activemodule;
        if(activemodule == "mail_detail") servletUrl = "mail";
        var param=AjaxRoot + "/"+servletUrl+"?action=update&session=" + session 
                + "&id="+encodeURIComponent(ids[0].id)
                + "&folder="+encodeURIComponent(ids[0].folder || ids[0].folder_id)
                + "&timestamp="+timestamp;
        if(ids[0].recurrence_position && ids[0].recurrence_position > 0) {
            tmpObject.recurrence_position = ids[0].recurrence_position;
        }
        json.put(param,tmpObject,null, 
            function(cb) {  
                if(newlivegrid) {
                
                }
                timestamp = cb.timestamp;
                if (callback) callback(timestamp);
            }
        );
    } else if(ids.length > 1){
        var multipleObject = [];
        for(var i = 0; i < ids.length; i++){
            multipleObject[i] = { action : "update", module : activemodule, timestamp : timestamp, id : ids[i].id, folder : (ids[i].folder || ids[i].folder_id), data : { color_label : tag } };
            if(ids[i].recurrence_position && ids[i].recurrence_position > 0) {
                multipleObject[i].recurrence_position = ids[i].recurrence_position;
            }
        }
        json.put(AjaxRoot + "/multiple?session=" + session + "&continue=true",
            multipleObject,
            null,
            function(cb){
                if(newlivegrid) {
                }
                timestamp = cb.timestamp;
                if (callback) callback(timestamp);
            }
        );
    }
}


  //////////////////////////
 //   Global variables   //
//////////////////////////

/**
 * The session ID as a string.
 * Until a successful login, the value is null. After a login succeeds, the
 * value must be added as an URI parameter to every server request.
 */
var session = null;

/**
 * User configuration.
 */
var config;

/**
 * Width of resizable splits in pixels.
 */
var SplitWidth = 3;

/**
 * Array for the Tabs
 */
var tabsList = new Array();
var panelsList = new Array();

/**
 * Global storage cache.
 */
var storageCache = new StorageCache(10);

/**
 * Currently focused element.
 */
var focusedElement = null;

register("Loaded", function() {
	addDOMEvent(body, "focus", function(e) {
		focusedElement = e.target || e.srcElement;
	});
});

/**
 * Sets the focus to the specified node.
 */
function setFocus(node) {	
	if (node && node.focus) {
		setTimeout(function() { 
			try {
				node.focus();
				focusedElement = node;
			} catch (e) { } 
		}, 0);
	}
}

/**
 * Params in the Url
 */
var url = {};
(function() {
	if (location.href.indexOf('#') == -1) return;
	var qs = location.href.substring(location.href.indexOf('#')+1);
	var nv = qs.split('&');

	for(i = 0; i < nv.length; i++)
	{
	   eq = nv[i].indexOf('=');
	   url[nv[i].substring(0,eq).toLowerCase()] = 
	   	decodeURIComponent(nv[i].substring(eq + 1));
	}
})();

/**
 * Removes elements from an array based on a predicate function.
 * @param {Array} array The array to filter.
 * @param {Function} predicate A function which takes an array element and
 * its index as parameters and returns false if that element needs
 * to be removed, true otherwise.
 * @param {Number} start The index of the element where the filtering should
 * start.
 * @type Array
 * @return The modified array.
 */
function filterArray(array, predicate, start) {
	var len = array.length;
	for (var s = start || 0; s < len && predicate(array[s], s); s++);
	for (var d = s++; s < len; s++)
		if (predicate(array[s])) array[d++] = array[s];
	if (d < len) array.length = d;
	return array;
}

  //////////////////////////////////
 //   Global browser detection   //
//////////////////////////////////

/**
 * Value of the property Event.button which indicates the left mouse button.
 * @final
 */
var LeftButton = document.implementation.hasFeature("MouseEvents", "2.0") ? 0 : 1;

/**
 * Value of the property Event.button which indicates the right mouse button.
 * @final
 */
var RightButton = LeftButton + document.implementation.hasFeature("MouseEvents", "2.0") ?2:1;

/**
 * Name of the CSS &quot;float&quot; property.
 * @final
 */
var flt = (function() {
	var div = document.createElement("div");
	div.innerHTML = "<div stlye='float:left'></div>";
	return div.firstChild.style.cssFloat === undefined ? "styleFloat" : "cssFloat";
})();

/**
 * The DOM node of the HTML body.
 */
var body;

/**
 * Resizes a resizable panel.
 * @param {String} id The ID of the resized element.
 * @param {String} size The new size of the first child as a CSS length
 * specification.
 */
var resizeSplit;

/**
 * Returns a DOM node with the specified ID. Only nodes from static HTML are
 * present.
 * @param {String} id The ID of the node.
 * @type Object
 * @return the DOM node with the specified ID.
 */
var $ =function(arg) {return document.getElementById(arg);}
,replace$;

/**
 * Skips non-element nodes.
 * When navigating in the DOM tree, usually only element nodes are of interest.
 * To skip all other nodes, this function is called on a node and returns
 * the first element node in the list of siblings, starting with the specified
 * node, or the specified node if it is already an element node. Typical usage
 * involves calling this function on the <code>firstChild</code> of a parent
 * node to retrieve the first child element, then calling it on
 * the <code>nextSibling</code> of the result of the last call to retrieve
 * further child elements.
 * @param {Object} node A DOM node which is used as a starting point in
 * the search for element nodes.
 * @return Object The first element node in the sibling chain starting with
 * {@link #node}.
 */
function getElement(node) {
	if (!node) return node;
	while (node && node.nodeType != 1) node = node.nextSibling;
	return node;
}

/**
 * Utility function for creation of DOM trees from JavaScript.
 * @param {String} tag Tag name, e. g. "div".
 * @param {Object} style An object with stylesheet properties for the new node.
 * The property name &quot;flt&quot; gets converted to the correct property name
 * for float.
 * @param {Object} props An object with other properties of the new node.
 * @param {Array} chlidren An array with children of the new node.
 * @return The new DOM node.
 */
function newnode(tag, style, props, children, doc) {
	if (!doc)
		doc = document;
	var retval = doc.createElement(tag);
	if (style) for (var i in style)
		retval.style[i == "flt" ? flt : i] = style[i];
	if (props) for (var i in props) retval[i] = props[i];
	if (children) for (var i in children) retval.appendChild(children[i]);
	return retval;
}

function newtext(text, doc) {
    if (!doc) doc = document;
    return doc.createTextNode(text || "");
}

function newradio(style, props, doc) {
	style = style || {};
	props = props || {};
	if (IE) {
		delete props.type;
		return newnode('<input type="radio" name="' + (props.name || "") + '">', style, props, 0, doc);
	} else {
		props.type = "radio";
		return newnode("input", style, props, 0, doc);
	}
}

function newfrag(doc) {
    if (!doc) { doc = document; }
    return doc.createDocumentFragment();
}

  ///////////////////
 //   Animation   //
///////////////////

/**
 * Plays an animation.
 * Animations should be implemented by callback functions which take a number
 * between 0 and a specified end value as parameter. This function will call
 * such an animation callback as often as possible in a specified time interval.
 * @param {Number} duration Total duration of the animation, in milliseconds.
 * @param {Number} end The final parameter value for the callback.
 * @param {Function} callback A callback function which is called repeatedly
 * with numbers in the range from 0 to end, inclusive. The first call is
 * performed immediately, with the parameter 0. The last call always has
 * the parameter exactly equal to end.
 * @param {Function} final_cb An optional callback which is called after
 * the last iteration.
 * @type Function
 * @return A function which can be called to cancel the animation. The animation
 * callback is called immediately with the final value. The final callback can
 * be suppressed by specifying true as parameter to the cancelling function.
 */
function animate(duration, end, callback, final_cb) {
	var start = (new Date()).getTime();
	var f = end / duration;
	callback(0);
	var timer = setTimeout(anim, 0);
	function anim() {
		timer = null;
		var dt = (new Date()).getTime() - start;
		if (dt < duration) {
			callback(dt * f);
			timer = setTimeout(anim, 0);
		} else {
			callback(end);
			if (final_cb) final_cb();
		}
	}
	return function(disableFinal) {
		if (timer) clearTimeout(timer);
		callback(end);
		if(!disableFinal && final_cb) final_cb();
	};
}

/**
 * Creates a function for conversion of a linear motion to a polynomial motion
 * of a higher order. The motion ends at the same value, but with a final speed
 * of zero.
 * @param {Number} power The power of the parameter in the transforming
 * equation.
 * @param {Number} end The final value of the transformed parameter (the same
 * value as passed to the animate function).
 * @type Function
 * @return A function which takes the callback parameter in an animate callback
 * and returns the modified value to be used instead.
 */
function nonLinear(power, end) {
	var endPow = Math.pow(end, power - 1);
	return function(x) { return end - Math.pow(end - x, power) / endPow; };
} 

  //////////////////////
 //   Benchmarking   //
//////////////////////

var benchmark, stopbenchmark;

(function() {
	var times = [];
	benchmark = function(name) {
		times.push({time: new Date().getTime(), name: name});
	};
	stopbenchmark = function() {
		times.push({time: new Date().getTime()});
		var s = "Benchmark\n";
		for (var i = 1; i < times.length; i++)
			s += i + ": " + ((times[i].time - times[i - 1].time) / 1000) + "s (" + times[i - 1].name + ")\n";
		delete times[name];
		window.console ? console.log(s) : alert(s);
		times = [];
	};
})();

  ////////////////////////
 //   Initialization   //
////////////////////////

var resizeHandler;
var resizeEvents = new Events();
var pxPerEm;
var rootebene=this;
var evals=new Array();
var allnodes = {};
loadMessage =function() {};

// cannot be inside initAll since "login" (the function) is out of scope
function initLoginClick () {
	// explicit login click/submit (speed up)
	jQuery("#inner-login-button").bind("click", function (e) {
		login();
	    return false;
	});
	jQuery("#login").bind("submit", function (e) {
		login();
	    return false;
	});
	
	// init helptext-button for "security"-selection
	jQuery("#publicMachineDiv, #privateMachineDiv").hide();
	
	jQuery("#show_loginsecurity").toggle(function(){
		
		var oldBoxHeight = jQuery("#loginPrompt").height();
	
		jQuery("#show_loginsecurity").text(_("Hide description"));
		jQuery("#publicMachineDiv, #privateMachineDiv").slideDown("slow",function(){
			var deltaBoxHeight = jQuery("#loginPrompt").height()- oldBoxHeight;
			var pxVal = -120 - deltaBoxHeight/2 + "px";
			jQuery("#loginPrompt").animate({"margin-top": pxVal},"slow");
		});
			
	},
	function(){
		jQuery("#show_loginsecurity").text(_("Description"));
		jQuery("#publicMachineDiv, #privateMachineDiv").slideUp("slow",function(){
			jQuery("#loginPrompt").animate({"margin-top": "-120px"},"slow");
		});
		
	});
}


function initAll(login) {
	body = document.getElementsByTagName("body")[0];
	if(login) {

		// init login click
		initLoginClick();
		
		// maintain hasFocus of current window
	    corewindow.hasFocus = true;
	    if (IE) {
	        document.onfocusin = function(e) { corewindow.hasFocus = true; };
	        document.onfocusout = function(e) { corewindow.hasFocus = false; };
	    } else {
	        addDOMEvent(window, "focus", function() { corewindow.hasFocus = true; });
	        addDOMEvent(window, "blur", function() { corewindow.hasFocus = false; });
	    }
		
		loadMessage = function(message, status) {
		    //#. Loading message.
		    //#. %s is the current activity.
		    //#, c-format
			document.getElementById("loadmessage_text").firstChild.data = format(_("%s Please wait..."), _(message));
			document.getElementById("loadmessage_bar").style.width=status+"%";
			document.getElementById("loadmessage_bar").firstChild.data=status+"%";
		};

		loadMessage("Autologin..." /*i18n*/,"10");
		if (url.session) { // SSO
		    window.location.hash = String(window.location.hash).replace(
		        /&?\bsession=[^&]+/, "");
		    dologin({ session: url.session });
		    if(url.store && url.store == "true") {
		        window.location.hash = String(window.location.hash).replace(
    		        /&?\bstore=[^&]+/, "");
                storeSession(url.session);
		    }
		} else {
    		var match = /(\w+)([-_](\w+))?/.exec(navigator.language || navigator.userLanguage);
    		var lang = "en_US";
    		Found: if (match) {
    			if (match[2]) {
    				lang = match[1].toLowerCase() + "_" + match[3].toUpperCase();
    				if (corewindow && lang in corewindow.all_languages) break Found;
    				lang = "en_US";
    			}
    			var lng = match[1].toLowerCase();
    			if (corewindow && lng in corewindow.all_languages) break Found;
    			lng += "_";
    			var len = lng.length;
    			for (var i in corewindow.all_languages) {
    				if (i.substring(0, len) == lng) {
    					lang = i;
    					break Found;
    				}
    			}
    		}
    		setLanguage(lang);
    		(new JSON()).get(AjaxRoot + "/login?action=autologin&client=" + encodeURIComponent(oxProductInfo.id || "") + "&version=" + encodeURIComponent(oxProductInfo.pversion || ""), null,
    			function(result) { dologin(result); },
    			function(result, status) {
                    if (!status && result.code == "SVL-0015") {
                    	jQuery("#rememberme").remove();
//                        document.getElementById("login_rememberme").style.display = "none";
//                        document.getElementById("rememberme").disabled = true;
                    }
    				// enable login form
    				if (document.getElementById("loading_data")) document.getElementById("loading_data").style.display="none";
    				if (document.getElementById("loading_data_complete")) document.getElementById("loading_data_complete").style.display=""; //instead of "block" remove this style by setting it to empty string
    				if (document.getElementById("login")) document.getElementById("login").username.focus();
    				triggerEvent("LoginPageLoaded");
    				return true;
    			}
    		);
		}
		register("OX_Configuration_Loaded_Complete",function () {
			setLanguage(configGetKey("language"));
		});
	} else {
		initAll2(login);
		setLanguage(corewindow.configGetKey("language"));
	}
}
function initAll2(login) {
    
    // Callback for plugins in the parent window.
    if (!login && "initCallback" in window) initCallback();
    
	body = document.getElementsByTagName("body")[0];
	
	function getComputedStyle(node) {
		return window.getComputedStyle ? window.getComputedStyle(node, "")
		                               : node.currentStyle;
	}

	loadMessage("Rebuild Tree...", /*i18n*/
	"60");
	
	
	var nodes = document.getElementsByTagName("*");
	var nodes_len = nodes.length;
	
	for (var i = 0; i < nodes_len; i++) {
		var node = nodes[i];
		var id = node.id;
		if (id) {
			if (allnodes[id]) {
				alert("Internal Error: ID \"" + id + "\" used multiple times!");
				allnodes[id] = "Duplicate ID";
			} else
				allnodes[id] = node;
		}
	}

	$ = function(id) { return allnodes[id]; };
	replace$ = function(node) {
		if(node && node.id) {
			allnodes[node.id]=node;
		}
	};
   if(corewindow != window) {
	   setLanguage(corewindow.configGetKey("language"));
	}
	function cp(o) {
		var retval = {};
		for (var i in o)
			retval[i] = typeof(o[i]) == "object" ? cp(o[i]) : o[i];
		return retval;
	}

	function copy(o) {
		if (typeof(o) != "object") return o;
		return cp(o);
	}
	
	function arraycopy(a) {
		var retval = [];
		for (var i in a) retval[i] = copy(a[i]);
		return retval;
	}
	
	function copycontents(from, to) {
		for (var i in to) delete to[i];
		for (var i in from) to[i] = from[i];
	}
	
	/**
	 * Returns a zero Coord.
	 * A coordinate (type Coord) is {px: Number, em: Number,
	 * ids: {Sintrg: Number}, dep: {String: Number}}.
	 * @type Coord
	 * @return A new instance of the zero coordinate.
	 */
	function zero() { return {px: 0, em: 0, ids: {}, dep: {}}; }

	/**
	 * Converts a DOM node ID to a dependent coordinate of the same size.
	 * @param {String} id The ID of a DOM node.
	 * @type Coord
	 * @return A coordinate which computes to the size of the node.
	 */
	function dependency(id) {
		var retval = zero();
		retval.ids[id] = 1;
		return retval;
	}

	/**
	 * Converts a textual size specification into a coordinate.
	 * A specification consists of one or more CSS length values.
	 * Currently supported units are px, em and %.
	 * A coordinate (type Coord) is
	 * {px: Number, em: Number, ids: {String: Number}, dep: {String: Number}}.
	 * @param {String} id The node ID displayed in error messages.
	 * @param {String} text The size specification.
	 * @param {Coord} relativeTo A coordinate relative to which % is
	 * interpreted.
	 * @type Coord
	 * @return The size as a coordinate.
	 */
	function parse(id, text, relativeTo) {
		var retval = zero();
		var regex = /([+-]?[0-9]*(\.[0-9]*)?)(px|em|%)/g;
		var match;
		while (match = regex.exec(text)) {
			if (match[3] == "%")
				retval = add(retval, scale(relativeTo, Number(match[1]) / 100));
			else
				retval[match[3]] += Number(match[1]);
		}
		return retval;
	}
	
	/**
	 * Adds two coordinates.
	 * @param {Coord} a First coorfdinate
	 * @param {Coord} b Second coordinate
	 * @type Coord
	 * @return The sum of a and b.
	 */
	function add(a, b) {
		var retval = {px: a.px + b.px, em: a.em + b.em, ids: copy(a.ids),
			dep: copy(a.dep)};
		for (var i in b.ids) retval.ids[i] = (retval.ids[i] || 0) + b.ids[i];
		for (var i in b.dep) retval.dep[i] = (retval.dep[i] || 0) + b.dep[i];
		return retval;
	}
	
	/**
	 * Subtracts one coordinate from another.
	 * @param {Coord} a The minuend.
	 * @param {Coord} b The subtrahend.
	 * @type Coord
	 * @return The difference between a and b.
	 */
	function sub(a, b) {
		var retval = {px: a.px - b.px, em: a.em - b.em, ids: copy(a.ids),
			dep: copy(a.dep)};
		for (var i in b.ids) retval.ids[i] = (retval.ids[i] || 0) - b.ids[i];
		for (var i in b.dep) retval.dep[i] = (retval.dep[i] || 0) - b.dep[i];
		return retval;
	}
	
	/**
	 * Scales a coordinate by a constant factor.
	 * @param {Coord} a The coordinate.
	 * @param {Coord} s The scaling factor.
	 * @type Coord
	 * @return The scaled coordinate.
	 */
	function scale(a, s) {
		var retval = {px: a.px * s, em: a.em * s, ids: {}, dep: {}};
		for (var i in a.ids) retval.ids[i] = a.ids[i] * s;
		for (var i in a.dep) retval.dep[i] = a.dep[i] * s;
		return retval;
	}
	
	// Some browsers don't understand "position: absolute; height: auto;".
	IE6 = false;

	function initResize() {
		
		/**
		 * Size of every panel as {String: {size: [Coord], panel: Panel}}.
		 * The size array contains the Coord values for top, right, bottom,
		 * left, width and height, in that order.
		 * Values which must not be set are deleted/undefined.
		 * Panel is {id: String, size: String, children: panels,
		 * padding: String, margin: String}.
		 */
		var sizes = {};
		
		/**
		 * Computes the actual value of a coordinate.
		 * @param {Coord} coord The coordinate to compute.
		 * @param {Number} ix The index of the coordinate in its size array.
		 * @type {em: Number, px: Number}
		 * @return The coordinate with all dependencies resolved.
		 */
		function compute(coord, ix) {
			var wh = (ix & 1) + 4;
			var retval = {px: coord.px, em: coord.em};
			for (var i in coord.dep)
				retval = add(retval, scale(compute(sizes[i].size[wh], wh), coord.dep[i]));
			wh = ix & 1 ? "offsetWidth" : "offsetHeight";
			for (var i in coord.ids) retval.px += $(i)[wh] * coord.ids[i];
			return retval;
		}
		
		/**
		 * Computes the size of a panel and inserts it into {@link sizes}.
		 * HC SVNT DRACONES
		 * @param {String} auto ox:align of the parent if the parent doesn't
		 * have an ox:size
		 * @param {Array} size Available space as [Coord]. This object is
		 * modified by subtracting the space occupied by the panel.
		 * @param {Panel} panel the panel to layout.
		 * Panel is {id: String, size: String, children: panels,
		 * padding: String, border: String, margin: String}.
		 * @type Coord
		 * @return The size of the panel in the direction of {@link #auto}.
		 */
		function getPanelSize(auto, size, panel) {
			if (auto && panel.align != auto)
				alert(format('At id="%s": invalid ox:align="%s" inside ox:align="%s" without ox:size.',
					panel.id, panel.align, auto));

			var z = zero();
			var margin = extract(size, panel.margin, "margin");
			var border = extract(size, panel.border, "border");
			var padding = extract(size, panel.padding, "padding");
			var nodesize = adjust_wh(adjust_wh(adjust(size, margin), border), padding);
			var childsize = [padding[0], padding[1], padding[2], padding[3],
			                 nodesize[4], nodesize[5]];
			sizes[panel.id] = {size: nodesize, panel: panel};
			var ix = {top: 0, right: 1, bottom: 2, left: 3}[panel.align];
			var wh = (ix & 1) + 4;

			var retval = add(add(margin[wh], border[wh]), padding[wh]);
			if (panel.align == "stretch")
				recursion(auto);
			else {
				delete nodesize[(ix + 2) & 3];
				if (panel.size) {
					update(parse(panel.id, panel.size, size[wh]));
					recursion(null);
				} else if (panel.children.length)
					update(recursion(panel.align));
				else {
					retval = margin[wh];
					update(dependency(panel.id));
					delete nodesize[wh];
				}
			}
			return retval;
			
			function extract(size, border, type) {
				var retval = [z, z, z, z, z, z];
				if (!border) return retval;
				var deltas = border.split(" ");
				if (deltas.length != 4)
					alert(format('At id="%s": invalid ox:%s="%s"',
					             panel.id, type, border));
				for (var i = 0, wh = 4; i < 4; i++, wh ^= 1)
					retval[wh] = add(retval[wh],
						retval[i] = parse(panel.id, deltas[i], size[wh]));
				return retval;
			}
			
			function adjust(size, deltas) {
				if (!deltas) return arraycopy(size);
				var retval = [, , , , sub(size[4], deltas[4]), sub(size[5], deltas[5])];
				for (var i = 0; i < 4; i++) retval[i] = add(size[i], deltas[i]);
				return retval;
			}
			
			function adjust_wh(size, deltas) {
				var retval = arraycopy(size);
				if (deltas) {
					retval[4] = sub(retval[4], deltas[4]);
					retval[5] = sub(retval[5], deltas[5]);
				}
				return retval;
			}
		
			function recursion(auto) {
				var retval = zero();
				for (var i = 0; i < panel.children.length; i++) {
					var child = panel.children[i];
					if (child)
						retval = add(retval, getPanelSize(auto, childsize, child));
				}
				return retval;
			}

			function update(psize) {
				childsize[wh] = nodesize[wh] = copy(psize);
				if (panel.resize) {
					(psize = zero()).dep[panel.id] = 1;
					childsize[wh] = psize;
				}
				retval = add(retval, psize);
				size[ix] = add(size[ix], retval);
				size[wh] = sub(size[wh], retval);
			}
		}


		// Compute sizes
		var size = [zero(), zero(), zero(), zero(),
		            dependency("body"), dependency("body")];
		for (var i = 0; i < init.size.length; i++) {
			var child = init.size[i];
			if (child) getPanelSize(null, size, child);
		}

		// Remove unnecessary size specificatinos.
		var del1 = IE6 ? 2 : 4; // delete bottom for IE and height for the rest
		var check1 = IE6 ? 4 : 2;
		var del2 = IE6 ? 1 : 5; // same for right and width
		var check2 = IE6 ? 5 : 1;
		for (var i in sizes) {
			var size = sizes[i].size;
			if (size[0] && size[check1]) delete size[del1];
			if (size[3] && size[check2]) delete size[del2];
		}

		// Extract dynamic dependencies
		var deps = {}; // {String: {String: true}}
		for (var i in sizes) {
			var size = sizes[i].size;
			var dep = deps[i] = {};
			for (var j = 0; j < 6; j++)
				if (size[j]) for (var id in size[j].ids) dep[id] = true;
		}
/*
		// Remove indirect dependencies: i>j and j>k => not i>k
		for (var i in deps) {
			var dep = deps[i];
			var del = {};
			for (var j in dep) for (var k in deps[j]) del[k] = true;
			for (var j in del) delete dep[j];
		}
*/
		// Remove dependencies on body
		for (var j in deps)	delete deps[j].body;

		// Create size panel lists
		var size_panels = []; // [{String: [Coord]}]
		do {
			// Add independent panels to the lists
			var level = {};
			var hasNodes = false;
			for (var i in deps) {
				var dep = deps[i];
				var independent = true;
				for (var j in dep) { independent = false; break; }
				if (independent) copycontents(sizes[i].size, level[i] = []);
				hasNodes |= independent;
			}
			if (!hasNodes) break;
			size_panels.push(level);
			// Remove independent panels
			for (var i in level) delete deps[i];
			// Remove dependencies on removed panels
			for (var i in level)
				for (var j in deps)	delete deps[j][i];
		} while (true);

		// Check for circular dependencies
		var s = ["Circular dependencies detected:"];
		for (var i in deps) {
			var d = [];
			for (var j in deps[i]) d.push(j);
			s.push(format('"%s" depends on "%s"', i, d.join('", "')));
		}
		if (s.length > 1) alert(s.join("\n"));

		// Extract resize dependencies
		var sdeps = {}; // {String: {String: true}}
		for (var i in sizes) {
			var size = sizes[i].size;
			for (var j = 0; j < 6; j++) {
				if (!size[j]) continue;
				for (var k in size[j].dep) {
					var sd = sdeps[k];
					if (!sd) sd = sdeps[k] = {};
					sd[i] = true;
				}
			}
		}

		// Compute the transitive closure over resize dependencies
		var computed = {}; // {String: true}
		for (var i in sdeps) if (!computed[i]) transClosure(i);
		function transClosure(i) {
			computed[i] = true;
			var list = sdeps[i];
			var newentries = {};
			for (var j in list) {
				if (!computed[j]) transClosure(j);
				for (var k in sdeps[j]) newentries[k] = true;
			}
			for (var j in newentries) list[j] = true;
		}

		// Create resize panel lists from size panel lists
		var resize_panels = {}; // {String: [{String: [Coord]}]}
		var len = size_panels.length;
		for (var i in sdeps) {
			var panels = resize_panels[i] = new Array(len);
			for (var j = 0; j < len; j++) panels[j] = {};
			panels[0][i] = sizes[i].size;
			for (var j in sdeps[i]) {
				for (var k = 0; k < len; k++) {
					if (j in size_panels[k]) {
						copycontents(size_panels[k][j], panels[k][j] = []);
						break;
					}
				}
			}
		}

		loadMessage("Static resizing...", /*i18n*/
				"70");

		// Set static sizes and remove them from size panel lists
		var pxFields = ["top", "right", "bottom", "left", "height", "width"];
		var emFields = ["marginTop", "marginRight", "marginBottom", "marginLeft"];
		for (var i in size_panels) {
			var panels = size_panels[i];
			for (var j in panels) {
				var panel = panels[j];
				var style = $(j).style;
				style.position = "absolute";
				for (var k in panel) {
					var stat = true;
					for (var l in panel[k].ids) { stat = false; break; }
					var size = compute(panel[k], k);
					if (stat && (emFields[k] || !size.px || !size.em)) {
						if (size.px) {
							style[pxFields[k]] = size.px + "px";
							if (emFields[k])
								style[emFields[k]] = (size.em || 0) + "em";
						} else {
							style[pxFields[k]] = (size.em || 0) + "em";
							if (emFields[k])
								style[emFields[k]] = 0;
						}
						delete panel[k];
/*					} else if (IE_Expressions) {
						function getExpr(coord, ix) {
							var retval = [coord.px, "+pxPerEm*", coord.em];
							for (var l in coord.ids)
								retval.concat(["+$(", l, ").offset",
									(ix & 1 ? "Width*" : "Height*"),
									coord.ids[l]]);
							for (var l in coord.dep)
								retval.concat(["+$(", l, ").offset",
									(ix & 1 ? "Width*" : "Height*"),
									coord.dep[l]]);
							return retval.join("");
						}
						style.setExpression(pxFields[k], getExpr(panel[k], k));
						delete panel[k];*/
					}
				}
			}
		}
		loadMessage("Dynamic resizing...", /*i18n*/
				"80");

		// Remove static panels.
		for (var i in size_panels) {
			var panels = size_panels[i];
			Panels: for (var j in panels) {
				var panel = panels[j];
				for (var k in panel) continue Panels;
				delete panels[j];
			}
		}

		/**
		 * Computes changes which are necessary for a single coordinate due to
		 * the resizing of the window.
		 * @param {Array} changes An array to which the computed changes are
		 * appended.
		 * @param {Object} style A DOM style object of the node whose changes
		 * are computed.
		 * @param {Object} size An object of the form {px: Number, em: Number}
		 * which contains the new computed coordinate.
		 * @param {Number} ix The index of the coordinate in arrays like [Coord]
		 * or {@link pxFields} and {@link emFields}.
		 */
		function resizeChanges(changes, style, size, ix) {
			var value = Math.max(0, size.px + size.em * pxPerEm) + "px";
			var field = pxFields[ix];
			if (style[field] != value)
				changes.push({style: style, field: field, value: value});
		}

		/**
		 * Computes changes which are necessary for a single coordinate due to
		 * the resizing of an element.
		 * @param {Array} changes An array to which the computed changes are
		 * appended. Each change has the form
		 * {style: Object, field: String, Value: String}.
		 * @param {Object} style A DOM style object of the node whose changes
		 * are computed.
		 * @param {Object} size An object of the form {px: Number, em: Number}
		 * which contains the new computed coordinate.
		 * @param {Number} ix The index of the coordinate in arrays like [Coord]
		 * or {@link pxFields} and {@link emFields}.
		 */
		function resizeSplitChanges(changes, style, size, ix) {
			function change(field, value) {
				if (style[field] != value)
					changes.push({style: style, field: field, value: value});
			}
			if (emFields[ix] || !size.em) {
				change(pxFields[ix], size.px + "px");
				if (emFields[ix]) change(emFields[ix], size.em + "em");
			} else if (size.px)
				resizeChanges(changes, style, size, ix);
			else
				change(pxFields[ix], size.em + "em");
		}
		
		/**
		 * Resizes all panels.
		 * @param {Array} panel_lists An array with a list of panels for each
		 * dynamic dependency level. Each list of panels is {String: [Coord]}.
		 * @param {Number} n The current dynamic dependency level. It is
		 * an index into the panel_lists array.
		 * @param {Function} changesF A function which computes necessary
		 * changes for a coordinate and appends them to an array, which is
		 * passed as the first parameter to it. The changes are computed from
		 * a DOM style object, a computed coordinate of the form
		 * {px: Number, em: Number} and the index of the coordinate, which are
		 * passed as the second to fourth parameters, respectively.
		 * @param {Object} pending And object {timeout: Number} containing the
		 * currently pending resize. If there are further dynamic dependency
		 * levels after the current, their resizing is scheduled via
		 * setTimeout() and the returned handle is placed in this object.
		 * @see resizeChanges
		 * @see resizeSplitChanges
		 */
		function resize(panel_lists, n, changesF, pending) {
			var panels = panel_lists[n];
			// Compute required changes.
			var changes = [];
			for (var i in panels) {
				var node = $(i);
				var hidden = (node.style.display == "none");
				while (!hidden && node.parentNode) {
					hidden = node.style && node.style.display == "none";
					node = node.parentNode;
				}
				if (hidden) continue;
				var panel = panels[i];
				var style = $(i).style;
				for (var j in panel) {
					if (!panel[j]) 
						eval("debugger");
					changesF(changes, style, compute(panel[j], j), j);
				}
			}
			// Update the styles.
			if (changes.length) {
				for (var j in changes) {
					var change = changes[j];
					change.style[change.field] = change.value;
				}
			}
			// Schedule next dependency level or trigger final event.
			if (++n < panel_lists.length) {
				pending.timeout = setTimeout(function() {
					resize(panel_lists, n, changesF, pending);
				}, 0);
			} else {
				delete pending.timeout;
				resizeEvents.post("Resized");
			}
		}
		
		var resizeSplitPending = {};
		resizeSplit = function(id, size) {
			if (!sizes[id])
				alert(format('Invalid resizeSplit() call with id="%s"', id));
			var panel = sizes[id].panel;
			var ix = {top: 0, right: 1, bottom: 2, left: 3}[panel.align];
			var wh = (ix & 1) + 4;
			copycontents(parse(id, size), sizes[id].size[wh]);
			if ("timeout" in resizeSplitPending)
				clearTimeout(resizeSplitPending.timeout);
			pxPerEm = scalediv.offsetHeight / 1000;
			resize(resize_panels[id], 0, resizeSplitChanges, resizeSplitPending);
		};

		var scalediv = newnode("div",
			{position: "absolute", visibility: "hidden", width: 0, height: "1000em"});
		body.appendChild(scalediv);
		var resizePending = {};
		resizeHandler = function() {
			if ("timeout" in resizePending) clearTimeout(resizePending.timeout);
			pxPerEm = scalediv.offsetHeight / 1000;
			if (IE6)
				resizePending.timeout = setTimeout(function() {
					resize(size_panels, 0, resizeSplitChanges, resizePending);
				}, 0);
			else
				resize(size_panels, 0, resizeSplitChanges, resizePending);
		};
		window.onresize = resizeHandler;
		function final_resize() {
			resizeEvents.unregister("Resized", final_resize);
			loadMessage("Initialization ...", /*i18n*/
					"90");
			triggerEvent("Preload");
			triggerEvent("Loaded");
			if(login) { loggedIn(); } else {
				if ($("loading_data")) $("loading_data").style.display="none";
				if ($("loading_data_complete")) $("loading_data_complete").style.display="block";
			}
		}
		
		resizeEvents.register("Resized", final_resize);
		resizeHandler();
	}

	// Logging
	if (!window.console) {
		var appended = false;
		var caption = newnode("div", {color: "white", backgroundColor: "#576586"}, 0, [
			document.createTextNode("Debug Log"),
			newnode("span", {flt: "right"}, {onclick: function() {
					while (caption.nextSibling)
						logger.removeChild(caption.nextSibling);
					body.removeChild(logger);
					appended = false;
				}}, [newnode("img", 0, {src: getFullImgSrc("img/x.png") })])]);
		var logger = newnode("div", {zIndex: 9999, position: "absolute",
			width: "40em", height: "20em", overflow: "auto", right: 0,
			bottom: 0, border: "2px dashed red", background: "white"}, 0, [caption]);
		window.console = {log: function(text, params) {
			if (!appended) {
				body.appendChild(logger);
				appended = true;
			}
			var lines = format.apply(null,arguments).split("\n");
			for (var i = 0; i < lines.length; i++)
				logger.appendChild(newnode("div", 0, 0,
					[document.createTextNode(lines[i])]));
		}};
	}
	
	function makeSplitCallback(split, align, live) {
		var parent = split.parentNode;
		var previous = split.previousSibling;
		while (previous.nodeType != 1) previous = previous.previousSibling;
		return function(e) {
			function getPixels(value) {
				if (!value) return value;
				var match = /^([0-9.]+)(em|px)$/.exec(value);
				if (!match) alert(format("Invalid ox:min or ox:max at id=\"%1\".", split.id));
				var num = parseFloat(match[1]);
				switch (match[2]) {
					case "px": return num;
					case "em": return pxPerEm * num;
				}
			}
			var min = getPixels(init.min[previous.id]) || 0;
			var max = getPixels(init.max[previous.id]) || Infinity;
			var displayOffset;
			var sizeF = {
				left: function() {
					displayOffset = previous.offsetLeft;
					var offset = previous.offsetWidth - e.clientX;
					var max2 = Math.min(max, parent.clientWidth - split.offsetWidth);
					return function(x, y) {
						return Math.min(max2, Math.max(min, offset + x));
					};
				},
				right: function() {
					displayOffset = previous.offsetLeft + previous.offsetWidth - parent.offsetWidth;
					var offset = previous.offsetWidth + e.clientX;
					var max2 = Math.min(max, parent.clientWidth - split.offsetWidth);
					return function(x, y) {
						return Math.min(max2, Math.max(min, offset - x));
					};
				},
				top: function() {
					displayOffset = previous.offsetTop;
					var offset = previous.offsetHeight - e.clientY;
					var max2 = Math.min(max, parent.clientHeight - split.offsetHeight);
					return function(x, y) {
						return Math.min(max2, Math.max(min, offset + y));
					};
				},
				bottom: function() {
					displayOffset = previous.offsetTop + previous.offsetHeight - parent.offsetHeight;
					var offset = previous.offsetHeight + e.clientY;
					var max2 = Math.min(max, parent.clientHeight - split.offsetHeight);
					return function(x, y) {
						return Math.min(max2, Math.max(min, offset - y));
					};
				}
			}[align]();
			var size = sizeF(e.clientX, e.clientY);
			function m(e) {
				stopEvent(e);
				size = sizeF(e.clientX, e.clientY);
				if (live) {
					var s = function() { return size; };
					resizeSplit(previous.id, size + "px");
					resizeEvents.post("SplitResized", s, parent);
				} else
					movingSplit.style[align] = (displayOffset + size) + "px";
			};
			function u() {
				showIFrames();
				removeDOMEvent(body, "mousemove", m);
				removeDOMEvent(body, "mouseup", u);
				parent.style.cursor = "";
				if (!live) {
					split.parentNode.removeChild(movingSplit);
					movingSplit = null;
					var s = function() { return size; };
					resizeSplit(previous.id, size + "px");
					resizeEvents.post("SplitResized", s, parent);
				}
			};
			hideIFrames();
			parent.style.cursor = split.style.cursor;
			addDOMEvent(body, "mousemove", m);
			addDOMEvent(body, "mouseup", u);
			if (!live) {
				var movingSplit = split.cloneNode(true);
				movingSplit.style[{top: "marginTop", right: "marginRight",
					bottom: "marginBottom", left: "marginLeft"}[align]] = 0;
				movingSplit.style[align] = (displayOffset + size) + "px";
				movingSplit.className = movingSplit.className + " moving";
				split.parentNode.appendChild(movingSplit);
			}
			cancelDefault(e);
		};
	}
	
	// Manually resizable splits
	for (var i in init.split) {
		var split = $(i);
		addDOMEvent(split, "mousedown", makeSplitCallback(split, init.split[i], false));
	}

	var scalediv2 = newnode("div",
		{position: "absolute", visibility: "hidden", width: 0, top: 0, bottom: 0});
	body.appendChild(scalediv2);
	setTimeout(function() {		
		IE6 = scalediv2.offsetHeight < body.clientHeight;
		
		// IE6 workaround
		if (IE6) {
			for (var i in init.IE6workaround) {
				var workaround = init.IE6workaround[i];
				var dir = workaround.dir;
				var node = $(i);
				for (dir = dir & (dir - 1); dir; dir = dir & (dir - 1)) {
					node = getElement(node.firstChild);
					node.style.padding = workaround.padding;
				}
			}
			try {
				document.execCommand('BackgroundImageCache', false, true);
			} catch(e) {}
		}
		// End of IE6 workaround
		
		body.removeChild(scalediv2);
		scalediv2 = null;
		initResize();
	}, 0);
	
	originalTitle = document.title; // Document title
	register("LanguageChanged", function() {
		document.title = _(originalTitle);
		resizeOXButtons(); // resizing ox buttons when changing the language
	});
	
	/*
	 * function to resize buttons, e.g. when changing the language
	 * _oxbuttons is an array, which holds a set of arrays with two parameter
	 */ 
	var _oxbuttons = [ ["mc_today_but", "mc_today_but-text"] ];
	function resizeOXButtons() {
		for (i = 0; i < _oxbuttons.length; i++) {
		    try {
		    	if (!$(_oxbuttons[i][0]) || Math.round(parseInt($(_oxbuttons[i][1]).offsetWidth) / pxPerEm) <= 0) continue;	    
			    $(_oxbuttons[i][0]).style.width = Math.round(parseInt($(_oxbuttons[i][1]).offsetWidth) / pxPerEm + 2)  + "em";
		    } catch (ex) { }
		}
    }

	// Direct linking
	if (login) register("OX_Configuration_Loaded_Complete", function() {
		var module = location.hash.match(/[#&]m=([^#&]+)/);
		var folder = location.hash.match(/[#&]f=([^#&]+)/);
		var id = location.hash.match(/[#&]i=([^#&]+)/);
		if (module && folder) {
			triggerEvent("OX_Direct_Linking", module[1],
			             { module: module[1], folder: folder[1], id: id && id.length ? id[1] : null, 
			               folder_id: folder[1]});
		}
	});
	
	// Automatic logout handling
	var loggingOut = false;
	if (login) JSON.errorHandler = function(result, status) {
		if (status) {
			triggerEvent("OX_New_Error", 2,
                //#. HTTP Errors from the server
                //#. %1$s is the numeric HTTP status code
                //#. %2$s is the corresponding HTTP status text
			    //#, c-format
                format(_("Error: %1$s - %2$s"), status, result));
		} else if (result.code.match(/^SES-02..$/)) {
			if (!loggingOut) {
				loggingOut = true;
				window.onbeforeunload = null;
                newAlert(_("Session has expired"), _("Your session has expired. Please log in again."), 
                       function() {
                            window.onbeforeunload = null;
                            setTimeout( function() { window.location.replace(sessionExpired_location.format()); },0);       
                       });
			}
		} else
			newServerError(result,4);
	};
	
	// TODO: move to separate event handlers.
	addDOMEvent(body, "click", function(e) {
		triggerEvent('OX_GLOBAL_CLICK',e);
	});
}

function unloadMessageMainLogin(e) { }

var hideIFrames, showIFrames;

(function() {
	var count = 0;
	
	hideIFrames = function() {
		if (count++) return;
		for (var i in init.hide) {
			var div = $(i + "-hide").style;
			var iframe = $(i);
			div.width = iframe.offsetWidth + "px";
			div.height = iframe.offsetHeight + "px";
			div.display = "block";
		}
	};
	
	showIFrames = function() {
		if (--count) return;
		for (var i in init.hide) $(i + "-hide").style.display = "none";
	};
})();

/**
 * Utility function for seperating file names from the path
 * @param {String} path with file name<br />
 * 		Example:<br />
 * 		/path/to/file.ext
 * @return The new file name
 */
function separateFilenameFromPath(sValue) {
	var aTMP = sValue.split("/");
	if(aTMP.length==1)
	{
		aTMP2 = sValue.split("\\");
		return aTMP2[(aTMP2.length-1)];	
	}	
	return aTMP[(aTMP.length-1)];	
}

function removeClass(sClassName,sClassToDel)
{
	var sDeseletedClassName = '';
	if(!sClassName || sClassName.length == 0)
		return sDeseletedClassName;
	var aSplited = sClassName.split(' ');
	for(var nInd=0;nInd < aSplited.length;nInd++)
	{
		if(aSplited[nInd].length>0 && aSplited[nInd] != sClassToDel)
		{
			sDeseletedClassName += ' '+aSplited[nInd];
		}
	}			
	return sDeseletedClassName;
}
/**
*	Replace node.innerHTML="" with this
*/
function removeChildNodes(node) {
	if (!node) return;
    var nodes = node.childNodes;
	if (nodes) while (nodes.length > 0) node.removeChild(nodes[0]);
}

/* 
 * convert Bytes to KB, MB or GB
 * @param {Number} bytes Number of bytes
 * @return {String} The size as a human-readable, already translated string.
 * */
function bytesToString(bytes)
{
	var units = [
		//#. Byte unit
		"bytes", /*i18n*/
		//#. Kilobyte unit (1024)
		"KB", /*i18n*/
		//#. Megabyte unit (1024^2)
		"MB", /*i18n*/
		//#. Gigabyte unit (1024^3)
		"GB", /*i18n*/
		//#. Terabyte unit (1024^4)
		"TB", /*i18n*/
		//#. Petabyte unit (1024^5)
		"PB", /*i18n*/
		//#. Exabyte unit (1024^6)
		"EB", /*i18n*/
		//#. Zettabyte unit (1024^7)
		"ZB", /*i18n*/
		//#. Yottabyte unit (1024^8)
		"YB" /*i18n*/
	];

	for (var i = 0; i < units.length; i++) {
		if (bytes < 1000)
			//#. Byte size like "500 MB". Space or no space?
			//#. %1$s is the number
			//#. %2$s is the unit
		    //#, c-format
			return format(pgettext("bytes", "%1$s %2$s"),
			              formatNumbers(Math.round(bytes * 100) / 100),
			              _(units[i]));
		bytes /= 1024;
	}
}

function clone(element, sourceWindow) {
	if (typeof(element) != "object") return element;
    var array = sourceWindow ? sourceWindow.Array : Array;
	return subclone(element);

    function subclone(element) {
        if(!element) return null;
        var retval = element.constructor == array ? [] : {};
        //var retval = Object.prototype.toString.call(element) == "[object Array]"
        //             ? [] : {};
        for (var i in element)
            retval[i] = typeof element[i] == "object" ? subclone(element[i])
                                                      : element[i];
        return retval;
    }
}

function trimStr(withBlanks)
{
    return String(withBlanks || "").replace(/^\s*(\S*(\s+\S+)*)\s*$/, "$1");
}

function isIDNEmail(mail) {
	var regexmail = /^([+a-zA-Z0-9_-]|\\[!#$%&'*+=?^_{|}~])+(\.([+a-zA-Z0-9_-]|\\[!#$%&'*+=?^_{|}~])+)*@.(.{0,61}.)?(\..(.{0,61}.)?)*\.[a-zA-Z]{2,6}$/;
	return regexmail.test(mail);
}

function validateEmail(mail) {
    var regexmail = /^([+a-zA-Z0-9_-]|\\[!#$%&'*+=?^_{|}~])+(\.([+a-zA-Z0-9_-]|\\[!#$%&'*+=?^_{|}~])+)*@[a-zA-Z0-9_-]([a-zA-Z0-9_-]{0,61}[a-zA-Z0-9_-])?(\.[a-zA-Z0-9_-]([a-zA-Z0-9_-]{0,61}[a-zA-Z0-9_-])?)*\.[a-zA-Z]{2,6}$/;
    return regexmail.test(mail);
}

//DEFAULT
var defaultviews=new Object();

function addDefaultView(myview,viewname) {
	if(!defaultviews) {
		defaultviews=new Object();
	}
	defaultviews[myview]=viewname;
}
function removeDefaultView(myview) {
	if(!defaultviews) {
		defaultviews=new Object();
	}
	delete defaultviews[myview];
}	
function getDefaultViewName(myview) {
	if(defaultviews) {
		if(defaultviews[myview]) {
			return defaultviews[myview];
		}
	}
	return null;
}
function getDefaultSubviews(myview) {
	var myret = new Object();
	for (i in defaultviews) {
		var splitview = i.split("/");
		var searchview = myview.split("/");
		for(i2=0;i2<searchview.length;i2++) {
			if(!splitview[i2]) {
				break;
			}
			if(splitview[i2]!=searchview[i2]) {
				break;
			}
			if(searchview.length==(i2+1)) {
				myret[i]=defaultviews[i];
			}
		} 
	}
	return clone(myret);
}

function isDefaultableView(key) {
	if(defaultviews) {
		if(defaultviews[key]) {
			return true;
		}
	}
	return false;	
}


function revertUrlEncodedString(str) {
	var sEncStr = decodeURIComponent(str);	
	return sEncStr;
}

//replaces blanks and & with url code
function getUrlEncodedString(str){
	return encodeURIComponent(str);	
}

function extendConfObj(obj) {	
    obj["language{0}"] = configGetKey("language").split("_")[0];
    obj["language{1}"] = (configGetKey("language").split("_")[1]).toLowerCase();
    obj["protocol"] = location.protocol.match(/^(.*):/)[1];
    obj["hostname"] = location.host;
    obj["path"] = location.pathname.match(/(.*\/)(.*)/)[1];
    obj["file"] = location.pathname.match(/(.*\/)(.*)/)[2];
    obj["session"] = session;
    obj["loginname"] = $("username").value || "";
    return obj;
}

var _strFormatRegexp = new RegExp("\\[([^\\]]+)\\]", "g");
String.prototype.format = function(obj) {
	obj = extendConfObj(obj || {});
	function repl(_,val) {
        return obj[val] || configGetKey(val) || val;   
    }
    return this.replace(_strFormatRegexp, repl);
};

function redirect2Help(param) {
	window.open(param.format() || help_location.format(), 'oxhelp');
}
register("OX_Show_Help", redirect2Help);
		
register("OX_Show_About", function() {	
			$("about_product_name").firstChild.data = oxProductInfo.product_name;
			$("about_gui_version").firstChild.data = oxProductInfo.pversion;
		    $("about_vendor_address").innerHTML = oxProductInfo.vendor_address.replace(/\n/gi,"<br/>");
			$("about_server_version").firstChild.data = configGetKey("serverVersion");
			AboutPopup.openWindow();
		});

register("Loaded", function() {
	// Context menu for the help and about dialog in the upper panel
	var helpImage = $("help_image");
	if (helpImage) {
	    var menu = globalContextMenus.help = new PulldownMenu(helpImage);
	    menu.addItem(new MenuItem("Help", /*i18n*/
	        function() { triggerEvent("OX_Show_Help", help_location); }));
	    menu.addItem(new MenuItem("Error messages", /*i18n*/
	        function() { showErrorinPopup(); }));
	    menu.addItem(new MenuItem("About", /*i18n*/
	        function() { triggerEvent("OX_Show_About"); }));
	}
	var menu = globalContextMenus.mailAddress = new ContextMenu();
    menu.addItem((new MenuItem("Add to address book"/*i18n*/, function() {
        createNewContactfromMail(this.getContext());
    })).setIcon("img/folder/newfolder.gif", "img/folder/newfolder_d.gif"));
});

/**
 * Global object for the help menu in the top right corner of the Groupware.
 */
var HelpMenu = {
    /**
     * Adds a plain text entry to the help menu.
     * @param {String} text The human-readable text which is displayed
     * in the menu.
     * @param {Function} callback A callback function which is called when
     * the user clicks on the added entry.
     */
    addText: function(text, callback) {
        globalContextMenus.help.addItem(new MenuItem(text, callback));
    }
};

function setContentHeader(fields) {
	function setHeaderContent(oFolder){
		if(oFolder.oxfolder.data.created_by) {
			internalCache.getUsers([oFolder.oxfolder.data.created_by], function(cbObj){
				if(oFolder.oxfolder.data.type == 1 || oFolder.oxfolder.data.type == 3){
					for(var i=0;i<fields.length;i++) {
					    //#. Title of the content area when a shared folder is displayed.
					    //#. %1$s is the folder name.
					    //#. %2$s is the user name of the folder's owner.
					    //#, c-format
						$(fields[i]).firstChild.data = format(_("%1$s of %2$s"),oFolder.oxfolder.data.title,cbObj[oFolder.oxfolder.data.created_by].display_name);
					}
				} else if(oFolder.oxfolder.data.type == 2 ){
					for(var i=0;i<fields.length;i++) {
					    //#. Title of the content area when a public folder is displayed.
					    //#. %s is the folder name.
					    //#, c-format
						$(fields[i]).firstChild.data = format(_("Public folder %s"),oFolder.oxfolder.data.title);
					} 
				} else {
					for(var i=0;i<fields.length;i++) {
                        //#. Title of the content area when a public folder is displayed.
                        //#. %s is the folder name.
                        //#, c-format
						$(fields[i]).firstChild.data = format(_("Public folder %s"),oFolder.oxfolder.data.title);
					} 
				}
			});
		}
	}
	oMainFolderTree.cache.get_folder(activefolder,setHeaderContent);		
}

/* 
 * Converts an array with multiple addresses to a linked address list
 * @param {node} The node where the addresses will be appended to
 * @param {array} The server array which holds all the addresses 
 * @param {boolean} true = only the personal address information will be shown, other false
 * @return {node}
 * */
function getAdressStringLinked(node, addresses, personal) {	
	for (a = 0; a < addresses.length; a++) {
		var pAddr = addresses[a][0];
		var mAddr = addresses[a][1] || "";
		
		// quote mail address if not already quoted
		if (pAddr != null && (pAddr.split("\"").length <= 2 && pAddr.split("'").length <= 2)) {
			pAddr = "\"" + pAddr + "\"";			
		}
		
		// build visible address string, depending on the personal setting
		var vMailAddr = personal && pAddr ? pAddr : pAddr ? pAddr + " <" + mAddr + ">" : mAddr;
		// build full address string, used for the click to send
		var rMailAddr = pAddr ? pAddr + " <" + mAddr + ">" : mAddr;
		
		// create span with mail address
		var oDOMDiv = newnode("span", null, { className: "linkInView" }, 
			[ document.createTextNode(vMailAddr + (addresses.length-1 > a ? ", " : ""))] );
				
		addDOMEvent(oDOMDiv, "click", (function(oDOMDiv, rMailAddr) {
				return function(e) {
					//TODO SELECT MAIL	
					cancelDefault(e);
					corewindow.sendMailToRecipientMail(rMailAddr);
				};
			})(oDOMDiv, rMailAddr));
		
		var o = {};
        o.email1 = mAddr;
        o.last_name = pAddr || "";
        o.last_name = o.last_name.replace(/"|'/g,""); /*remove quotes*/
        /* good guess check to get first and last name (cudos to vp for the regexp :))*/
        var m = o.last_name.match(/(([^,]*),\s*(.+))|((\S*)\s+(.+))/);
        if (m) {
          o.last_name = m[2] || m[6];
          o.first_name =  m[3] || m[5];
        }
        addDOMEvent(oDOMDiv, "contextmenu", (function(o) {
            return function(e) {
                globalContextMenus.mailAddress.display(e.clientX, e.clientY, o);
            };
        })(o));
		try {
			if ("registerSource" in window && registerSource) {
				registerSource(oDOMDiv, "mailaddress", (function(o) {
					return function() {
						return o;
					};
				})(o), null, null, mailaddressdefaultdisabled, defaultdisabledremove);
			}
		} catch (e) { }
		node.appendChild(oDOMDiv);
	}
	return node;
}

/* 
 * function createas and writes creator information in the bottom of detailview
 * @param {Object}: should be contain fields modified_by, created_by, last_modified and creation_date
 * @param {String}: id of the container div
 * */
function writeBottomString(oObj,sDomIdContainer) {		
 	var nIdCreatedBy = oObj.created_by;
 	var nIdModifiedBy = (oObj.modified_by == undefined) ? oObj.created_by : oObj.modified_by; 	

	var creation_date = formatDate(oObj.creation_date, "datetime");
	var last_modified = formatDate(oObj.last_modified, "datetime");
	internalCache.getUsers([nIdCreatedBy], function(arg) {
		var created_by = arg[nIdCreatedBy].display_name;
		internalCache.getUsers([nIdModifiedBy], function(arg) {
			var modified_by = arg[nIdModifiedBy].display_name;
			removeChildNodes($(sDomIdContainer));
			$(sDomIdContainer).appendChild((new I18nNode(function() {
				//#. General object information at the bottom of the content area.
			    //#. %1$s is the creation date.
			    //#. %2$s is the name of the creator.
			    //#. %3$s is the last modification date.
			    //#. %4$s is the name of the last editor.
			    //#, c-format
			    return format(_("Created on %1$s by %2$s, last changed on %3$s by %4$s"),
				              creation_date, created_by, last_modified, modified_by);
			})).node);
		});	
	});	
}
function getFrameElement(id) {
	return $ALL(id).contentWindow;
}
var $2,$ALL,removeTMPId, addTMPId;
var tmp_nodes;
(function() {
	tmp_nodes=new Object();	
	addTMPId= function (node) {
		tmp_nodes[node.id]=new Object();
		tmp_nodes[node.id]["node"]=node;
	};
	removeTMPId= function (id) {
		if(id.id) { id=id.id; }
		delete tmp_nodes[id];
	};
	$2 = function(id) { 
		return (tmp_nodes[id]) ? tmp_nodes[id].node : undefined;
	};
	$ALL = function(id) { return $(id) || $2(id) || document.getElementById(id); };
})();

function getAbsolutePositionLeft(node) {
	 var xPos=node.offsetLeft;
	 var oParent=node.offsetParent;
	 while(oParent != null) {
	     xPos +=oParent.offsetLeft - (oParent.scrollLeft || 0);
	     oParent=oParent.offsetParent;
	 }
	 return xPos;
}

function getAbsolutePositionTop(node) {
    var yPos=node.offsetTop;
    var oParent=node.offsetParent;
    while(oParent != null) {
        yPos +=oParent.offsetTop - (oParent.scrollTop || 0);
        oParent=oParent.offsetParent;
    }
    return yPos;
}

/**
 * Returns the absolute position of a DOM node in pixels.
 * @param {DOMNode} node The DOM node for which to determine the position.
 * @type Object
 * @return An object with the members <code>x</code> and <code>y</code>,
 * representing the horizontal and vertical position in pixels, respectively. 
 */
function getAbsolutePosition(node) {
    for (var x = 0, y = 0; node; node = node.offsetParent) {
        x += node.offsetLeft;
        y += node.offsetTop;
    }
    return { x: x, y: y };
}

/**
 * Compares an object with an old copy of that object and removes fields which
 * have not changed.
 * @param {Object} oldObject The old copy of the object.
 * @param {Object} newObject The current object which is modified.
 */
function checkModified(oldObject, newObject) {
	for (var i in newObject) {
		if (!(i in oldObject)) continue;
		var newItem = newObject[i];
		var oldItem = oldObject[i];
		if (newItem == oldItem) delete newObject[i];
		else if (newItem && typeof newItem == "object") {
			if (newItem instanceof Array) {
				Compare: if (newItem.length == oldItem.length) {
					for (var j = 0; j < newItem.length; j++)
						if (newItem[j] != oldItem[j]) break Compare;
					delete newObject[i];
				}
			} else {
				if (oldItem) checkModified(oldItem, newItem);
				if (isEmpty(newItem)) delete newObject[i];
			}
		}
	}
}


function loadFileForCacheOnInit(file) {
    (new JSON).get(file, null, emptyFunction, null, true);
}
register("LoginPageLoaded",function() {
	setTimeout(function() {
		preloadimagescore();
		preloadMailNewImages();
	},1000);
});

function preloadimagescore() {
	loadFileForCacheOnInit(getFullImgSrc("img/dummy.gif"));
	loadFileForCacheOnInit(getFullImgSrc("img/mail/btnnew_email.gif"));
	loadFileForCacheOnInit(getFullImgSrc("img/mail/btnnew_email.gif"));
	loadFileForCacheOnInit(getFullImgSrc("img/mail/btnnew_email.gif"));
	loadFileForCacheOnInit(getFullImgSrc("img/portal/mod_portal_sel.gif"));
	loadFileForCacheOnInit(getFullImgSrc("img/calendar/mod_calendar.gif"));
	loadFileForCacheOnInit(getFullImgSrc("img/contacts/mod_contacts.gif"));
	loadFileForCacheOnInit(getFullImgSrc("img/tasks/mod_tasks.gif"));
	loadFileForCacheOnInit(getFullImgSrc("img/infostore/mod_infostore.gif"));
	loadFileForCacheOnInit(getFullImgSrc("img/configuration/mod_configuration.gif"));
	loadFileForCacheOnInit(getFullImgSrc("img/plus.gif"));
	loadFileForCacheOnInit(getFullImgSrc("img/noplus.gif"));
	loadFileForCacheOnInit(getFullImgSrc("img/folder/folder_closed.gif"));
}

function preloadMailNewImages() {
	loadFileForCacheOnInit(urlify("3rdparty/tinymce/jscripts/tiny_mce/themes/advanced/img/icons.gif"));
}

function getDirectLinkLocal(oObj){
	if (!oObj) return;
	oObj["folder"] = oObj.folder || oObj.folder_id;
	if (oObj.id) oObj["object_id"] = oObj.id;
	oObj["module"] = oObj.module || "infostore";
	return directLink_location.format(oObj);
}

function getMimeImage(sMimeType){
	if (sMimeType.indexOf(";") != -1) {
		sMimeType = sMimeType.substring(0, sMimeType.indexOf(";"));
	}
	var oImageMap = new Object();
	oImageMap["application/pdf"] = "pdf.png";
	oImageMap["text/plain"] = "txt.png";
	oImageMap["text/ical"] = "ical.gif";
	oImageMap["text/x-ical"] = "ical.gif";
	oImageMap["text/calendar"] = "ical.gif";
	oImageMap["text/x-calendar"] = "ical.gif";
	oImageMap["text/vcard"] = "vcard.gif";
	oImageMap["text/x-vcard"] = "vcard.gif";
	oImageMap["application/vnd.oasis.opendocument.spreadsheet"] = "ooo_calc.png";		
	oImageMap["application/vnd.oasis.opendocument.text"] = "ooo_writer.png";			 	
	oImageMap["application/vnd.oasis.opendocument.graphics"] = "ooo_draw.png";			 				
	oImageMap["application/x-gzip"] = "tgz.png";
	oImageMap["application/x-tar"] = "tar.png";
	oImageMap["image/png"] = "image.png";
	oImageMap["image/jpeg"] = "image.png";		
	oImageMap["image/pjpeg"] = "image.png";				
	oImageMap["image/gif"] = "image.png";
	oImageMap["application/postscript"]	= "postscript.png";	
	oImageMap["application/octet-stream"]	= "binary.png";
	oImageMap["application/java-archive"]	= "java_jar.png";
	oImageMap["text/x-log"] = "log.png";
	oImageMap["video/x-ms-wmv"]	= "video.png";
	
	return (oImageMap[sMimeType] == undefined) ? getFullImgSrc("img/infostore/mimetypes/empty.png") : getFullImgSrc("img/infostore/mimetypes/" + oImageMap[sMimeType]);	
}

//@TODO: THIS NEEDS TO BE CHANGED ALL OVER THE APPLICATION!!!
var json = new JSON(); 

//fade(node,start,end,speed,step,cb)

function fade(node,start,end,duration,cb) {
	if(configGetKey("gui.effects.fading")) {
		return animate(duration, Math.abs(start-end),
    		function(val) {
    			var tmpvalue=start;
    			if(start>end) { tmpvalue=tmpvalue-val; }
    			else { tmpvalue=tmpvalue+val; }
    			fade_setOpacity(node,tmpvalue,true);
    		}, cb);
	} else {
		fade_setOpacity(node, end, false);
		if (cb) cb();
        return emptyFunction;
	}
}

function fade_setOpacity(node,opacity,fading) {
/*
	if(configGetKey("gui.effects.fading")) {
		node.style.filter = "alpha(style=0,opacity:" + opacity + ")"; // IE
        node.style.MozOpacity = (opacity / 100);        // Gecko < 1.5
        node.style.opacity = (opacity / 100);       // Gecko >= 1.5
	} else {
		node.style.filter = "alpha(style=0,opacity:" + 100 + ")"; // IE
        node.style.MozOpacity = (100 / 100);        // Gecko < 1.5
        node.style.opacity = (100 / 100);       // Gecko >= 1.5
	}
*/
	if(opacity < 100 || fading) {
    	node.style.filter = "alpha(style=0,opacity:" + opacity + ")"; // IE
        node.style.opacity = (opacity / 100);       // Gecko >= 1.5
    } else {
        node.style.filter = "";
        node.style.opacity = "";
    }
	if(opacity==0) {node.style.display="none"; return; }
	if(node.style.display=="none" || node.style.display=="NONE") {
		if(node.tagName == "DIV") { node.style.display = "block"; }
		else { node.style.display = ""; }
	} 
}

var windowWidth = 800;
var windowHeight = 660;
function getWindowOptions(width, height, optional) {
	var x = (screen.width - (width || windowWidth)) / 2;
	var y = (screen.height - (height ||windowHeight)) / 2 - (25);	
	return "width=" + (width || windowWidth) + "px,height=" + (height ||windowHeight) + "px,resizable=yes,menubar=no,toolbar=no,status=no,left=" + x + ",top=" + y + ",screenX=" + x + ",screenY=" + y + (optional ? ", " + optional : "");
}

/**
 * Compares two objects recursively.
 * @param a 
 * @param b 
 */
function equals(a, b) {
    if(a === b) return true;
    if (   !(a instanceof Object)
        || !(b instanceof Object)) return a == b;
    for(var child in a) 
        if(!equals(a[child], b[child])) return false;
    for(var child in b)
        if(!(child in a)) return false;
    return true;
}

function getDefaultCalendarView() {
    var defaults = configGetKey("gui.calendar.views");
    return "calendar/" + defaults.view + "/" + defaults[defaults.view];
}

var menuarrows = {};
  ///////////////////
 //   Selection   //
///////////////////

/**
 * Selection as a set of object IDs.
 */
function Selection2() {
    /**
     * Number of selected items.
     */
    this.count = 0;
    /**
     * A map from object IDs to object IDs.
     * @private
     */
    this.data = new LRUKeyList();
    
    /**
     * Index of the selection anchor.
     * The anchor is used for range selects with the Shift key.
     * @private
     */
    this.anchor = 0;
    
    /**
     * Events triggered by this Selection.
     * <dl><dt>Selected</dt><dd>The selection has changed. Parameters:
     * <ul><li>Number of selected elements.</li></ul></dd></dl>
     */
    this.events = new Events();
    
    var Self = this;
    
    this.collection;
    
    this.selectedIndex = 0;
    
    //TODO
    /**this.changed_cb = function() {
        var oldcount = Self.count;
        for (var id in Self.data) {
            if (!(id in Self.storage.indices)) {
                delete Self.data[id];
                Self.count--;
            }
        }
        if (Self.count != oldcount)
            Self.events.post("Selected", Self.count);
    };***/
}

Selection2.prototype = {
    /**
     * Returns the selection status of a single item.
     * @param {Int} index The index of the queried item.
     * @return Boolean
     */
    get: function(index) {
        if (!this.collection) console.error("Selection.get without collection");
        var key = this.collection.objects[index];
        if (!key) return false;
        return this.data.get(key) != null;
    },
    
    /**
     * Returns the selection status of a single item. returns false if ths index
     * is out of range.
     * @param {Int} index The index of the queried item.
     * @return Boolean
     */
    get2: function(index) {
        if (!this.collection) console.error("Selection.get2 without collection");
        var key = collection.objects[index];
        if (!key) return false;
        return this.data.get(key) != null;
    },
    /**
     * Toggles the selection status of a single item.
     * @param {Int} index The index of the toggled item.
     * @return the new selection status of the toggled item.
     * @private
     */
    toggle: function(index) {
        if (!this.collection) console.error("Selection.toggle without collection");
        var key = this.collection.objects[index];
        var oldValue = this.data.get(key) ;
        (oldValue)  ? this.data.remove(key) : this.data.set(key,true);
        this.count += oldValue ? -1 : 1;
        this.events.post("Selected", this.count);
        return !(oldValue != null) ;
    },
    
    /**
     * Deselects the specified serialized object IDs.
     * @param {Object} sids An object with serialized object IDs to deselect
     * as keys.
     */
    deselectSIDs: function(keys) {
        for (var i in keys) {
            var oldValue = this.data.get(keys[i]);
            this.count -= (oldValue) ? 1 : 0;
            this.data.remove(keys[i]);
        }
        this.events.post("Selected", this.count);
    },
    
    /**
     * Selects the specified serialized Keys
     * @param {Object} keys - an Array of objects with serialized Keys
     * @see OXCache.Key
     */
    selectIDs: function(keys) {
    	this.reset();
    	// set new selection    	
        for (var i in keys) {
            this.data.set(keys[i], true);
        }
        this.count = keys.length;
        this.events.post("Selected", this.count);
    },

    /**
     * Clears the entire selection.
     * @private
     */
    reset: function() {
        this.data = new LRUKeyList();
        this.count = 0;
        this.events.post("Selected", this.count);
    },
    
    /**
     * Selects multiple items.
     * @param {Int} from Lower inclusive limit of the selected range.
     * @param {Int} to Upper exclusive limit of the selected range.
     * @private
     */
    select: function(from, to) {
         if (!this.collection) console.error("Selection.select without collection");
        for (var i = from; i < to; i++) {
            var key = this.collection.objects[i];
            var oldValue = this.data.get(key);
            if (!(oldValue)) {
                this.count++;
                this.data.set(key,true);
            }
        }
        this.events.post("Selected", this.count);
    },
    
    /**
     * Returns an array with object IDs of selected items.
     * @type Array
     * @return an array with object IDs of selected items.
     */
    getSelected: function() {
        var myArray = new Array();
        var tmpKeys=this.data.keys();
        //FIX FOR UPDATES AFTER OBJECT IS STILL UPDATED E.G. UPDATE CONTACTS FLAGs two times on same object
        if(!this.collection) { return tmpKeys; }
        for(var i=0;i<tmpKeys.length;i++) {
            myArray.push(this.collection.objects[
                this.collection.map_objects.get(tmpKeys[i]) - 1
            ] || tmpKeys[i]);
        }
	    return myArray;
    },
    
    /**
     * Handles a mouse click.
     * @param {Number} index Index of the clicked item.
     * @param {Event} e An optional event object which specifies which modifier
     * keys were held down at the time of the click.
     */
    click: function(index, e) {
        if (!this.collection) console.error("Selection.click without collection");
        if (index < 0 || index >= this.collection.objects.length) {
            this.reset();
            return;
        }
        if (!e || !(Mac ? e.metaKey : e.ctrlKey)) this.reset();
        if (e && e.shiftKey)
            this.select(Math.min(this.anchor, index),
                Math.max(this.anchor, index) + 1);
        else {
            this.toggle(index);
            this.anchor = index;
        }
    },
    
    /**
     * Sets the storage which is used to convert indices to object IDs.
     * @param {Collection} collection The collection, or null.
     * @param {Int} selectedIndex An optional parameter which specifies
     * the index of an automatically selected element if the selection is empty
     * or becomes empty because of the new collection.
     * @type Boolean
     * @return Whether an element was automatically selected.
     */
    setCollection: function(collection, selectedIndex) {
        var oldcount=this.count;
        if (collection) {
			var keys=this.data.keys();
            for (var id in keys) {
                if (!(collection.map_objects.get(keys[id]))) { 
                    this.data.remove(keys[id]);
                    this.count--;
                }
            }
        }
        this.collection = collection;
        var retval = !this.count;
        if (collection) {
            if (!this.count && selectedIndex != undefined) { 
            	this.click(selectedIndex); 
            } else if (this.count != oldcount) { 
            	this.events.post("Selected", this.count); 
            }
        }
        return retval;
    },
    
    /**
     * @deprecated
     */
    getID: function() {
        return this.data.get(this.data.keys()[0]);
    }
};
/**
 * 
 * All content on this website (including text, images, source
 * code and any other original works), unless otherwise noted,
 * is licensed under a Creative Commons License.
 * 
 * http://creativecommons.org/licenses/by-nc-sa/2.5/
 * 
 * Copyright (C) 2004-2010 Open-Xchange, Inc.
 * Mail: info@open-xchange.com 
 * 
 * @author Viktor Pracht <viktor.pracht@open-xchange.com>
 */

function centerPopupWindow(popupwindow) {
    var windowheight=$("body").offsetHeight;
    var windowwidth=$("body").offsetWidth;
    var tmpnode=popupwindow.cloneNode(true);
    tmpnode.style.display="block";
    tmpnode.style.zIndex=-1;
    body.appendChild(tmpnode);
    var popupwindowheight=tmpnode.offsetHeight;
    var popupwindowwidth=tmpnode.offsetWidth;
    body.removeChild(tmpnode);
    popupwindow.style.left =((windowwidth/2) -(popupwindowwidth/2)) +"px";
    popupwindow.style.top =((windowheight/2) -(popupwindowheight/2)) +"px";
}

register("Loaded", function() {
    addDOMEvent($("body"), "contextmenu", function(e) {
        switch ((e.target || e.srcElement).nodeName.toLowerCase()) {
            case "textarea":
            case "a":
                break;
            default:
                stopEvent(e);
                return false;
        }
    });
    for (var i in init.movable) {
        var node = $(init.movable[i]);
		var parent = $(node.id.substring(0, node.id.length-2));
        (function(node, parent) {
            addDOMEvent(node, "mousedown", function(e) {
                if (e.button != LeftButton) return;
                moving = parent;
                offsetX = parent.offsetLeft - e.clientX;
                offsetY = parent.offsetTop - e.clientY;
                parent.style.cursor = "move";
                function moveHandler(e) {
                    var topposition=(e.clientY + offsetY);
                    var leftposition=(e.clientX + offsetX);
                    if(topposition<0) {
                        topposition=0;
                    }
                    else if(topposition+parent.offsetHeight > $("body").offsetHeight) {
                        topposition=$("body").offsetHeight-parent.offsetHeight;
                    }
                    if(leftposition<0) {
                        leftposition=0;
                    }
                    else if(leftposition+parent.offsetWidth > $("body").offsetWidth) {
                        leftposition=$("body").offsetWidth-parent.offsetWidth;
                    }
                    moving.style.left = leftposition + "px";
                    moving.style.top = topposition + "px";
                    stopEvent(e);
                }
                function upHandler(e) {
                    if (moving && e.button == LeftButton) {
                        moving = null;
                        parent.style.cursor = "auto";
                        removeDOMEvent(body, "mousemove", moveHandler);
                        removeDOMEvent(body, "mouseup", upHandler);
                        //removeDOMEvent(body, "mouseout", upHandler);
                        stopEvent(e);
                    }
                }
                addDOMEvent(body, "mousemove", moveHandler);
                addDOMEvent(body, "mouseup", upHandler);
                //addDOMEvent(body, "mouseout", upHandler);
                cancelDefault(e);
            });
            addDOMEvent(node.firstChild, "click", function() {
                parent.style.display = "none";
            });
        })(node, parent);
    }
});

/**
 * Adds a DOM event listener to the onclick event of the "Close" icon in the
 * right top corner of a popup window.
 * @param {Object} popupwindow The DOM node of the popup window.
 * @param {Function} onclose The callback function which will be called when
 * the popup window is closed by clicking on the "Close" icon.
 */
function addOnClose(popupwindow, onclose) {
	addDOMEvent(popupwindow.getElementsByTagName('img')[1], "click", onclose);
}

/**
 * Removes a previously added DOM event listener from the onclick event of
 * the "Close" icon in the rihgt top corner of a popup window.
 * @param {Object} popupwindow The DOM node of the popup window.
 * @param {Function} onclose The callback function which was supplied in
 * a previous call to {@link addOnClose}.
 */
function removeOnClose(popupwindow, onclose) {
    removeDOMEvent(getElement(popupwindow.firstChild).firstChild, "click", onclose);
}

/**
 * @class The common class for all popups
 * @param {DOMNode} node The DOM node which will be displayed and hidden.
 * The opacity effect works only if the node is positioned by CSS.
 */
function Popup(node) {
    this.node = node;
    body.appendChild(this.node);
}

/** @private */
Popup.stopAnimation = null;

Popup.prototype = {
    show: function() {
        if (this.isVisible) return;
        this.isVisible = true;
        if (Popup.stopAnimation) Popup.stopAnimation();
        var style = this.node.style;
        if(configGetKey("gui.effects.fading")) {
            Popup.stopAnimation = animate(300, 1,
                function(value) {
                    style.opacity = value;
                    style.filter = "alpha(opacity=" + value * 100 + ")";
                }, function() {
                    style.opacity = "";
                    style.filter = "";
                    Popup.stopAnimation = null;
                });
        }
        style.display = "";
        hideIFrames();
    },
    
    hide: function() {
        if (!this.isVisible) return;
        this.isVisible = false;
        if (Popup.stopAnimation) Popup.stopAnimation();
        this.node.style.display = "none";
        showIFrames();
    },
    
    /**
     * Indicates whether the popup is currently displayed.
     */
    isVisible: false,
    
    /**
     * Positions the popup so that it is fully visible. By default, the popup is
     * positioned with its top left corner at the specified desired coordinates.
     * If the popup would not be fully visible because it would extend beyond
     * the right or the bottom edge of the browser window, the right or
     * the bottom edge of the popup is placed at the specified coordinates
     * instead of the left or the top edge, respectively.
     * All coordinates are given in px relative to the document body.
     * @param {Number} x The preferred X coordinate of the left popup edge.
     * @param {Number} y The preferred Y coordinate of the top popup edge.
     * @param {Number} w The distance to add to the desired X coordinate if
     * the right popup edge is used instead of the left edge. Defaults to 0.
     * A positive value will keep a rectangle at x to x + w always covered,
     * a negative value will keep a rectangle at x + w to x always cleared.
     * @param {Number} h The distance to add to the desired Y coordinate if
     * the bottom popup edge is used instead of the top edge. Defaults to 0.
     * A positive value will keep a rectangle at y to y + h always covered,
     * a negative value will keep a rectangle at y + h to y always cleared.
     */
    position: function(x, y, w, h) {
        if (x + this.node.offsetWidth > body.offsetWidth)
            x = x + (w || 0) - this.node.offsetWidth;
        if (y + this.node.offsetHeight > body.offsetHeight)
            y = y + (h || 0) - this.node.offsetHeight;
        this.node.style.left = x + "px";
        this.node.style.top = y + "px";
    }
};

/**
 * @class The common class for all popups which disappear when the user clicks
 * somewhere else.
 * @param {DOMNode} node The DOM node which will be displayed and hidden.
 * @augments Popup
 */
function MousePopup(node) {
    Popup.call(this, node);
    var Self = this;
    if (document.addEventListener) {
        this.clickHandler = function(e) {
            if (Self.captureEvents(e.target)) stopEvent(e);
        };
    } else {
        var recursion = {};
        this.clickHandler = function(e) {
            if (recursion[e.type]) return;
            if (!Self.captureEvents(e.srcElement)) Self.mouseHandler(e);
        };
        this.mouseHandler = function(e) {
            if (recursion[e.type]) return;
            recursion[e.type] = true;
            if (MousePopup.active) {
                body.onlosecapture = null;
                body.releaseCapture();
            }
            switch (e.type) {
                case "click":
                    e.srcElement.click();
                    break;
                default:
                    e.srcElement.fireEvent("on" + e.type);
            }
            if (MousePopup.active) {
                body.onlosecapture = MousePopup.hideActive;
                body.setCapture();
            }
            recursion[e.type] = false;
        };
    }
}

/**
 * The number of currently displayed MousePopups.
 * @type Number
 */
MousePopup.active = 0;

MousePopup.prototype = extend(Popup, {

    show: function() {
        Popup.prototype.show.call(this);
        if (!MousePopup.active && "Hover" in window && Hover.current)
            Hover.current.hide();
        this.registerHide();
        MousePopup.active++;
    },

    hide: function() {
        // check if the pop-up is visible. otherwise MousePopup.active gets into
        // an invalid state and hovers do not work any longer
        if (this.isVisible) {
            Popup.prototype.hide.call(this);
            MousePopup.active--;
            this.unregisterHide();
        }
    },

    /**
     * Decides what to do with captured events while the popup menu is
     * displayed.
     * @param {DOMNode} t the target of the captured event.
     * @type Boolean
     * @return Whether to prevent the bubbling and the default action of
     * the event.
     */
    captureEvents: function(t) {
        while (t && !t.oxPopupMenu) t = t.parentNode;
        if (!t) this.hide();
        return false;
    }

});

if (document.addEventListener) {
    MousePopup.prototype.registerHide = function() {
        body.parentNode.addEventListener("click", this.clickHandler, true);
        body.parentNode.addEventListener("contextmenu", this.clickHandler,
            true);
        body.parentNode.addEventListener("mousedown", this.clickHandler, true);
    };
    MousePopup.prototype.unregisterHide = function() {
        body.parentNode.removeEventListener("click", this.clickHandler, true);
        body.parentNode.removeEventListener("contextmenu", this.clickHandler,
            true);
        body.parentNode.removeEventListener("mousedown", this.clickHandler,
            true);
    };
} else {
    MousePopup.activePopups = {};
    MousePopup.nextID = 0;
    MousePopup.hideActive = function() {
        for (var i in MousePopup.activePopups)
            MousePopup.activePopups[i].hide();
    };
    MousePopup.prototype.registerHide = function() {
        if (!MousePopup.active) {
            addDOMEvent(body, "click", this.clickHandler);
            addDOMEvent(body, "mousedown", this.clickHandler);
            addDOMEvent(body, "mouseover", this.mouseHandler);
            addDOMEvent(body, "mouseout", this.mouseHandler);
            body.onlosecapture = MousePopup.hideActive;
            body.setCapture();
        }
        if (!this.activeID) this.activeID = ++MousePopup.nextID;
        MousePopup.activePopups[this.activeID] = this;
    };
    MousePopup.prototype.unregisterHide = function() {
        if (!MousePopup.active) {
            removeDOMEvent(body, "click", this.clickHandler);
            removeDOMEvent(body, "mousedown", this.clickHandler);
            removeDOMEvent(body, "mouseover", this.mouseHandler);
            removeDOMEvent(body, "mouseout", this.mouseHandler);
            body.onlosecapture = null;
            body.releaseCapture();
        }
        delete MousePopup.activePopups[this.activeID];
    };
}

/**
 * @class The common class for all popup menus.
 * @augments Popup
 */
function PopupMenu() {
    MousePopup.call(this, newnode("div", {
        position: "absolute",
        display: "none"
    }, {
        className: "PopupMenuDiv background-color-popup-menu border-color-popup-menu font-size-small",
        oxPopupMenu: true
    }));
    this.items = [];
}

/**
 * Delay for displaying and hiding of sub-menus, in milliseconds.
 * @type Number
 * @default 100
 */
PopupMenu.timeout = 100;

/**
 * @private
 */
PopupMenu.getTimeout = function() { return PopupMenu.timeout; };

/**
 * An optional callback which is called when the menu is displayed.
 * It can be used to adjust the menu by disabling or hiding certain menu items.
 * @function
 * @name PopupMenu.prototype.onShow
 */

/**
 * An optional callback which is called when the menu is hidden.
 * It can be used to release any resources acquired in onShow.
 * @function
 * @name PopupMenu.prototype.onHide
 */

PopupMenu.prototype = extend(MousePopup, {
    /**
     * Displays the popup menu.
     * @protected
     */
    show: function() {
        if (this.isVisible) return;
        if (this.onShow) this.onShow();
        MousePopup.prototype.show.call(this);
        for (var i in this.items) this.items[i].show();
        PopupMenu.last = this;
    },
    
    hide: function() {
        if (!this.isVisible) return;
        MousePopup.prototype.hide.call(this);
        for (var i in this.items) this.items[i].hide();
        if (this.onHide) this.onHide();
    },
    
    close: function() {
        PopupMenu.last = null;
        this.hide();
        if (this.parent instanceof MenuItem) this.parent.close();
    },

    addItem: function(item) {
        this.items.push(item);
        this.node.appendChild(item.node);
        item.parent = this;
        if (item.icon) this.iconAdded();
        if (item.submenu) this.submenuAdded();
    },
    
    removeItem: function(item) {
        for (var i = 0; i < this.items.length; i++) {
            if (this.items[i] == item) {
                this.items = this.items.splice(i, 1);
                this.node.removeChild(item.node);
                break;
            }
        }
    },
    
    removeAllItems: function() {
        for (var i = 0; i < this.items.length; i++) 
            this.node.removeChild(this.items[i].node);
        this.items = [];
    },
    
    itemSelected: function(e) {
        for (var i in this.items) this.items[i].siblingSelected(e);
    },
    
    /**
     * Returns the context of the popup menu.
     */
    getContext: function() { return this.parent.getContext(); },
    
    /**
     * @ignore
     */
    iconAdded: function() { PopupMenu.setIconClass(this.node, true); },
    
    /**
     * @ignore
     */
    submenuAdded: function() { PopupMenu.setSubmenuClass(this.node, true); }
    
});

/**
 * @private
 */
PopupMenu.setIconClass = classNameSetter("popup-menu-icons");

/**
 * @private
 */
PopupMenu.setSubmenuClass = classNameSetter("popup-menu-submenus");

/**
 * @class A menu item for PopupMenu and its descendants.
 * @param {I18nString} text The text of the menu item.
 * @param {Function or PopupMenu} action Either a callback function which is
 * called when the menu item is activated, or a PopupMenu object which is
 * displayed as a submenu.
 */
function MenuItem(text, action) {
    if (action instanceof PopupMenu) {
        this.submenu = action;
        action.parent = this;
    } else
        this.action = action;
    var Self = this;
    this.node = newnode("div", { position: "relative" },
        { className: "PopupEntryDiv" },
        [newnode("span", 0, 0, [addTranslated(text)])]);
    addDOMEvent(this.node, "click", function(e) {
        if (Self.checkbox) {
            var target = e.target || e.srcElement;
            if (target == Self.checkbox) {
                cancelBubbling(e);
                handleClick();
            } else {
                stopEvent(e);
                Self.checkbox.click();
            }
        } else {
            stopEvent(e);
            handleClick();
        }
        function handleClick() {
            if (Self.enabled) {
                if (Self.action && Self.action() !== false)
                    Self.parent.close();
            } else {
                if (Self.disabled_action && Self.disabled_action() !== false)
                    Self.parent.close();
            }
        }
    });
    with (FSM) var transitions = [
        Trans("start", "selected", Event(this.node, "mouseover", select)),
        Trans("selected", "start", Event(this.node, "mouseout",
            MouseOut(deselect)))
    ];
    if (this.submenu) {
        with (FSM) {
            var deselection = Trans("both", "open", Callback(deselect));
            transitions = transitions.concat([
                Trans("selected", "both", Timeout(PopupMenu.getTimeout, open)),
                deselection,
                Trans("open", "both", Event(this.node, "mouseover", select)),
                Trans("open", "start", Timeout(PopupMenu.getTimeout, close))
            ]);
        }
        this.node.appendChild(newnode("img", IE < 8 ? 0 : {
            position: "absolute", right: 0, top: 0, bottom: 0, margin: "auto", width: "4px", height: "7px"
        }, { src: getFullImgSrc("/img/arrows/arrow_darkgrey_right.gif") }));
    }
    this.fsm = FSM("start", transitions);
    if (this.submenu) this.siblingSelected = deselection.fire;

    function select() {
        Self.parent.itemSelected();
        if (!Self.enabled) return false;
        Self.node.className =
            "PopupEntryDivActive border-color-PMG-selection-elements";
    }
    function deselect() { Self.node.className = "PopupEntryDiv"; }
    function open() {
        Self.submenu.show();
        var pos = getAbsolutePosition(Self.node);
        Self.submenu.position(pos.x + Self.node.offsetWidth, pos.y,
                              -Self.node.offsetWidth, Self.node.offsetHeight);
    }
    function close() { Self.submenu.hide(); }
}

MenuItem.prototype = {

    enabled: true,
    
    enable: function() { this.setEnabled(true); },
    
    disable: function() { this.setEnabled(false); },
    
    setEnabled: function(enabled) {
        this.enabled = enabled;
        this.node.className = this.enabled ? "PopupEntryDiv"
                                           : "PopupEntryDivDisabled font-color-disabled";
        if (this.img) this.img.src = this.getIcon();
    },
    
    siblingSelected: emptyFunction,
    
    setIcon: function(icon, disabled_icon) {
        this.icon = icon;
        this.disabled_icon = disabled_icon;
        if (icon) {
            if (this.img) {
                this.img.src = this.getIcon();
            } else {
                this.img = newnode("img", {
                    position: "absolute", left: 0, top: 0, bottom: 0,
                    margin: "auto"
                }, { src: this.getIcon() });
                this.node.insertBefore(this.img, this.node.firstChild);
            }
            if (this.parent) this.parent.iconAdded();
        } else if (this.img) {
            this.node.removeChild(this.img);
            delete this.img;
        }
        return this;
    },
    
    getIcon: function() {
        return getFullImgSrc(this.enabled ? this.icon
                                          : (this.disabled_icon || this.icon));
    },
    
    setChecked: function(checked) {
        if (this.checkbox) {
            this.checkbox.checked = checked;
        } else {
            this.checkbox = newnode("input", 0,
                { type: "checkbox", className: "noborder", checked: checked });
            this.node.insertBefore(this.checkbox, this.node.firstChild);
        }
        return this;
    },
    
    getChecked: function() {
        return this.checkbox && this.checkbox.checked;
    },
    
    show: function() {
        if (this.onShow) this.onShow();
        this.fsm.reset();
        this.node.className = this.enabled ? "PopupEntryDiv"
                                           : "PopupEntryDivDisabled font-color-disabled";
        if (this.img) this.img.src = this.getIcon();
    },

    hide: function() {
        if (this.submenu) this.submenu.hide();
        this.fsm.disable();
    },
    
    close: function() { this.parent.close(); },
    
    visible: true,
    
    setVisible: function(visible) {
        this.visible = visible;
        this.node.style.display = visible ? "" : "none";
    },
    
    /**
     * The current waiting status.
     * @type Boolean
     */
    waiting: false,
    
    /**
     * Sets the waiting status.
     * When waiting, the menu item is shown as disabled, with an animated icon.
     * @param {Boolean} waiting The new waiting status.
     */
    setWaiting: function(waiting) {
        if (waiting == this.waiting) return;
        this.waiting = waiting;
        if (waiting) {
            this.waitEnabled = this.enabled;
            this.setEnabled(false);
            this.waitSetEnabled = this.setEnabled;
            this.setEnabled = function(enabled) { this.waitEnabled = enabled; };
            this.waitIcon = this.icon;
            this.waitDisabledIcon = this.disabled_icon;
            this.setIcon("img/ox_animated_withoutbg.gif");
        } else {
            this.setEnabled = this.waitSetEnabled;
            this.setEnabled(this.waitEnabled);
            this.setIcon(this.waitIcon, this.waitDisabledIcon);
            delete this.waitEnabled;
            delete this.waitSetEnabled;
            delete this.waitIcon;
            delete this.waitDisabledIcon;
        }
    },
    
    /**
     * Configures the menu item to automatically enter the waiting state when
     * the enabled status cannot be computed from
     * @param {Function} enableFunction A function which computes the enabled
     * state of this menu item from Value objects.
     */
    setEnabledF: function(enableFunction) {
        var Self = this;
        Value.eval(enableFunction, function(wait, enable) {
            if (!wait) Self.setEnabled(enable);
            Self.setWaiting(wait);
        });
    },

    /**
     * Automatically disables the menu item if its submenus do not have a single
     * enabled option.
     */
    autoSetEnabled: function() { this.setEnabled(this.getAutoEnabled()); },
    
    getAutoEnabled: function() {
        if (!this.submenu) return this.enabled && this.visible;
        for (var i = 0; i < this.submenu.items.length; i++) {
            var item = this.submenu.items[i];
            if (item.getAutoEnabled && item.getAutoEnabled()) return true;
        }
        return false;
    },
    
    /**
     * Returns the context in which this item is used. Usually, this is
     * the current selection for which a context menu is displayed.
     */
    getContext: function() { return this.parent.getContext(); }
    
};

/**
 * Creates a new dynamic value.
 * A dynamic value is a special object which works like a variable which
 * contains the results of an asynchronous request and automatically updates
 * calculations in which it is used when the request completes. The reason for
 * using dynamic values instead of the Join class is that the results may be
 * computed before the last request completes because some values are not needed
 * e. g. (a && b) is false when a is false, regardless of the value of b.
 *
 * New dynamic values are created by calling the function Value():
 * <code>var x = Value(), y = Value();</code>
 * They can then be used in calculations by appending "()" to the variable
 * names:
 * <code>function calculation() { return x() > 3 && y() < 9; }</code>
 * To enable automatic updates, the calculation function and a callback function
 * which reacts to updates, are passed to Value.eval():
 * <code>Value.eval(calculation, function(wait, result) { ... });</code>
 * The callback function takes two parameters. The first parameter (wait) is
 * true if the calculation could not be completed because some required dynamic
 * values didn't receive their values yet. Otherwise, if the first parameter is
 * false, then the second parameter (result) contains the result of
 * the calculation.
 *
 * When a dynamic value becomes outdated, e. g. just before a new request is
 * started to receive the new value, the old value must be cleared by calling
 * the clear() method. When the request completes, the new value is set by
 * calling the set() method:
 * <code>
 * x.clear();
 * ox.JSON.get(..., function(data) { x.set(...); });
 * </code>
 */
function Value() {
    var f = function() {
        var e = Value.evaluations[Value.evaluations.length - 1];
        f.callbacks[e.id] = e.callback;
        if ("value" in f) return f.value; else throw Value.WaitException;
    };
    f.callbacks = {};
    f.set = function(value) {
        if (this.value == value) return;
        this.value = value;
        this.update();
    };
    f.clear = function() {
        delete this.value;
        this.update();
    };
    f.update = function() {
        var callbacks = this.callbacks;
        this.callbacks = {};
        for (var i in callbacks) callbacks[i]();
    };
    return f;
};

Value.WaitException = {};

Value.id = 0;

Value.evaluations = [];

/**
 * Creates an automatically updated calculation.
 * @param {Function} f A calculation function which uses dynamic values.
 * @param {Function} callback A callback function which is called with one or
 * two parameters: The first parameter is true when f throws Value.WaitException
 * (usually a result of attempting to evaluate an empty dynamic value).
 * The second parameter contains the return value of f and is present only when
 * the first parameter is false.
 */
Value.eval = function(f, callback) {
    var updatePending = false;
    function delayUpdate() {
        if (!updatePending) {
            updatePending = true;
            setTimeout(update, 0);
        }
    }
    function update() {
        updatePending = false;
        Value.evaluations.push({ id: Value.id++, callback: delayUpdate });
        try {
            var result = f();
            Value.evaluations.pop();
            callback(false, result);
        } catch (e) {
            Value.evaluations.pop();
            if (e == Value.WaitException) callback(true); else throw e;
        }
    }
    update();
};

/**
 * A separator for popup menus. Can be used as a MenuItem most of the time, but
 * the only available method is setVisible.
 */
function MenuSeparator() {
    this.node = newnode("div",
        { border: "1px inset gray", margin: "0.6em 0" });
    var Self = this;
    addDOMEvent(this.node, "mouseover",
        function() { Self.parent.itemSelected(); });
}

MenuSeparator.prototype = {
    siblingSelected: emptyFunction,
    show: emptyFunction,
    hide: emptyFunction,
    setVisible: function(visible) {
        this.visible = visible;
        this.node.style.display = visible ? "" : "none";
    }
};

/**
 * @augments PopupMenu
 */
function ContextMenu() { PopupMenu.call(this); }

ContextMenu.prototype = extend(PopupMenu, {

    /**
     * Displays the context menu at the specified coordinates.
     * @param {Number} x The X coordinate of the menu (usually Event.clientX).
     * @param {Number} y The Y coordinate of the menu (usually Event.clientY).
     * @param context The context which is passed to all children of the menu.
     */
    display: function(x, y, context) {
        this.context = context;
        this.show();
        this.position(x, y);
    },

    getContext: function() { return this.context; }

});

/**
 * @augments PopupMenu
 * @param {DOMNode} trigger The DOM node which will trigger the display of
 * the menu. The menu is displayed below the DOM node.
 */
function PulldownMenu(trigger) {
    PopupMenu.call(this);
    this.trigger = trigger;
    var Self = this;
    addDOMEvent(trigger, "click", function(e) {
        if (PopupMenu.last == Self) PopupMenu.last = null; else Self.show();
        cancelBubbling(e);
    });
    trigger.oxPulldownTrigger = true;
}

PulldownMenu.prototype = extend(PopupMenu, {

    show: function() {
        this.node.style.minWidth = this.trigger.offsetWidth + "px";
        PopupMenu.prototype.show.call(this);
        var pos = getAbsolutePosition(this.trigger);
        this.position(pos.x, pos.y + this.trigger.offsetHeight,
                      this.trigger.offsetWidth, -this.trigger.offsetHeight);
    },
    
    captureEvents: function(t) {
        while (t && !t.oxPopupMenu && !t.oxPulldownTrigger) t = t.parentNode;
        if (!t || t == this.trigger) {
            this.hide();
            return true;
        } else if (t.oxPulldownTrigger) {
            this.hide();
        }
        return false;
    }

});
var corewindow=window;
function configGetKey(key) {
	var splitkey=key.split(".");
	if (!window.config) return null;
	var tmp=(corewindow) ? corewindow.config : config;
	for(var i=0;i<splitkey.length;i++) {
		if(tmp[splitkey[i]] != undefined) {
			tmp=tmp[splitkey[i]];
		} else {
			return null;
		}
	}
	return clone(tmp);
}

function configSetKey(key,value) {
	if(!corewindow.config) {
		corewindow.config=new Object();
	}
	var tmp=(corewindow) ? corewindow.config : config;
	var splitkey = key.split(".");
	for(var i=0;i<splitkey.length-1;i++) {
		if(tmp[splitkey[i]]) {
			tmp=tmp[splitkey[i]];
			if(typeof(tmp) != "object") {
				alert(""+tmp+" parentNode is a value");
				return;
			}
		} 
		else {
			tmp[splitkey[i]]=new Object();
			tmp=tmp[splitkey[i]];				
		}
	}
	tmp[splitkey[splitkey.length-1]]=clone(value);
	configuration_changed_outside=true;
	triggerEvent("OX_Configuration_Parameter_Changed",key,value);	
}

function configContainsKey(key) {
	var splitkey=key.split(".");
	if(!config) {
		return false;
	}
	var tmp=(corewindow) ? corewindow.config : config;
	for(var i=0;i<splitkey.length;i++) {
		if(tmp[splitkey[i]] != undefined ) {
			tmp=tmp[splitkey[i]];
		} else {
			return false;
		}
	}
	return true;
}

var angle = 0;
var dir = 0.01;
var left, right;
var cubetimeout;
function startrotate(node1,node2,direction) {
	if(cubetimeout) {
		clearTimeout(cubetimeout);
	}	
	if(configGetKey("gui.effects.global")) {
		var a = Math.PI / 4, b = Math.PI / 2;
		cubetimeout=setTimeout(function() {
			if(node1 && node2 && node1.id==node2.id) return;
			if(direction >= 0) {left = node1; right = node2; }
			else { right = node1; left = node2; }
			dir = (direction >= 0) ? 0.005 : -0.005;
			angle = (direction >=0) ? 0 : b;
			rotate(direction);
		},1200);
	} else {
		if(node1) {	
			node1.style.left = Math.round(24 - 24 * Math.cos(Math.PI / 4))+  "px";	
			node1.style.width = "0px";
		}
		if(node2) {
			node2.style.left = Math.round(24 - 24 * Math.cos(Math.PI / 4) )+ "px";
			node2.style.width = "34px";
		}	
	}
}


function rotate(direction) {
	var a = Math.PI / 4, b = Math.PI / 2; 	
	var start = (new Date()).getTime();
	setTimeout(function() {
		var now = (new Date()).getTime();
		angle += dir * (now - start);
		if (dir > 0 && angle >= b) {
			angle = b;
		} else if (dir < 0 && angle < 0) {
			angle = 0;
		} else
			rotate(direction);
		if(left) {	
			left.style.left = Math.round(24 - 24 * Math.cos(angle - a) )+  "px";	
			left.style.width = Math.round(34 * Math.cos(angle)) + "px";
		}
		if(right) {
			right.style.left = Math.round(24 + 24 * Math.cos(angle + a) )+ "px";
			right.style.width = Math.round(34 * Math.sin(angle)) + "px";
		}
	}, 0);
}

/******************************************************/
/*** Find as you type                              ****/
/******************************************************/
register("Loaded",function () {
	if($("menu_search_global_searchfield")) {
		var d=new PreSearch($("menu_search_global_searchfield"));
		d.addEvents();
	} 
	if($("menu_search_mail_searchfield")) {
		var d2=new PreSearch($("menu_search_mail_searchfield"));
		d2.addEvents();
	}
});
PreSearch = function (searchfield) {
	var Self=this;
	this.keytimeout;
	this.actualposition=-1;
	this.searchfield=searchfield;
	this.mycontent=newnode("div",null,{ className : "presearchcontent" },[]);
	this.mycontent.style.display="none";
	this.mycontent.onselectstart = function () { var tmp=menuselectstart ; menuselectstart = false; return tmp; }  
	this.mycontent.onmousedown = function () { var tmp=menumousedown ; menumousedown = false; return tmp; }
	this.popup = new MousePopup(this.mycontent);
}
PreSearch.prototype.close = function () {
    this.popup.hide();
}
PreSearch.prototype.open = function () {
	this.popup.show();
}
PreSearch.prototype.addEvents = function () {
	var Self=this;
	addDOMEvent(this.searchfield,"keydown",Self.addKeyDown()); 	
}
PreSearch.prototype.nextContent = function(direction) {
	if(direction) {
		this.actualposition++;
		this.actualposition=this.setSelected(this.actualposition);
	} else {
		this.actualposition--;
		this.actualposition=this.setSelected(this.actualposition);
	}
}
PreSearch.prototype.addKeyDown = function () {
	var Self=this;
	return function(evt) {
		if(Self.keytimeout) {
			clearTimeout(Self.keytimeout);
		}
		
		if (!evt) evt = window.event;
		var a = evt.keyCode;
		switch (a) {
			case 27: //ESC
				Self.actualposition=-1;
				Self.close();
				stopEvent(evt); 
				return false;
				break;
			case 13: case 9: // ENTER
				if(Self.actualposition!=-1) {
					if(Self.mycontent.childNodes[Self.actualposition]) {
						var myelem=Self.mycontent.childNodes[Self.actualposition];					
 						Self.searchfield.value=myelem.oxvalue;
					} 
				}
				Self.close();
				break;
			case 38:	// UP
				Self.nextContent(false);
				break;
			case 40:	// DOWN
				Self.nextContent(true);
				break;
		
			default:
				Self.actualposition=-1;
				Self.keytimeout=setTimeout(function() {
					var newpresearch=Self.searchfield.value;
					Self.presearch(newpresearch);
				},500);
				break;
		}
	}
}
PreSearch.prototype.presearch = function (searchstring) {
	var Self=this;
	removeChildNodes(this.mycontent);
	if(searchstring.length > 2)  {
		this.mycontent.appendChild(
			newnode("div", { textAlign: "center", width: "100%", padding: "1px" }, null, [ 
				newnode("img",null,{src: getFullImgSrc("img/ox_animated_withoutbg.gif")},[])
			])
		);
		this.mycontent.style.left=getAbsolutePositionLeft(this.searchfield.parentNode.parentNode.parentNode.parentNode.parentNode)+"px";
		this.mycontent.style.top=(getAbsolutePositionTop(this.searchfield.parentNode.parentNode.parentNode.parentNode.parentNode)+this.searchfield.parentNode.parentNode.parentNode.parentNode.offsetHeight)+"px";
		switch(currentpath2[0]) {
			case "calendar":
				Self.open();
				var reqobj = new Object();
				reqobj.pattern = searchstring;
				if(!calendar_getAllFoldersAttribute()) {
					reqobj.folder = activefolder;
				}
				new JSON().put(AjaxRoot + "/calendar?session=" + session + "&action=search&columns=200",
				reqobj, null, 
				function(cb) {
					Self.refill("calendar", cb.data);
				});
				break;
			case "contacts":
				Self.open();
                var reqobj = {
                    display_name: searchstring, first_name: searchstring,
                    last_name: searchstring, email1: searchstring,
                    email2: searchstring, email3: searchstring,
                    orSearch: true, folder: activefolder
                };
				new JSON().put(AjaxRoot + "/contacts?session=" + session + "&action=search&columns=500,501,502,20,1",
				reqobj, null, 
				function(cb) {
					Self.refill("contacts", cb.data);
				});
				break;
			case "infostore":
				Self.open();		
				var reqobj = new Object();
				reqobj.pattern = searchstring;
				reqobj.folder = activefolder;
				(new JSON()).put(AjaxRoot + "/infostore?session=" + session + "&action=search&columns=700",
				reqobj, null, 
				function(cb) {
					Self.refill("infostore", cb.data);
				});
				break;
			case "tasks":				
				Self.open();
				var reqobj = new Object();
				reqobj.pattern = "*"+searchstring+"*";
				reqobj.folder = activefolder;
				(new JSON()).put(AjaxRoot + "/tasks?session=" + session + "&action=search&columns=200",
				reqobj, null, 
				function(cb) {
					Self.refill("tasks", cb.data);
				});
				break;
			case "mail":
				var column="";
				var searchcolumn=menumailsearchcontents[mailsearchcontent];
				if(OXMailMapping.stringmapping[searchcolumn]) { 
					searchcolumn = OXMailMapping.stringmapping[searchcolumn];
					column=""+searchcolumn;
				} else { 
					break;
				}
				Self.open();
				var req=""+AjaxRoot + '/mail?action=search&session='+session+'&columns='
					+ column + '&folder=' + encodeURIComponent(activefolder);
				
				var reqobj = new Array();
				var oObj = new Object();
				oObj.pattern = searchstring;
				oObj.col=searchcolumn;
				reqobj.push(oObj);
				if(reqobj.length<=0) { return; }
				new JSON().put(req,reqobj, null, 
				function(cb) {
					var newarray = new Array();
					function createEntry(entry) {
						if(entry) {
							for(var i2=0;i2<entry.length;i2++) {
								var mystring=entry[i2].join("");
								if(mystring.toLowerCase().indexOf(searchstring.toLowerCase())!=-1) { 
									if(entry[i2][0] && entry[i2][0]!="") {
										var tmp3=entry[i2][0];
										if(tmp3.charAt(0)=="\"" && tmp3.charAt(tmp3.length-1) == "\"") {
											tmp3=tmp3.substring(1,tmp3.length-1);
										}
										newarray.push([tmp3]);
									} else {
										newarray.push([entry[i2][1]]);
									}
								}
							}
						}
					} 
					if (activemodule=="mail" && (menumailsearchcontents[mailsearchcontent] == "from" || menumailsearchcontents[mailsearchcontent] == "to" || menumailsearchcontents[mailsearchcontent] == "cc")) {
						for (var i=0; i<cb.data.length; i++) {
							createEntry(cb.data[i][0]);
						}
						Self.refill("mail", newarray);	
					} else {
						Self.refill("mail", cb.data);
					}
					
				});
				break;
			default:
				Self.close();
		}	
	} else {
		Self.close();
	}	
}
PreSearch.prototype.refill = function (module, dataarray) {
	var Self=this;
	var tmpobject=new Object();
	var counter=0;
	if(dataarray.length==0) {
		removeChildNodes(this.mycontent);
		this.popup.hide();
	} else {
		removeChildNodes(this.mycontent);
		for (var  i = 0; i < dataarray.length; i++ ) {
			if(!tmpobject[dataarray[i][0]]) {
				tmpobject[dataarray[i][0]]=true;
				counter++;
				if(counter>5) break;
				var nDiv = newnode("div", { overflow: "hidden", whiteSpace: "nowrap", padding: "1px" }, []);
				switch (module) {
					case "contacts":
						var fn =  dataarray[i][2] || "";
						var sn =  dataarray[i][1] || "";
						shownStr = dataarray[i][0] || (sn + ", " + fn);
						break;
					default:
						shownStr = dataarray[i][0];
				}
				nDiv.appendChild(document.createTextNode(shownStr));
				addDOMEvent(nDiv,"mouseover",function(e) { this.className = "background-color-PMG-selection-elements";});
				addDOMEvent(nDiv,"mouseout",function(e) { 
					if (Self.actualposition != this.position) {
						this.className = "background-color-content";
					}
				});
				nDiv.oxvalue=dataarray[i][0];
				nDiv.position=i;
				Self.actualposition=-1;
				addDOMEvent(nDiv,"mousedown",function(e) {
					var myelem=e.currentTarget || e.srcElement;					
 					Self.searchfield.value=myelem.oxvalue; 
					Self.popup.hide(); 
					if (activemodule=="mail") {
						triggerEvent("OX_New_Search",activemodule,Self.searchfield.value,menumailsearchcontents[mailsearchcontent]);
					} else {
						triggerEvent("OX_New_Search",activemodule,Self.searchfield.value);
					}
				});
			}
			if(counter>5) break;
			this.mycontent.appendChild(nDiv);	
		}
	} 
}
PreSearch.prototype.setSelected = function (int1) {
	var Self=this;
	var childs=this.mycontent.childNodes;
	if(int1<0) { int1=0; }
	if(int1>=childs.length) { int1=childs.length-1; }
	for(var i=0;i<childs.length;i++) {
		if(i==int1) {
			childs[i].className = "background-color-PMG-selection-elements";
		} else {
			childs[i].className = "background-color-content";
		}
	}
	return int1;
}

function Accordion(maxheight,width,hover) {
	this.hover=hover;
	this.speed=12;
	this.intervalcounts=0;
	this.interval=10;
	this.actualcontent=0;
	this.components=new Object();
	this.maxheight=maxheight;
	this.width="100%";
	if(width) { this.width = width +"px" }
	this.container=newnode("div",{ width: this.width },{},[]);

}
Accordion.prototype.addComponent = function(title,node,maximumheight) {
	if(this.components[title]) {
		this.container.removeChild(this.components[title].content);
	}
	this.components[title]= {"title" : title , "content" : node, maximumheight : maximumheight };
	node.style.height="0px";
	var height=Accordion.getHeight(node,this,maximumheight);
	this.components[title].maxheight=height;
	this.container.appendChild(node);
	this.checkHeight();	
}
Accordion.prototype.checkComponentHeight = function(title) {
	if(this.components[title]) {
		this.components[title].maxheight=Accordion.getHeight(this.components[title].content,this,this.components[title].maximumheight);
		this.checkHeight();
	}
}
Accordion.prototype.removeComponent=function(title) {
	if(this.components[title]) {
		try {
			this.container.removeChild(this.components[title].content)
		} catch(e) {}
		delete this.components[title];
	}
}
Accordion.prototype.clear=function() {
	this.components=new Object();
	this.getDomNode();
}
Accordion.prototype.inTree = function() {
	var node=this.container;
	while(node) {
		if(node==$("body")) { return true; }
		node=node.parentNode;
	}
}
Accordion.prototype.setHover = function(hover) {
	this.hover=hover;
}
Accordion.prototype.checkHeight = function() {

	if(this.intervalcounts==0) {
	
		if(this.maxheight && this.maxheight<this.container.offsetHeight) {
			this.container.style.height=this.maxheight+"px";
			this.container.style.overflowX="hidden";
			this.container.style.overflowY="auto";
		} else {
			this.container.style.height="";
		}
		
		if(!this.hover) {
			if(OXAppointmentHover.actualHover) {
				OXAppointmentHover.actualHover.sizeChanged();
			}
		} else {
			this.hover.sizeChanged();
		}
	}
}
Accordion.prototype.getDomNode = function () {
	removeChildNodes(this.container);
	
	var tmpintree=this.inTree();
	if(!tmpintree) { $("body").appendChild(this.container); }
	for(var i in this.components) {
		var tmp=this.components[i];
		this.container.appendChild(tmp.content);
		this.components[i].maxheight=Accordion.getHeight(tmp.content,this);
		this.components[i].content.style.height="0px";
	}
	if(!tmpintree) { $("body").removeChild(this.container); }
	return this.container;
}

Accordion.prototype.showExtraContent = function(number) {
	var tmpcontents=(""+this.actualcontent).split(",");
	tmpcontents.push(number);
	this.showContent(tmpcontents.join(","));
}
Accordion.prototype.hideExtraContent = function(number) {
	var tmpcontents=(""+this.actualcontent).split(",");
	for(var i=0;i<tmpcontents.length;i++) {
		if(tmpcontents[i] == number) {
			tmpcontents.splice(i,1);
			break;
		}
	}
	this.showContent(tmpcontents.join(","));
	
}

Accordion.prototype.showContent = function(number) {
	var Self=this;
	
	var newnumbers=(""+number).split(",");
	var oldnumbers=(""+Self.actualcontent).split(",");
	var oldobject=new Object();
	var newobject=new Object();
	for(var i=0;i<oldnumbers.length;i++) {
		oldobject[oldnumbers[i]]=true;
	}
	for(var i=0;i<newnumbers.length;i++) {
		newobject[newnumbers[i]]=true;
	}
	this.container.style.height="";
	this.container.style.overflow="";
	
	for(var i2 in Self.components) {	
		if(!oldobject[i2]) {
			if(!newobject[i2]) {
				Self.components[i2].content.style.height="0px";
			} else {
				Self.setDisplay(Self.components[i2],true);
			}
		} else {
			if(!newobject[i2]) {
				Self.setDisplay(Self.components[i2],false);			
			} else {
				if(Self.components[i2].content.style.height=="0px") {
					Self.setDisplay(Self.components[i2],true);
				}
				
			} 
		}
	} 
	Self.checkHeight();
	var newnumbers=new Array();
	for(var i in newobject) {
		newnumbers.push(i);
	}
	Self.actualcontent=newnumbers;
}

Accordion.prototype.setDisplay = function(containerobject,direction) {

	var Self=this;
	var fade=configGetKey("gui.effects.fading");
	if(direction) {

		containerobject.content.style.height="0px";
		containerobject.content.style.display="block";
		if(!containerobject.maxheight) { containerobject.tmpheight=0; }
		else { containerobject.tmpheight=containerobject.maxheight; }
		
		if(containerobject.interval) { containerobject.interval(); }
		Self.intervalcounts++;
		function final_cb1() {
			if(fade) {
				containerobject.content.style.opacity = "";
				containerobject.content.style.filter = "";	// IE
			}
			if(containerobject.maximumheight && containerobject.content.offsetHeight>=containerobject.maximumheight) {
				containerobject.content.style.height=containerobject.maximumheight+"px";	
			} else {
				containerobject.content.style.height="";
			}
			containerobject.interval=null;
			Self.intervalcounts--;
			if(fade) {
				Self.checkHeight();
			}
		}
		
		if(configGetKey("gui.effects.global")) {
			containerobject.interval=animate(500, 100,
			function(curr) {
				var tmpheight=containerobject.maxheight || 0 ;
				var nextheight=Math.round(tmpheight* (curr/100));
				if(containerobject.maximumheight && nextheight>containerobject.maximumheight) {
					nextheight=containerobject.maximumheight;
				}
				if(fade) {
					containerobject.content.style.opacity = (curr/100);
					containerobject.content.style.filter = "alpha(style=0,opacity:" +(curr)+ ")";	// IE 
				}
				containerobject.content.style.height = nextheight+"px";
			},final_cb1);
		
		} else {	
			if (containerobject.maximumheight && containerobject.maxheight > containerobject.maximumheight){
				containerobject.content.style.height = containerobject.maximumheight+"px";
			}
			final_cb1();
		}					
	} else {
		if(containerobject.interval) { containerobject.interval(); }

		Self.intervalcounts++;
		function final_cb2() {
			if(fade) {
				containerobject.content.style.opacity = 0;
				containerobject.content.style.filter = "alpha(style=0,opacity:" +(0)+ ")";	// IE
			}
			containerobject.content.style.height="0px";
			containerobject.interval=null;
			Self.intervalcounts--;
			if(fade) {
				Self.checkHeight();
			}
		}
		if(configGetKey("gui.effects.global")) {

			containerobject.interval=animate(500, 100,
			function(curr) {
				var tmpheight=containerobject.maxheight || 0;
				var nextheight=Math.round(tmpheight* ((100-curr)/100));
				if(containerobject.maximumheight && containerobject.maximumheight<=nextheight) { nextheight=containerobject.maximumheight; }
				if(fade) {
					containerobject.content.style.opacity = (curr/100);
					containerobject.content.style.filter = "alpha(style=0,opacity:" +(curr)+ ")";	// IE
				} 
				containerobject.content.style.height = nextheight+"px";
			},final_cb2);	
		} else {
			if (containerobject.maximumheight && containerobject.maxheight > containerobject.maximumheight){
				containerobject.content.style.height = containerobject.maximumheight+"px";
			}		
			final_cb2();
		}
	}
}

Accordion.getHeight = function(node,accordion,maximumheight) {
	
	var tmpheight=node.style.height;
	var tmpnode=node.cloneNode(true);
	tmpnode.style.height="";
	tmpnode.style.zIndex=-1;
	body.appendChild(tmpnode);
	var value=tmpnode.offsetHeight;
	if(maximumheight && value > maximumheight) {
		node.style.overflow="auto";
	} else {
		node.style.overflow="hidden";
	}
	body.removeChild(tmpnode);
	return value;
}

function createHoverContent(innerwidth,headerheight,borderheight,imgsizes, tabnummer) {
	var mywidth=412;
	var innerwidthtop;
	if(!innerwidthtop) { innerwidthtop= 392;}
	var innerwidthbottom;
	if(!innerwidthbottom) { innerwidthbottom= 392;}
	
	if(!innerwidth) { innerwidth= 400;}
	if(!imgsizes) { imgsizes= { t: 9 , tl:10 , tr:10 , r : 6,l : 6 , b :15, bl : 10, br:10} }
	if(!headerheight) { headerheight = 9 }
	if(!borderheight) { borderheight = 15 }
	var mywidthtop=innerwidth+imgsizes.tr+imgsizes.tl;
	var returnobj=new Object();
	var coreelement=newnode("div",{ position: "absolute", width: "414px", zIndex: 100000, left : "300px", top : "100px" , display:"none" },{},[
		
		
		newnode("div",{ position:"relative" , width: "0px", height : ("0px"), whiteSpace: "nowrap"},{},[		   	
			newnode("div",{ position: "absolute", top: "0px", left: "0px", width: "0px" , height: "0px" },{ className : "hover-header-t" },[
				newnode("img",{},{src: getFullImgSrc("img/dummy.gif") },[])
			]),
			newnode("div",{ position:"absolute",top : "0px",left: "0px",width: "0px",height : "0px" },{ className : "hover-header-tr" },[
				newnode("img",{},{src: getFullImgSrc("img/dummy.gif") },[])
			]),
			newnode("div",{ position: "absolute",top : "0px",left: "0px",width: "0px", height: "0px" },{ className : "hover-header-tl" },[
				newnode("img",{},{src: getFullImgSrc("img/dummy.gif") },[])
			]),
			newnode("div",{ position: "absolute", left : "0px" , top: "0px", width: "0px", height: "0px" },{ className : "hover-header-r" },[
				newnode("img",{},{src: getFullImgSrc("img/dummy.gif") },[])
			]),
			newnode("div",{ position: "absolute", left : "0px", top: "0px", width: "0px", height: "0px" },{ className : "hover-header-l" },[
				newnode("img",{},{src: getFullImgSrc("img/dummy.gif") },[])
			]),
			newnode("div",{ position: "absolute", left: "0px", top: "0px" , width:"0px" , height: "0px" },{ className : "hover-header-color" },[
				newnode("img",{},{src: getFullImgSrc("img/dummy.gif") },[])
			])
			
		]),
			
		newnode("div",{ position:"relative",overflow: "hidden", width: "414px" },{},[
			
			
			newnode("div", { marginLeft: (imgsizes.l +2) + "px"  , width: (innerwidth)+"px" ,overflow:"hidden", zIndex:"1" },{ className : "hover_background" },[
				newnode("div", { height: "100%", width: "100%" ,overflow:"hidden" },null)		
			]),
		    	
			newnode("div",{ position:"absolute",left : "408px", top:"23px",height: "1000px",width: "8px" },{ className : "hover-body-r" },[
				newnode("img",{},{src: getFullImgSrc("img/dummy.gif") })
			]),
			
			newnode("div",{ position:"absolute",left : "0px", top:"23px",height: "1000px",width: "8px" },{className : "hover-body-l" },[
				newnode("img",{},{src: getFullImgSrc("img/dummy.gif") })
			]),
			
			newnode("div",{ position: "absolute",top : "0px",left: "0px",width: (imgsizes.tl+"px"), height: ("23px") },{ className : "hover_master_background hover-header-tab-tl_ac" , id : tabnummer+"hover-tabbin-corner-l" },[
				newnode("img",{},{src: getFullImgSrc("img/dummy.gif") })
			]),
			newnode("div",{ position:"absolute",top : "0px",left: "406px",width: "10px",height : "23px"},{ className : "hover_master_background hover-header-tab-tr", id : tabnummer+"hover-tabbin-corner-r" },[
				newnode("img",{},{src: getFullImgSrc("img/dummy.gif") })
			])	

		]),
		newnode("div",{ position: "relative",width: mywidth+"px",height : (borderheight+"px")},{},[
			newnode("div",{ position: "absolute",left : "406px", top:borderheight-imgsizes.b+"px" , width: (imgsizes.br+"px"), height: (imgsizes.b +"px") },{ className : "hover_master_background hover-bottom-br" },[
				newnode("img",{},{src: getFullImgSrc("img/dummy.gif") },[])
			]),
			newnode("div",{ position: "absolute",left : "0px", top: borderheight-imgsizes.b+"px" , width: (imgsizes.bl+"px"), height: (imgsizes.b +"px") },{ className : "hover_master_background hover-bottom-bl" },[
				newnode("img",{},{src: getFullImgSrc("img/dummy.gif") },[])
			]),
			newnode("div",{ position: "absolute",left : "0px", top:"0px" , width: (imgsizes.bl+"px"), height: ((borderheight-imgsizes.b)+"px") },{ className : "hover-bottom-l" },[
				newnode("img",{},{src: getFullImgSrc("img/dummy.gif") },[])
			]),
			newnode("div",{ position: "absolute",left : (innerwidthbottom+imgsizes.bl)+"px", top:"0px" , width: (imgsizes.br+"px"), height: ((borderheight-imgsizes.b)+"px") },{ className : "hover-bottom-r" },[
				newnode("img",{},{src: getFullImgSrc("img/dummy.gif") },[])
			]),
			newnode("div",{ position: "absolute", left: imgsizes.bl+ "px", top: (borderheight-imgsizes.b)+"px", width: "396px" , height: (imgsizes.b)+"px"},{ className : "hover-bottom-b" },[
				newnode("img",{},{src: getFullImgSrc("img/dummy.gif") },[])
			]),
			newnode("div",{ position: "absolute", left: imgsizes.bl+ "px", top: "0px" , width:(innerwidthbottom)+"px" , height: (borderheight-imgsizes.b)+"px"},{ className : "hover-bottom-color" },[
				newnode("img",{},{src: getFullImgSrc("img/dummy.gif") },[])
			])
		])
	]);
 
	returnobj.node=coreelement;
	returnobj.header=coreelement.getElementsByTagName("div")[6];
	returnobj.body=coreelement.getElementsByTagName("div")[9];
	returnobj.bottom=coreelement.getElementsByTagName("div")[18];
	return returnobj;
}

/* ########################################## TABS ####################################################### */

/**
 * tab container
 * @param id {String} the id of the hover which the tab container is attached to (deprecated)
 */
function Tabbing(id) {
	// @TODO: the id is just a workaround for the 'broken by design' hover layout
	// and needs to be reviewed!
	this.id = id;
	this.tabs = new Array();
	this.selected; 
}

Tabbing.prototype = {
		
	/**
	 * returns the DOMNode containing all tabs
	 * @returns {DOMNode} the DOMNode containing all tabs
	 */
	getDOMNode: function() {
		var Self = this;
		
		var trnode = newnode("tr");
		for (var i in this.tabs) {			
			var tdnode = this.tabs[i].getDOMNode();
			trnode.appendChild(tdnode);
		}
		
		trnode.appendChild(
				newnode("td", { width : "100%" }, 
					{ className : "tabbing_border_style" }, 
					[ document.createTextNode("\u00a0")] ) 
				);
		
		return trnode;
	},

	/**
	 * adds a new tab with the given id
	 * @param tab {Tab} the tab
	 */
	addTab : function(tab) {
		 var Self = this;	
		 if (this.tabs.length == 0) {
			tab.select(true);
			this.selected = 0;
		 }
		 this.tabs.push(tab);	
		 
		 // attach click event to td - do we have to unregister this event?!?
		 addDOMEvent(tab.getDOMNode(), "click", 
			(function(id, pos) {
				return function(e) {
					Self.selectTabById(id);
				}
			})(tab.id)
		);		
	},
	
	/**
	 * removes a tab with the given id
	 * @param id {String} the id of the tab
	 */
	removeTab : function(id) {
		for (var i in this.tabs) {
			if (this.tabs[i].id == id) {
				this.tabs.splice(i,i);
			}
		}
	},
	
	selectTabById : function(id) {
		var pos = 0;
		// iterate through tabs and select / deselect them
		for (var i in this.tabs) {
			if (id == this.tabs[i].id) {
				this.selected = pos = i;
			}
			this.tabs[i].select(id == this.tabs[i].id);
		}
		
		// do we have to trigger a callback?
		if (this.tabs[this.selected].onShow) {
			this.tabs[this.selected].onShow();
		}
		
		// ugly hack to fix the 'broken by design' hover layout
		// hover layout needs to be reviewed (again) :-|
		if (this.selected == 0) {
			// change bg image on left corner of hover if first tab is selected
			$ALL(this.id + "hover-tabbin-corner-l").className = "hover_master_background hover-header-tab-tl_ac";
		} else {
			// change bg image on left corner of hover if first tab is selected
			$ALL(this.id + "hover-tabbin-corner-l").className = "hover_master_background hover-header-tab-tl";
			// change the bg image of the td on the left of the selected td
			this.tabs[this.selected-1].getDOMNode().className="hover_master_background tabbing_tab_inactive_left";
		}
	},
	
	/**
	 * returns the requested {Tab} tab
	 * @param id {String} the id of the tab
	 * @returns {Tab} the tab object, otherwise null
	 */
	getTabById : function(id) {
		for (var i in this.tabs) {
			if (this.tabs[i].id == id) {
				return this.tabs[i];
			}
		}	
		return null;
	}
	
}

/**
* a single tab
* @param id {String} the id of the tab
* @param title {String} the tab title
* @param onShow {function} a function called when entering the tab
*/
function Tab(id, title, onShow) {
	this.id = id;
	this.title = title;
	this.onShow = onShow;
	this.enabled = true;
	this.node = newnode("td", 0, 
			{ className : "clickable hover_master_background tabbing_tab_inactive" }, 
			[ addTranslated(title) ]);
}

Tab.prototype = {
	/**
	 * show / hide the tab
	 * @param {boolean} enabled - true shows the tab, otherwise false
	 */
	show : function(enabled) {
		this.enabled = enabled;
		this.node.style.display = this.enabled ? "" : "none";
	},
	
	/**
	 * selects / highlights the tab
	 * @param {boolean} selected - true select the tab, otherwise false
	 */
	select : function(selected) {
		this.node.className = selected ? "hover_master_background tabbing_tab_active" : "hover_master_background tabbing_tab_inactive";
	},
	
	setTitle : function(title) {
		this.node.firstChild.nodeValue = title;
	},
	
	/**
	 * returns the DOM node of the tab
	 * returns {DOMNode} the dom node of the tab
	 */
	getDOMNode : function() {
		return this.node;
	}
}
/**
 * 
 * All content on this website (including text, images, source
 * code and any other original works), unless otherwise noted,
 * is licensed under a Creative Commons License.
 * 
 * http://creativecommons.org/licenses/by-nc-sa/2.5/
 * 
 * Copyright (C) 2004-2010 Open-Xchange, Inc.
 * Mail: info@open-xchange.com 
 * 
 * @author Jan Finsel <jan.finsel@open-xchange.com>
 * 
 */




  /////////////////////////////////////
 //   Key and Small Key             //
/////////////////////////////////////
/***
 * Creates a new Key
 * @class Key
 * The class key is needed for MRUKeyList, every key has two functions.
 * hashCode and equals   
 */
function Key() {
}
Key.prototype.equals = function(keyObject1) {
	return this.module==keyObject1.module &&
	    this.folder_id == keyObject1.folder_id &&
	    this.id == keyObject1.id &&
	    (this.recurrence_position || 0) ==
	        (keyObject1.recurrence_position || 0);
}
Key.prototype.hashCode = function() {
    if(this.calculatedHash) { return this.calculatedHash; }
	if(this.recurrence_position || (this.recurrence_position == 0)) {
		if(!isNaN(this.folder_id) && !isNaN(this.id) && !isNaN(this.recurrence_position)) {
		  this.calculatedHash = parseInt(this.folder_id) + parseInt(this.id) +
		      Number(this.recurrence_position);
          return this.calculatedHash;		 	
		} else {
		  this.calculatedHash=(this.folder_id+this.id+this.recurrence_position);
          return this.calculatedHash;	
		}
	} else {
		if(!isNaN(this.folder_id) && !isNaN(this.id)) {
			this.calculatedHash= (parseInt(this.folder_id)+parseInt(this.id));
			return this.calculatedHash;
	    } else {
		    this.calculatedHash=(this.folder_id+this.id);
            return this.calculatedHash;
		}
	}
}
Key.createfromObject= function(keyObject) {
	var element=new Key();
	element.module=keyObject.module;
	element.folder_id=keyObject.folder_id;
	element.id=keyObject.id;
	element.recurrence_position=keyObject.recurrence_position;
	return element;
}

function SmallKey(module,id) {
	this.module=module;
	this.id=id;
}
SmallKey.prototype.equals = function(keyObject1) {
    return this.module==keyObject1.module && this.id==keyObject1.id;
}
SmallKey.prototype.hashCode = function() {
    return this.id;
}
  /////////////////////////////////////
 //   List-Recently-Used Key list   //
/////////////////////////////////////
/**
 * Creates an LRU list.
 * @class List-recently-used list (LRU list).
 * An MRU list limits the number of stored items by discarding
 * the least-recently referenced item, when storing a new item would otherwise
 * exceed the limit.
 * @param {Int} size The maximum size of the list.
 */
function LRUKeyList(size) {
	this.free = size;
	if(!this.free) {
        this.free=-1;
    }
    this.cache = {};
    this.list = new LinkedList();
}

LRUKeyList.prototype = {
    /**
     * Retrieves an item from the list.loadconfig();
     * @param key The key of the item to retrieve.
     * @return The retrieved item or <code>undefined</code> if the item was not
     * found.
     */
    get: function(key,changeList,wholeobject) {
        var items = this.cache[key.hashCode()];
        var item;
        if(items) {
            for (var i=0;i<items.length;i++) {
                if(key.equals(items[i].key)) {
                    item=items[i];  
                    break;
                }
            }
        }
        if(!item) return undefined;
        if(changeList) {
            this.list.remove(item);
            this.list.addLast(item);
        }
        if(!wholeobject) return item.data;
        else { return item; }
    },
	
	/**
     * Stores an item in the list.
     * @param key The key under which to store the item.
     * @param value The item to store in the list.
     * @return A removed item if cache is full or <code>undefined</code>
     * found.
     */
    set: function(key, value) {
        var item = this.get(key,false,true);
        if (item)
            item.data = value;
        else {
            item = {key: key, data: value};
            this.list.addLast(item);
            var hash = key.hashCode();
            if(!this.cache[hash]) this.cache[hash]=new Array();
			this.cache[hash].push(item);
            if (this.free) {
                this.free--;
            } else {
                var key1=this.list.removeFirst().key;
                var ret=this.get(key1,false,true);
                this.remove(key1);
				this.free--;
                return ret;
            } 
        }
    },


    /**
     * Removes an item from the list.
     * @param key The key of the item to remove.
     */
    remove: function(key) {
        var items = this.cache[key.hashCode()];
        var item;
        if(items) {
            for (var i=0;i<items.length;i++) {
                if(key.equals(items[i].key)) {
					this.list.remove(items[i]);
                    items.splice(i,1);
					if(!items.length) {
						this.cache[key.hashCode()]= undefined;
					}
					this.free++;
					break;  
                }
            }
        }
    },
	/**
     * Returns an array of keys.
     * @returns an Array of keys in LRUList
     */
	keys : function() {
	   var myKeys=new Array();
	   for(var hashcode in this.cache) {
	   	   if(this.cache[hashcode]) {
	           for(var i=0;i<this.cache[hashcode].length;i++) {
		          if(this.cache[hashcode][i].key) { myKeys.push(this.cache[hashcode][i].key) }	
		       }
		   }
	   }
	   return myKeys;
	}
};
  /////////////////////////////////////
 //   OX Collection                 //
/////////////////////////////////////

function OXCollection() {
    this.last_modified; //Maximum of last modified
    this.lastRequest;   //Last request of 
    this.criteria;      //Criteria
    this.order;         //Order 
    this.objects=new Array();
    this.map_objects=new LRUKeyList();
}

OXCollection.prototype = {
	/**
	 * Returns the index of the given Key
	 * @param  {Object} key
	 * @return {int} the index or null if not founds
	 */
	getIndex: function(key) {
		return this.map_objects.get(key);
	}
}

  /////////////////////////////////////
 //   HELP FUNCTIONS                //
/////////////////////////////////////
function switchStringObject(object) {
    var myObject=new Object();
    for(var i in object) {
        myObject[object[i]]=i;
    }
    return myObject;
}

  /////////////////////////////////////
 //   OXCACHE                       //
/////////////////////////////////////
var OXCache = {}
/**
 * Time {int} (in Milliseconds), at which an existing object in Cache is expired and ought
 * ought to requested again.
 * @value 0 existing objects in Cache never expire
 */
OXCache.updateTime       = 300000; //Time in Milliseconds for next update if is requested
/**
* Object size {int}, number of objects, which are maximum in Cache.
*/
OXCache.objectSize       = 20000;
/**
* Object size {int}, number of criteria list, which are maximum in Cache.
*/
OXCache.criteriaSize     = 10;
/**
 * @private
 * Sequence (int) of Cache 
 */
OXCache.sequence    = 0;
/**
* @private
* Returns next Sequence .
* @return The next sequence value {int}
*/
OXCache.getSequence = function() {
    return OXCache.sequence++;
}
/**
* @private 
* Registered module mappings.
*/
OXCache.moduleMappings   = new Object();
/**
* @private
* Cache for Objects.
*/
OXCache.cachedObjects    = new LRUKeyList(OXCache.objectSize);
/**
* @private
* Cache for Collections.
*/
OXCache.cachedCollections = new MRUList(OXCache.criteriaSize); //new Array();
/**
* @private 
* Temporary Trash for objects, which are removed out of cachedCollections between the last requests.
*/
OXCache.cachedObjectsTrash = new LRUKeyList();
/**
* @private 
* Temporary Trash for collections, which are removed out of cachedCollections between the last requests.
*/
OXCache.cachedCollectionsTrash = new Array();
/**
* @private 
* Short key mapping for objects.
*/
OXCache.objectShortKey   = new LRUKeyList();
/**
* @private 
* Request, which will be requested after execute all callbacks.
*/
OXCache.waitingrequests = new Array();
/**
* @private
* Registered modified callbacks
*/
OXCache.modifiedCallbacks = new Object();
/**
* @private 
* Registered callbacks which are executed just one time.
*/
OXCache.actualCallbacks = new Object();
/**
* @private
* List of modified Objects, which are changed since last request.
*/
OXCache.modifiedObjects= new Array();
/**
* @private
* List of possible deleted Objects in list requests, which are changed since last request.
*/
OXCache.deletedObjects= new Array();
/**
 * @private
 * List all all requests send to server
 */
OXCache.requested = {
    CNOT: [], CEXP: [], CMIS: {}, LNOT: {}, LEXP: {}, LMIS: {}
};
/**
 * @private
 * Checks if OXCache is in execution 
 */
OXCache.executeActive=false;

/**
* @private
* Adds request to waiting requests. Waiting requests will be inquired after all callbacks are executed.
* @param module {string}, String of module
* @param collection {OXCollection}, Collection object
* @param cb {function}, Callback 
*/
OXCache.requestSequence = 0;
OXCache.getRequestSequence = function() {
    return OXCache.requestSequence++;
}

OXCache.addRequest= function(module,collection,cb,uniqueName) {
	OXCache.waitingrequests.push({module : module ,collection : collection , cb : cb, uniqueName : uniqueName });
}
/**
* @private
* Join of Cache. Executes all execute functions in Cache, if all started requests are returned 
* and clears tmp variables at last step.
*/
OXCache.join = new Join(function() {
	OXCache.callbackArray=new Object();
	OXCache.modifiedArray=new Object();
	for(var i in OXCache.actualCallbacks) {
        OXCache.callbackArray[i]=true;
    }
    for(var i in OXCache.modifiedCallbacks) {
        OXCache.modifiedArray[i]=true;
    }
    
	for (var i in OXCache.moduleMappings) {
        if(OXCache.moduleMappings[i].execute) {
            OXCache.moduleMappings[i].execute();
        }
    }
    for(var i in OXCache.callbackArray) {
        delete OXCache.actualCallbacks[i];
    }
    for(var i in OXCache.modifiedObjects) {
       OXCache.modifiedObjects[i].modified=false;
    }
    OXCache.modifiedObjects=new Array();
    OXCache.deletedObjects=new Array();
    for(var i in OXCache.cachedCollections.cache) {
       OXCache.cachedCollections.cache[i].data.modified=false;
    }
	
    var wr = {};
    for (var i = 0; i < OXCache.waitingrequests.length; i++)
        wr[OXCache.waitingrequests[i].uniqueName] = OXCache.waitingrequests[i];
    OXCache.waitingrequests=new Array();
	for(var i in wr) {
        OXCache.actualCallbacks[i]={
			module : wr[i].module , 
		    collection : wr[i].collection ,
			fn : wr[i].cb
		};
        OXCache.moduleMappings[wr[i].module].get(i, wr[i].module,
            wr[i].collection, null, wr[i].cb, true);		
    }   
    OXCache.cachedObjectsTrash=new LRUKeyList();
    OXCache.cachedCollectionsTrash=new Array();
	OXCache.requested = {
        CNOT: [], CEXP: [], CMIS: {}, LNOT: {}, LEXP: {}, LMIS: {}
    };
});
OXCache.errorHandler = function(uniqueName,request,response,response2) {
	if(debug) { console.log("Error",uniqueName,request,response,response2); }
	if(response.error) {
		switch(response.category) {
			case  1: //USER IMPUT (SHOULD NOT EXIST) NOT IN CACHE
			    newServerError(response);
                OXCache.unregister(uniqueName);				
                break;
			case  2: //CONFIGURATION ERROR (SHOULD NOT EXIST) NOT IN CACHE
			    newServerError(response);
				OXCache.unregister(uniqueName);
                break;
			case  3: //MISSING RIGHTS
            case  4: //TRY AGAIN
			case  5: //SUBSYSTEM DOWN
			    newServerError(response);
                var col=OXCache.cachedCollections.get(uniqueName);
				if(col) { 
				    col.check=false;
					col.modified=false;
				} else {
					OXCache.unregister(uniqueName);
				}
				break;
			case  6: //SOCKET CONNECTION (CAN'T OCCUR)
                newServerError(response);
				OXCache.unregister(uniqueName);
				break;
			case  7: //INTERNAL ERROR (SERVER BUG)
                newServerError(response);
				OXCache.unregister(uniqueName);
				break;
			case  8: //CODE ERROR (SERVER BUG)
                newServerError(response);
				OXCache.unregister(uniqueName);
				break;
			case  9: //MODIFICATION CHANGES(TIMESTAMP EXPIRED)
                newServerError(response);
				OXCache.unregister(uniqueName);
				break;
			case 10: //CONFIG FILE ERROR(SERVER BUG)
                newServerError(response);
				OXCache.unregister(uniqueName);
				break;
			case 11: //RESSOURCE FULL e.g IMAP QUOTA
                newServerError(response);
				OXCache.unregister(uniqueName);
				break;
			case 12: //TO LONG VALUES IN FIELD (SHOULD NOT EXIST)
                newServerError(response);
				OXCache.unregister(uniqueName);
				break;
			case 13: //WARNING
                newServerError(response);
				OXCache.unregister(uniqueName);
                break;
			default:
			    newServerError(response);
                break;
		}
	}
	return;	
}
/**
* @private
* Starts a new JSON Request combined with OXCache join handling 
* @param url {String} Url of request
* @param data {object} Data of request
* @param cb {function} Callback function
*/
OXCache.getJSON = function(url,data,cb) {
    (new JSON()).put(url,data,null,OXCache.join.add(cb), OXCache.join.alt());   
}
/**
* @private
* Sets a new Mapping for ObjectCache 
* @param url {String} module of mapping
* @param data {OXMapping} An OX Mapping
*/
OXCache.setMapping = function(module,mapping) {
    if(OXCache.moduleMappings[module]) {
        if(debug) { alert("A module mapping for module >"+module+"< is still available in Object cache."); }
        return;
    } 
    OXCache.moduleMappings[module]=mapping;
}
/**
* Starts a OXCache request 
* @param uniqueName {String} Unique name for this request 
* @param module {String} Module of the request
* @param collection {OXCollection} see OXCollection for further description
* @param callbackModified {function} Callback Modified will be executed when Collection is modified (optional)
* @param callback {function} Callback which will be executed directly (optional)
* @param forceupdate {boolean} (optional) Ignore expire handling of object and start new request automatically (optional)
*        default <code>false</code>  
*/
OXCache.newRequest = function (uniqueName,module,collection,callbackModified,callback,forceupdate) {
    var returnobject;
	uniqueName="oxcache_external_"+OXCache.getRequestSequence();
	if(!OXCache.moduleMappings[module]) {
        if(debug) { alert("A module mapping for module >"+module+"< is not available in Object cache.");}
        return;
    }
	if(!uniqueName) {
        if(debug) { alert("No uniquename entered for modified function");}
        return;
    }
    if((OXCache.modifiedCallbacks[uniqueName] && 
        OXCache.modifiedCallbacks[uniqueName].fn && 
        callbackModified) || 
	   (OXCache.actualCallbacks[uniqueName] && 
       OXCache.actualCallbacks[uniqueName].fn && 
       callback))
	{
        if(debug) { alert("A modified function for name >"+uniqueName+"< is still available in Object cache.");}
    }
	if (callbackModified) {
	   var modCB = OXCache.modifiedCallbacks[uniqueName] =
	       { module: module, collection: collection };
	   modCB.fn = callbackModified.fn || callbackModified;
	   if (callbackModified.before) {
           modCB.before = callbackModified.before;
	   }
	}
	if(callback) {
	   OXCache.actualCallbacks[uniqueName]={module : module , collection : collection ,fn : callback};
	}
	var returnobject=OXCache.moduleMappings[module].get(uniqueName,module,collection,callbackModified,callback,forceupdate);
	returnobject.uniqueName=uniqueName;
	return returnobject;
}

/**
* Unregister a modified callback 
* @param uniqueName {String} Unique name of the modified request
* */
OXCache.unregister = function(uniqueName) {
    var registered=false
	if(OXCache.modifiedCallbacks[uniqueName]) { 
        delete OXCache.modifiedCallbacks[uniqueName];  
		registered=true;
    }
	if(OXCache.actualCallbacks[uniqueName]) { 
        delete OXCache.actualCallbacks[uniqueName];
		registered=true;
    } 
	//if(debug && !registered) { alert("No function for name >"+uniqueName+"< is not available in Object cache.");}
    
}
/**
* Updates all modified callbacks 
* */

OXCache.update = function() {
    for(var counter in OXCache.modifiedCallbacks) {
        var uniqueObject=OXCache.newRequest(null,OXCache.modifiedCallbacks[counter].module,OXCache.modifiedCallbacks[counter].collection,null,function(data) {
	        if (uniqueObject) {
			  OXCache.unregister(uniqueObject.uniqueName);
			} else {
			    //debugger;
			}
	    },true);
    }   
}
  /////////////////////////////////////
 //   OX Mapping                    //
/////////////////////////////////////
/**
 * Abstract Mapping which has implemented functions for whole core modules
 */
function OXAbstractMapping() {}
/**
 * Returns function get for a OXMapping implementation.  
 * @param oxMapping {OXMapping} OXMapping implementation
 * @return {function} get function for OXMapping implementation. Handles requests of a module type in combination if object cache.
 * params of the function:
 *      uniqueName {String} Unique name of a request
 *      module {String} Module of a request
 *      collection {OXCollection} OXCollection of a request see OXCollection for further description
 *      (optional) callbackModified {function} A callback which has to be executed when Collection is modified 
 *      (optional) callback {function} A callback which will be executed directly 
 *      (optional) forceupdate {boolean}  A boolean ig expire handling of OXCache ought to be ignored. 
 *          
 */
OXAbstractMapping.get=function(oxMapping) {
    return function(uniquename,module,collection,callbackModified,callback,forceupdate) {
        //Check criteria search or list search
        if(collection.criteria) {
            oxMapping.criteriaRequest(uniquename,module,collection,callbackModified,callback,forceupdate);
        } else if(collection.objects) {
            return oxMapping.listRequest(uniquename,module,collection,callbackModified,callback,forceupdate);
        } else {
            if(debug) { alert("Illegal request: Neighter criteria nor objects are set"); }
        }
		return {};
    }
}
/**
 * Returns Execute function a OXMapping implementation.
 * @param oxMapping {OXMapping} OXMapping implementation
 * @return {function} execute function for OXMapping implementation. Excutes all needed callbacks of an module in OXCache
 *          
 */
OXAbstractMapping.execute=function(oxMapping) {
	return function() {
		for(var i in OXCache.actualCallbacks) {
			if(OXCache.callbackArray[i]) {
			   var tmpCallback=OXCache.actualCallbacks[i];
                if(!tmpCallback.executed) {
					tmpCallback.executed=true;
					if(tmpCallback.module==oxMapping.module) {
	                    var ret=oxMapping.buildResponse(tmpCallback,false,i);
	                    if(ret) {
	                        OXCache.actualCallbacks[i].fn(ret);
	                    }
	                }
					tmpCallback.executed=false;
			    }	
			}    
        }
		for(var i in OXCache.modifiedCallbacks) {
            if(OXCache.actualCallbacks[i]) { continue; }
			if(OXCache.modifiedArray[i]) {
				var tmpCallback=OXCache.modifiedCallbacks[i];
                if(!tmpCallback.executed) {
					tmpCallback.executed=true;
		            if(tmpCallback.module==oxMapping.module) {
		                var ret=oxMapping.buildResponse(tmpCallback,true,i);
						if(ret) {
							OXCache.modifiedCallbacks[i].fn(ret);	
						}
		            }
					tmpCallback.executed=false;
				}
			}
        }
	}
}
/**
 * @private 
 * This function is needed, if abstract function execute of an OXAbstractMapping is mapped to an OXMapping implementation
 * Return function builds response object, for a callback
 * @param oxMapping {OXMapping} OXMapping implementation
 * @return {function} buildResponse function for OXMapping implementation. 
 * params of the function:
 *      collection {OXCollection} OXCollection of an response
 *      isModified {boolean} boolean if callback is modified request
 *      callback {boolean} executable Callback          
 */
OXAbstractMapping.buildResponse = function(oxMapping) {
	return function(tmpCallback,isModified,uniqueName) {
		var collection=tmpCallback.collection;
		if(collection.criteria) {
			var tmpCollection=oxMapping.getCollection(collection,false,true);
			if(!tmpCollection || tmpCollection.check) {
				if(debug) { console.log("Missing or changed collection:",collection); }
				OXCache.addRequest(oxMapping.module,collection,tmpCallback.fn,uniqueName);
                if (tmpCollection) tmpCollection.check = false;
			} else if(!isModified || tmpCollection.modified) {
                var retCol=new OXCollection();
				retCol.timestamp=tmpCollection.timestamp;
                retCol.criteria=tmpCollection.criteria;
				retCol.search=tmpCollection.search;
				//TODO CHECK FOR ORDER 
                retCol.order=collection.order;         //Order
                var direction=1;
                if(collection.order && collection.order.length) {
					if(collection.order[0].order!=tmpCollection.order[0].order) {
						direction=-1;
					}
				} 
                retCol.objects=new Array(tmpCollection.objects.length);
                retCol.map_objects=new LRUKeyList();
				var counter= (direction==1) ? 0 : tmpCollection.objects.length-1;
				for(var i=0;i<tmpCollection.objects.length;i++) {
				   //var tmpObject=OXCache.cachedObjects.get(tmpCollection.objects[i]) || OXCache.cachedObjectsTrash.get(tmpCollection.objects[i]);
				   var tmpObject=tmpCollection.objects[i];
	               if(tmpObject) { 
	                   retCol.objects[counter]=tmpObject;
	                   retCol.map_objects.set(tmpObject,counter+1);
	               } else {
	                   if(debug) {console.log(tmpCollection.objects[i],i,"is missing");//TODO : OBJECT IS OUT OF CACHE CAUSED BY SIZE AND MODIFIED REQUEST NEED DATA 
					   }   
	               }
				   counter=counter+direction;
			    }
			}
			return retCol;
		} else if(collection.objects) {
			if(isModified) {
				if(!tmpCallback.modified) {
					var realmodified=false;
					for(var i=0;i<OXCache.modifiedObjects.length;i++) {
						for(var i2=0;i2<collection.objects.length;i2++) {
							if(OXCache.modifiedObjects[i].equals(collection.objects[i2])) {
							  realmodified=true;
							  break;
					        }
						}	
					}
					if(!realmodified) return;
				} else {
					tmpCallback.modified=false;
				}
			}
			var retCol=new OXCollection();
            retCol.objects=new Array();
            for(var i=0;i<collection.objects.length;i++) {
                //TODO
				if(!collection.objects[i].hashCode || !collection.objects[i].equals) {
					collection.objects[i]=oxMapping.objectConstructor.createfromObject(collection.objects[i]);
				}
                var tmpObject=OXCache.cachedObjects.get(collection.objects[i]) || OXCache.cachedObjectsTrash.get(collection.objects[i]);
                if(tmpObject) {
                    retCol.objects.push(tmpObject);
                    retCol.map_objects.set(tmpObject, i + 1);
                } else {
					//TODO ??? IS OBJECT REAL DELETED ??? THROW ERROR
                    if(debug) {
						console.log(collection.objects[i],i,"is missing");//TODO : OBJECT IS OUT OF CACHE CAUSED BY SIZE AND MODIFIED REQUEST NEED DATA
					}   
                }
            }
			return retCol;	
        }
	} 
}
/**
 * @private 
 * This function is needed, if abstract function get of an OXAbstractMapping is mapped to an OXMapping implementation
 * Return function handles criteria requests of a OXMapping request
 * @param oxMapping {OXMapping} OXMapping implementation
 * @return {function} criteriaRequest function for OXMapping implementation. 
 *      params of the function:
 *      uniqueName {String} Unique name of a request
 *      module {String} Module of a request
 *      collection {OXCollection} OXCollection of a request see OXCollection for further description
 *      (optional) callbackModified {function} A callback which has to be executed when Collection is modified 
 *      (optional) callback {function} A callback which will be executed directly 
 *      (optional) forceupdate {boolean}  A boolean ig expire handling of OXCache ought to be ignored. 
 * 
 */
OXAbstractMapping.criteriaRequest =function(oxMapping) {
    return function(uniqueName,module,collection,callbackModified,callback,forceupdate) {
        var onlymandatory=false;
		if(!collection.columns) { 
            var tmpColumns=new Array();
            for(var value in oxMapping.stringmapping) {
                tmpColumns.push(value);
                collection.columns=tmpColumns;
            }
        }
		for(var i=0;i<collection.columns.length;i++) {
            for(var i2=0;i2<oxMapping.mandatoryfields.length;i2++) {
                if(collection.columns[i]==oxMapping.mandatoryfields[i2]) {
				    collection.columns.splice(i,1);
                }
            }
        }
		if(!collection.columns.length) {
			onlymandatory=true;
		}
        collection.columns=oxMapping.mandatoryfields.concat(collection.columns);             
		//Check criteria exists
        var myCollection=oxMapping.getCollection(collection,false);
        if(myCollection) { 
            //Check time expired
            if(!forceupdate && (myCollection.lastRequest && myCollection.lastRequest>(new Date().getTime()-OXCache.updateTime))) {
				if (!onlymandatory) {
					for (var i = 0; i < myCollection.objects.length; i++) {
						var tmpObject = OXCache.cachedObjects.get(myCollection.objects[i]);
						if (!(tmpObject && tmpObject.hasColumns(collection.columns))) {
							if (!tmpObject) {
								if (myCollection.objects[i].hashCode) {
									tmpObject = myCollection.objects[i];
								}
							}
							oxMapping.addCriteriaMissingColumn(collection.columns, tmpObject, uniqueName);
						}
					}
				}               
            } else {
                oxMapping.addCriteriaExpired(collection,myCollection.timestamp,uniqueName);
            }     
        } else {
            oxMapping.addCriteriaNotInCache(collection,uniqueName);
        }
		var tmpFn = OXCache.join.add();
        oxMapping.request();
		delete oxMapping.requestCriteriaNotInCache;
        delete oxMapping.requestCriteriaExpired;
        delete oxMapping.requestCriteriaMissingColumn;
        tmpFn();
    }
}
/**
 * @private 
 * This function is needed, if abstract function get of an OXAbstractMapping is mapped to an OXMapping implementation
 * Return function handles list requests of a OXMapping request
 * @param oxMapping {OXMapping} OXMapping implementation
 * @return {function} listRequest function for OXMapping implementation. 
 *      params of the function:
 *      uniqueName {String} Unique name of a request
 *      module {String} Module of a request
 *      collection {OXCollection} OXCollection of a request see OXCollection for further description
 *      (optional) callbackModified {function} A callback which has to be executed when Collection is modified 
 *      (optional) callback {function} A callback which will be executed directly 
 *      (optional) forceupdate {boolean}  A boolean if expire handling of OXCache ought to be ignored. 
 */
OXAbstractMapping.listRequest =function(oxMapping) {
    return function(uniqueName,module,collection,callbackModified,callback,forceupdate) {
        var returnobject={};
		returnobject.clear=[];
		returnobject.set=[];
		var isrequest=false;
		if(collection.objects) {
			if(!collection.columns) { 
                var cols = collection.columns = [];
                for(var value in oxMapping.stringmapping) cols.push(value);
                if (oxMapping.alternative) {
                    for(var i in oxMapping.alternative) {
                        var alt = oxMapping.alternative[i];
                        if (typeof alt != "function" || alt()) cols.push(i);
                    }
                 }
            } 
			for(var i=0;i<collection.columns.length;i++) {
                for(var i2=0;i2<oxMapping.mandatoryfields.length;i2++) {
                    if(collection.columns[i]==oxMapping.mandatoryfields[i2]) {
                        collection.columns.splice(i,1);
                    }
                }
            }
			collection.columns=oxMapping.mandatoryfields.concat(collection.columns);
            for(var i=0;i<collection.objects.length;i++) {
                var tmpVar = collection.objects[i];
                var tmpObject = oxMapping.objectConstructor.createfromObject(tmpVar);
				var myCacheObject=OXCache.cachedObjects.get(tmpObject);
                if(myCacheObject) {
                    if(myCacheObject.hasColumns(collection.columns)) {
                        if(forceupdate || (!(myCacheObject.lastRequest>(new Date().getTime()-OXCache.updateTime)))) {
                           oxMapping.addListExpired(collection.columns,tmpObject,uniqueName);
						   isrequest=true;
                        } 
						returnobject.set.push(OXCache.cachedObjects.get(tmpObject));
                    } else {
                       oxMapping.addListMissingColumn(collection.columns,tmpObject,uniqueName);
					   returnobject.clear.push(tmpObject);
					   isrequest=true;
                    }
                } else {
                    oxMapping.addListNotInCache(collection.columns,tmpObject,uniqueName);
					returnobject.clear.push(tmpObject);
					isrequest=true;
                }
           }
       }   
        var tmpFn = OXCache.join.add();
       oxMapping.request();
	   delete oxMapping.requestListNotInCache;
       delete oxMapping.requestListExpired;
       delete oxMapping.requestListMissingColumn;
        tmpFn();
		return isrequest ? returnobject : {};
    }
}
/**
 * @private 
 * This function is needed, if abstract function criteria Request of OXAbstractMapping is mapped to an OXMapping implementation
 * Return function adds OXCollection to the to server request list (not in Cache)
 * @param oxMapping {OXMapping} OXMapping implementation
 * @return {function} addCriteriaNotInCache function for OXMapping implementation. 
 *      params of the function:
 *      collection {OXCollection} OXCollection for a request see OXCollection for further description
 */
OXAbstractMapping.addCriteriaNotInCache = function(oxMapping) {
    return function(collection, uniqueName) {
		if(!oxMapping.requestCriteriaNotInCache) {
            oxMapping.requestCriteriaNotInCache=new Array();
        }
        var cnot = OXCache.requested.CNOT;
        for(var i=0;i<OXCache.requested["CNOT"].length;i++) {
            if(oxMapping.equalsCollection(cnot[i].collection, collection)) {
                return; 
            }
        }
        OXCache.requested["CNOT"].push({ collection : collection, uniqueName : uniqueName });
        oxMapping.requestCriteriaNotInCache.push({ collection : collection, uniqueName : uniqueName } );
    }
}
/**
 * @private 
 * This function is needed, if abstract function criteria Request of OXAbstractMapping is mapped to an OXMapping implementation
 * Return function adds OXCollection to the to server request list (in Cache but expired)
 * @param oxMapping {OXMapping} OXMapping implementation
 * @return {function} addCriteriaExpired function for OXMapping implementation. 
 *      params of the function:
 *      collection {OXCollection} OXCollection for a request see OXCollection for further description
 */
OXAbstractMapping.addCriteriaExpired = function(oxMapping) {
    return function(collection, last_modified, uniqueName) {
        if(!oxMapping.requestCriteriaExpired) {
            oxMapping.requestCriteriaExpired=new Array();
		}
        var cexp = OXCache.requested.CEXP;
        for(var i = 0; i < cexp; i++) {
            if (oxMapping.equalsCollection(cexp[i].collection, collection)) {
                return;
            }
        }
        cexp.push({ collection : collection, last_modified : last_modified });
        oxMapping.requestCriteriaExpired.push({ collection : collection, last_modified : last_modified, uniqueName : uniqueName });
	}
}
/**
 * @private 
 * This function is needed, if abstract function criteria Request of OXAbstractMapping is mapped to an OXMapping implementation
 * Return function adds OXObjects to the to server request list (object in Cache but columns missing)
 * @param oxMapping {OXMapping} OXMapping implementation
 * @return {function} addCriteriaExpired function for OXMapping implementation. 
 *      params of the function:
 *      columns {Array of Strings} Needed columsn for a request
 *      object {Key} key of an object
 */
OXAbstractMapping.addCriteriaMissingColumn = function(oxMapping) {
    return function(columns,object,uniqueName) {
        if(!oxMapping.requestCriteriaMissingColumn) {
            oxMapping.requestCriteriaMissingColumn=new Array();
		}
		var key = JSON.serialize([columns, object]);
        if (OXCache.requested.CMIS[key]) return;
        OXCache.requested.CMIS[key] = true;
        oxMapping.requestCriteriaMissingColumn.push({ columns : columns , object : object , uniqueName : uniqueName });
    }
}
/**
 * @private 
 * This function is needed, if abstract function list Request of OXAbstractMapping is mapped to an OXMapping implementation
 * Return function adds OXObjects to the to server request list (object not in Cache)
 * @param oxMapping {OXMapping} OXMapping implementation
 * @return {function} addListNotInCache function for OXMapping implementation. 
 *      params of the function:
 *      columns {Array of Strings} Needed columns for a request
 *      object {Key} key of an object
 */
OXAbstractMapping.addListNotInCache = function(oxMapping) {
    return function(columns,object,uniqueName) {
        if(!oxMapping.requestListNotInCache) {
            oxMapping.requestListNotInCache=new Array();
        }
        var key = JSON.serialize([columns, object]);
        if (OXCache.requested.LNOT[key]) return;
        OXCache.requested.LNOT[key] = true;
	    oxMapping.requestListNotInCache.push({ columns : columns , object : object, uniqueName : uniqueName });
    }
}
/**
 * @private 
 * This function is needed, if abstract function list Request of OXAbstractMapping is mapped to an OXMapping implementation
 * Return function adds OXObjects to the to server request list (object in Cache but expired)
 * @param oxMapping {OXMapping} OXMapping implementation
 * @return {function} addListExpired function for OXMapping implementation. 
 *      params of the function:
 *      columns {Array of Strings} Needed columns for a request
 *      object {Key} key of an object
 */
OXAbstractMapping.addListExpired = function(oxMapping) {
    return function(columns,object,uniqueName) {
        if(!oxMapping.requestListExpired) {
            oxMapping.requestListExpired=new Array();
        }
        var key = JSON.serialize([columns, object]);
        if (OXCache.requested.LEXP[key]) return;
        OXCache.requested.LEXP[key] = true;
        oxMapping.requestListExpired.push({ columns : columns , object : object, uniqueName : uniqueName });
    }
}
/**
 * @private 
 * This function is needed, if abstract function list Request of OXAbstractMapping is mapped to an OXMapping implementation
 * Return function adds OXObjects to the to server request list (object in Cache but missing columns)
 * @param oxMapping {OXMapping} OXMapping implementation
 * @return {function} addListMissingColumn function for OXMapping implementation. 
 *      params of the function:
 *      columns {Array of Strings} Needed columns for a request
 *      object {Key} key of an object
 */
OXAbstractMapping.addListMissingColumn = function(oxMapping) {
    return function(columns,object,uniqueName) {
        if(!oxMapping.requestListMissingColumn) {
            oxMapping.requestListMissingColumn=new Array();
        }
        var key = JSON.serialize([columns, object]);
        if (OXCache.requested.LMIS[key]) return;
        OXCache.requested.LMIS[key] = true;
        oxMapping.requestListMissingColumn.push({ columns : columns , object : object, uniqueName : uniqueName });
    }
}
/**
 * @private 
 * This function is needed, if abstract function list or criteria Request of OXAbstractMapping is mapped to an OXMapping implementation
 * Return function executes server requests.
 * @param oxMapping {OXMapping} OXMapping implementation
 * @return {function} request function for OXMapping implementation. 
 */
OXAbstractMapping.request = function(oxMapping) {
    return function() {
        var multipleRequests=new Array();
        if(oxMapping.requestCriteriaNotInCache) {
            for(var i = 0;i<oxMapping.requestCriteriaNotInCache.length;i++) {
                var tmpObject= { action : "all", module : oxMapping.module };
                var cols = oxMapping.convertColumns(oxMapping.requestCriteriaNotInCache[i].collection.columns)
                for (var j in cols) tmpObject[j] = cols[j].join(",");
                for(var counter in oxMapping.requestCriteriaNotInCache[i].collection.criteria) {
                    switch(counter) {
                        case "folder_id":
                           tmpObject["folder"]= oxMapping.requestCriteriaNotInCache[i].collection.criteria[counter];
                           break;
                        default:
                            tmpObject[counter]= oxMapping.requestCriteriaNotInCache[i].collection.criteria[counter];
                    }
                }
				if(oxMapping.requestCriteriaNotInCache[i].collection.search) {
					var searchObject=oxMapping.requestCriteriaNotInCache[i].collection.search;
					var operator="or";
					switch(oxMapping.module) {
                        case "tasks" :
                           for(var key in searchObject) {
                                var value=searchObject[key];
                                if(key.toLowerCase() == "operator" && value.toLowerCase() == "or") {
                                    operator="or";
                                } else if(key == "title" || key =="categories" || key =="note") {
                                    tmpObject.action="search";
                                    tmpObject.data = { pattern : value };    
                                    break;
                                } else {
                                    alert("This search" + searchObject +"is not supported for tasks");
                                }
                            }
                            break;
                        case "contacts" :
                            if(searchObject["startletter"]) {
                                if(searchObject["last_name"]) {
                                    tmpObject.action="search";
                                    tmpObject.data = { pattern : searchObject["last_name"] , startletter : true };
                                    if(tmpObject.folder) { tmpObject.data["folder"] = tmpObject.folder }
                                } else {
                                    alert("This search" + searchObject +"is not supported for contacts")
                                } 
                            } else {
                                tmpObject.action="search";
                                tmpObject.data = {};
                                if(tmpObject.folder) {
                                    tmpObject.data.folder = tmpObject.folder;
                                }
                                for(var key in searchObject) {
                                    var value=searchObject[key];
                                    if(key.toLowerCase() == "operator" && value.toLowerCase() == "or") {
                                        tmpObject.data.orSearch = true;
                                    } else if (key in { email1: 1, email2: 1,
                                                        email3: 1, display_name: 1,
                                                        first_name: 1, last_name: 1,
                                                        company: 1, categories: 1,
                                                        city_business: 1, street_business: 1,
                                                        department: 1 })
                                    {
                                        tmpObject.data[key] = value;
                                    } else {
                                        alert("This search" + searchObject +"is not supported for contacts");
                                    }
                                }   
                            }
						    break;
						case "mail":
                           for(var key in searchObject) {
                                var value=searchObject[key];
                                if(key.toLowerCase() == "operator" && value.toLowerCase() == "or") {
                                    operator="or";
                                } else {
                                    tmpObject.action="search";
                                    if(!tmpObject.data) { tmpObject.data=[]; }
									var tmpCol=oxMapping.convertColumns([trimStr(key)]).columns[0];
                                    if(tmpCol) { tmpObject.data.push({col:  parseInt(tmpCol), pattern : searchObject[key] }); }
                                }    
                            }
                            break;
                        case "infostore":
                            for(var key in searchObject) {
                                var value=searchObject[key];
                                if(key.toLowerCase() == "operator" && value.toLowerCase() == "or") {
                                    operator="or";
                                } else if(key == "title" || key =="url" || key =="filename" || key =="version_comment" || key=="categories" || key=="description") {
                                    tmpObject.action="search";
                                    tmpObject.data = { pattern : value };    
                                    break;
                                } else {
                                    alert("This search" + searchObject +"is not supported for tasks");
                                }
							}
                            break;
                    }
				}
				if(oxMapping.requestCriteriaNotInCache[i].collection.order){
                    var tmpOrder=oxMapping.requestCriteriaNotInCache[i].collection.order[0];
                    if(oxMapping.convertColumns([trimStr(tmpOrder.sort)]).columns[0]) {
                       tmpObject["sort"]=oxMapping.convertColumns([trimStr(tmpOrder.sort)]).columns[0];
                       tmpObject["order"]= (tmpOrder.order.toLowerCase()=="desc") ? "desc" : "asc";
                    }                   
                }
                multipleRequests.push(tmpObject);
            }
        }
        if(oxMapping.requestCriteriaExpired) {
            for(var i = 0;i<oxMapping.requestCriteriaExpired.length;i++) {
                var tmpObject= { action : "all", module : oxMapping.module };
                var cols = oxMapping.convertColumns(oxMapping.requestCriteriaExpired[i].collection.columns);
                for (var j in cols) tmpObject[j] = cols[j].join(",");
                var tmpObject2= {
                    action : "updates", module : oxMapping.module, timestamp : oxMapping.requestCriteriaExpired[i].last_modified || 0,
                    ignore : "deleted"
                };
                var cols = oxMapping.convertColumns(oxMapping.requestCriteriaExpired[i].collection.columns);
                for (var j in cols) tmpObject2[j] = cols[j].join(",");
                for(var counter in oxMapping.requestCriteriaExpired[i].collection.criteria) {
                    switch(counter) {
                        case "folder_id":
                           tmpObject["folder"]= oxMapping.requestCriteriaExpired[i].collection.criteria[counter];
                           tmpObject2["folder"]= oxMapping.requestCriteriaExpired[i].collection.criteria[counter];
                           break;
                        default:
                            tmpObject[counter]= oxMapping.requestCriteriaExpired[i].collection.criteria[counter];
                            tmpObject2[counter]= oxMapping.requestCriteriaExpired[i].collection.criteria[counter];
                    }
                }
				if(oxMapping.requestCriteriaExpired[i].collection.search) {
                    var searchObject=oxMapping.requestCriteriaExpired[i].collection.search;
					var operator="or";
                    switch(oxMapping.module) {
                        case "tasks" :
                           for(var key in oxMapping.requestCriteriaExpired[i].collection.search) {
                                var value=oxMapping.requestCriteriaExpired[i].collection.search[key];
                                if(key.toLowerCase() == "operator" && value.toLowerCase() == "or") {
                                    operator="or";
                                } else if(key == "title" || key =="categories" || key =="note") {
                                    tmpObject.action="search";
                                    tmpObject.data = { pattern : value };  
									if(tmpObject.folder) { tmpObject.data["folder"] = tmpObject.folder }  
                                } else {
                                    alert("This search" + oxMapping.requestCriteriaExpired[i].collection.search +"is not supported for tasks");
                                }
                            }
                            break;
						case "contacts" :
                           if(searchObject["startletter"]) {
                                if(searchObject["last_name"]) {
                                    tmpObject.action="search";
                                    tmpObject.data = { pattern : searchObject["last_name"] , startletter : true };
                                    if(tmpObject.folder) { tmpObject.data["folder"] = tmpObject.folder }
                                } else {
                                    alert("This search" + searchObject +"is not supported for contacts")
                                } 
                            } else {
                                tmpObject.action="search";
                                tmpObject.data = {};
                                if(tmpObject.folder) { tmpObject.data["folder"] = tmpObject.folder }
                                for(var key in searchObject) {
                                    var value=searchObject[key];
                                    if(key.toLowerCase() == "operator" && value.toLowerCase() == "or") {
                                        tmpObject.data.orSearch = true;
                                    } else if(key in { email1: 1, email2: 1,
                                                       email3: 1, display_name: 1,
                                                       first_name: 1, last_name: 1,
                                                       company: 1, categories: 1,
                                                       city_business: 1, street_business: 1,
                                                       department: 1 })
                                    {
                                        tmpObject.data[key] = value;
                                    } else {
                                        alert("This search" + searchObject +"is not supported for contacts");
                                    }
                                }   
                            }
                            break;
						case "mail":
                           for(var key in searchObject) {
                                var value=searchObject[key];
                                if(key.toLowerCase() == "operator" && value.toLowerCase() == "or") {
                                    operator="or";
                                } else {
                                    tmpObject.action="search";
                                    if(!tmpObject.data) { tmpObject.data=[]; }
                                    var tmpCol=oxMapping.convertColumns([trimStr(key)]).columns[0];
                                    if(tmpCol) { tmpObject.data.push({col:  parseInt(tmpCol), pattern : searchObject[key] }); }
                                }    
                            }
                            break;
						case "infostore":
                            for(var key in searchObject) {
                                var value=searchObject[key];
                                if(key.toLowerCase() == "operator" && value.toLowerCase() == "or") {
                                    operator="or";
                                } else if(key == "title" || key =="url" || key =="filename" || key =="version_comment" || key=="categories" || key=="description") {
                                    tmpObject.action="search";
                                    tmpObject.data = { pattern : value };    
                                    break;
                                } else {
                                    alert("This search" + searchObject +"is not supported for tasks");
                                }
							}
                            break;
                    }
                }
				if(oxMapping.requestCriteriaExpired[i].collection.order){
                    var tmpOrder=oxMapping.requestCriteriaExpired[i].collection.order[0];
                    if(oxMapping.convertColumns([trimStr(tmpOrder.sort)]).columns[0]) {
                       tmpObject["sort"]=oxMapping.convertColumns([trimStr(tmpOrder.sort)]).columns[0];
                       tmpObject["order"]= (tmpOrder.order.toLowerCase()=="desc") ? "desc" : "asc";
                    }                   
                }
                
                multipleRequests.push(tmpObject);
				if(!tmpObject.search) {
                    multipleRequests.push(tmpObject2);
				}
            }
        }
        if(oxMapping.requestCriteriaMissingColumn && oxMapping.requestCriteriaMissingColumn.length) {
            var tmpData= [];
            var tmpColumns;
            var tmpObject= { action : "list", module : oxMapping.module, data : tmpData };
            for(var i = 0;i<oxMapping.requestCriteriaMissingColumn.length;i++) {
                if(!tmpColumns) {
                    tmpColumns = oxMapping.convertColumns(oxMapping.requestCriteriaMissingColumn[i].columns);
                    for (var j in tmpColumns) tmpObject[j] = tmpColumns[j].join(",");           
                }
                var tmpString=oxMapping.requestCriteriaMissingColumn[i].object;
                tmpData.push({ folder : tmpString.folder_id || tmpString.folder,
                               id : tmpString.id});
            }
            multipleRequests.push(tmpObject);
        }
        processList(oxMapping.requestListNotInCache);
        processList(oxMapping.requestListExpired);
        processList(oxMapping.requestListMissingColumn);
        function processList(list) {
            if (!list || !list.length) return;
            var tmpData= [];
            var tmpColumns;
			var tmpAction="list";
			var param = { view: "text" };
            for(var i = 0; i < list.length; i++) {
                if(!tmpColumns) {
                    tmpColumns = oxMapping.convertColumns(list[i].columns);
                    for (var i2 = 0; i2 < tmpColumns.columns.length; i2++) {
                        if (!tmpColumns.columns[i2]) {
                            tmpAction="get";
                            list[i].get = true;
                            var stringcolumn = list[i].columns[i2];
                            if (stringcolumn == "attachments_plain") { param.view="text"; }
                            else if (stringcolumn == "attachments_html") { param.view="html"; }
                            else if (stringcolumn == "attachments_html_noimage") { param.view="noimg"; }
                            else if (stringcolumn == "unseen") { param.unseen = "true"; }
                        }
                    }
                }
			    var tmpString=list[i].object;
                if(tmpAction=="get") {
                    var tmpObject= { action : tmpAction, module : oxMapping.module, id : tmpString.id, folder : tmpString.folder_id || tmpString.folder };
                    for (var i3 in param) {
                        tmpObject[i3] = param[i3]; 
                    }
                    multipleRequests.push(tmpObject); 
                } else {
                    var fields = {};
                	fields.folder = tmpString.folder_id || tmpString.folder;
                	fields.id = tmpString.id;
                    if (tmpString.recurrence_position != undefined) {
                    	fields.recurrence_position = tmpString.recurrence_position;
                    }
                    tmpData.push(fields);
                }
            }
            if(tmpAction=="list") {
                var tmpObject= { action: tmpAction, module: oxMapping.module,
                                 data: tmpData };
                for (var i in tmpColumns) tmpObject[i] = tmpColumns[i].join();
				multipleRequests.push(tmpObject);
            }
        }
	    var tmpFn=OXCache.join.add();
		if(multipleRequests.length) {
			if(debug) { console.log("Requests",multipleRequests); }
			(function (
                requestCriteriaNotInCache,
                requestCriteriaExpired,
                requestCriteriaMissingColumn,
                requestListNotInCache,
                requestListExpired,
                requestListMissingColumn) 
            {
           
                tmptime=new Date().getTime();
        
                OXCache.getJSON(AjaxRoot + "/multiple?session="+session +"&continue=true", multipleRequests, function(response) {
                   oxMapping.handleResponse(response,requestCriteriaNotInCache,requestCriteriaExpired,requestCriteriaMissingColumn,requestListNotInCache,requestListExpired,requestListMissingColumn);
                });
            })(clone(oxMapping.requestCriteriaNotInCache),clone(oxMapping.requestCriteriaExpired),
               clone(oxMapping.requestCriteriaMissingColumn),clone(oxMapping.requestListNotInCache),
               clone(oxMapping.requestListExpired),clone(oxMapping.requestListMissingColumn));
       }
	   tmpFn();
    }
}
/**
 * @private 
 * This function is needed, if abstract function request of OXAbstractMapping is mapped to an OXMapping implementation
 * Return function handles response of server.
 * @param oxMapping {OXMapping} OXMapping implementation
 * @return {function} handleResponse function for OXMapping implementation. 
 *      params of the function:
 *      uniqueName {Object} Response object of the server
 *      requestCriteriaNotInCache {Array} Array of Criteria Requests not in Cache.
 *      requestCriteriaExpired {Array} Array of Criteria Requests which are expired.
 *      requestCriteriaMissingColumn {Array} Array of Objects in Criteria Requests with missing columns.
 *      requestListNotInCache {Array} Array of Objects in List Requests which are not in Cache.
 *      requestListExpired {Array} Array of Objects in List Requests which are in Cache but expired.
 *      requestListMissingColumn {Array} Array of Objects in List Requests which are in Cache but have missing columns.
 */
 OXAbstractMapping.handleResponse = function(oxMapping) {
	return function(response,requestCriteriaNotInCache,requestCriteriaExpired,requestCriteriaMissingColumn,requestListNotInCache,requestListExpired,requestListMissingColumn) {
		tmptime=new Date().getTime();
        
        // Execute every registered "before" callback.
		// TODO: determine which of them actually need to be called.
		for (var i in OXCache.modifiedCallbacks) {
            var mc = OXCache.modifiedCallbacks[i];
            if (mc && mc.module == oxMapping.module && mc.before) mc.before();
        }
        
		// shouldn't happen - but just in case the reponse is emtpy!?
		if (!response) {
			// ok, server fails - to prevent js errors we fake an error here
			response = [{"code":"UII-CA001","error_id":"-internal-","category":6,"error_params":[],"error":_("Unexpected: Unable to load data because the server response was empty! Please try again later or contact the system administrator!")}];
		}
		
        var count=0;
		for(var i=0;requestCriteriaNotInCache && i<requestCriteriaNotInCache.length;i++) {
            oxMapping.handleCriteriaNotInCache(requestCriteriaNotInCache[i].collection,response[count],requestCriteriaNotInCache[i].uniqueName);
            count++;
        }
        for(var i=0;requestCriteriaExpired && i<requestCriteriaExpired.length;i++) {
			if(requestCriteriaExpired[i].collection.search) {
				oxMapping.handleCriteriaExpired(requestCriteriaExpired[i].collection,response[count],null,requestCriteriaExpired[i].uniqueName);
				count++;
			} else {
                oxMapping.handleCriteriaExpired(requestCriteriaExpired[i].collection,response[count],response[count+1],requestCriteriaExpired[i].uniqueName);
                count++;		
                count++;
			}
        }
        if(requestCriteriaMissingColumn && requestCriteriaMissingColumn.length) {
            oxMapping.handleCriteriaMissingColumn(requestCriteriaMissingColumn,response[count],requestCriteriaMissingColumn.uniqueName);
            count++;
        }      
        processList(requestListNotInCache, oxMapping.handleListNotInCache);
        processList(requestListExpired, oxMapping.handleListExpired);
        processList(requestListMissingColumn, oxMapping.handleListMissingColumn);
        function processList(list, handler) {
    		if (!list || !list.length) return;
            if (list[0].get) {
                for (var i = 0; i < list.length; i++) {
                    handler.call(oxMapping, list[i], response[count],
                        list[i].uniqueName);
                    count++;
                }
    		} else {
                handler.call(oxMapping, list, response[count], list[0].uniqueName);
                count++;    
    		}
        }
        
		if (response[count] && response[count].error) {
			newServerError(response[count].error);
		}
        
	} 
}
OXAbstractMapping.handleCriteriaNotInCache = function(oxMapping) {
    return function(request,response,uniqueName) {
		if(debug) { console.log("Criteria not in cache:",request,response,uniqueName);	}
		oxMapping.handleCriteria(request,response,null,uniqueName);
    }
}
OXAbstractMapping.handleCriteriaExpired = function(oxMapping) {
    return function(request,response,response2,uniqueName) {
        if(debug) { console.log("Criteria expired:",request,response,response2,uniqueName); }
		oxMapping.handleCriteria(request,response,response2,uniqueName);
    }
}
OXAbstractMapping.handleCriteria= function (oxMapping) {
    return function(request,response,response2,uniqueName) {
        var tmpCollection=oxMapping.getCollection(request,false);
        if(response && response.error || response2 && response2.error) {
            //OXCache.errorHandler(uniqueName,request,response,response2);
            oxMapping.errorHandler(uniqueName,request,response,response2);
            if (tmpCollection) return;
            response = { data: [], timestamp: 0 };
        } 
        if (response.data.length > OXCache.objectSize) {
            OXCache.cachedObjects.free +=   response.data.length
                                          - OXCache.objectSize;
            OXCache.objectSize = response.data.length;
        }
        if(!tmpCollection) {
            var tmpCollection=new OXCollection();
            tmpCollection.criteria=request.criteria;
			tmpCollection.search=request.search
            tmpCollection.order=request.order;
            tmpCollection.last_modified=response.timestamp;
			tmpCollection.timestamp=response.timestamp;
            tmpCollection.lastRequest=new Date().getTime();
            tmpCollection.objects=new Array(response.data.length);
            tmpCollection.map_objects=new LRUKeyList();
			tmpCollection.modified=true;
            for(var i=0;i<response.data.length;i++) {
                var tmpObject=oxMapping.createKeyFromData(response.data[i],response["timestamp"]);
                tmpCollection.objects[i]=tmpObject;
                tmpCollection.map_objects.set(tmpObject,i+1);
                oxMapping.createCacheObject(request.columns,response.data[i],response["timestamp"],tmpObject);
            }
            oxMapping.checkAddCriteria(tmpCollection.objects,request);
            var tmp=OXCache.cachedCollections.set(OXCache.getSequence(),tmpCollection);
            if(tmp) {
                OXCache.cachedCollectionsTrash.push({ data: tmp });
            }
            tmpCollection.check=false;
        } else {
            //TIMESTAMP CAN NOT CHECK LIST EQUALS CACHED LIST
            var tmpList=new LRUKeyList(response.data.length);
            var tmpArray=new Array(response.data.length);
            var changed=false;
            for(var i=0;i<response.data.length;i++) {
                var tmpObject =oxMapping.createKeyFromData(response.data[i],response.timestamp);
                tmpList.set(tmpObject,i+1);
                tmpArray[i]=tmpObject; 
            }
            if(response2 && response2.data.length) {
                for(var i=0;i<response2.data.length;i++) {
                    var tmpObject =oxMapping.createKeyFromData(response2.data[i],response2.timestamp);
                    oxMapping.createCacheObject(request.columns,response2.data[i],response2["timestamp"],tmpObject,true,tmpCollection);
                    changed=true;
                }  
            }
            for(var i=0;i<tmpCollection.objects.length;i++) {
                //DELETE
				if(!tmpList.get(tmpCollection.objects[i])) {
					changed=true;
                    oxMapping.deleteObject(tmpCollection.objects[i],tmpCollection);
                }
			}
		    if(oxMapping.module=="mail" || request.search) {
			     // NEW
				 for(var i=0;i<tmpArray.length;i++) {
				    if(!tmpCollection.map_objects.get(tmpArray[i])) {
					   oxMapping.createCacheObject(request.columns,response.data[i],response["timestamp"],tmpArray[i],true,tmpCollection);
                       changed=true;
					} 
				}
				//UPDATE
				//NOT POSSIBLE
			}
            tmpCollection.objects=tmpArray;
            tmpCollection.order=request.order;
            tmpCollection.map_objects=tmpList;
			if(changed) {
                tmpCollection.last_modified=response.timestamp;
				tmpCollection.timestamp=response.timestamp;
                tmpCollection.modified=true;
            }
			tmpCollection.check=false;
            tmpCollection.lastRequest=new Date().getTime();
        }
    }
}
OXAbstractMapping.checkAddCriteria=function(oxMapping) {
    return function(objects,request) {
        var collections=oxMapping.getSubCollections(request);
        for(var i=0;i<collections.length;i++) {
			if(!oxMapping.equalsCollection(collections[i],request)) {
                if(!collections[i].check) {
                    for(var i2=0;i2<objects.length;i2++) {
                        if(collections[i].map_objects.get(objects[i2])) {
						    collections[i].check=true;
                            break;
                        }
                    }
                }
            }
		}
    }
}
OXAbstractMapping.checkAddEditDeleteObject=function(oxMapping) {
    return function(object,executeCollection) {
        var collections=oxMapping.getSubCollectionsfromObject(object,true);
		for(var i=0;i<collections.length;i++) {
		   if(!executeCollection || !oxMapping.equalsCollection(collections[i],executeCollection)) {
		      if(!collections[i].check) {
                  collections[i].check=true;
              }
		   }
		}
		for(var i in OXCache.modifiedCallbacks) {
            if(OXCache.modifiedCallbacks[i].module == oxMapping.module && !(OXCache.modifiedCallbacks[i].collection.criteria)) {
                for(var count=0;count<OXCache.modifiedCallbacks[i].collection.objects.length;count++) {
                    if(object.equals(OXCache.modifiedCallbacks[i].collection.objects[count])) {
                        OXCache.modifiedCallbacks[i].modified=true;
                        break;                 
                    }
                }
            }
        }
    }
}

OXAbstractMapping.handleCriteriaMissingColumn = function(oxMapping) {
    return function(request,response,uniqueName) {
		if(debug) { console.log("Criteria missing column:",request,response,uniqueName); }
		oxMapping.handleList(request,response,uniqueName);
    }
}
OXAbstractMapping.handleListNotInCache = function(oxMapping) {
    return function(request,response,uniqueName) {
	    if(debug) { console.log("Objects not in Cache:",request,response,uniqueName); }
		oxMapping.handleList(request,response,uniqueName);
    }
}
OXAbstractMapping.handleListExpired = function(oxMapping) {
    return function(request,response,uniqueName) {
        if(debug) { console.log("Objects expired:",request,response,uniqueName); }
		oxMapping.handleList(request,response,uniqueName);
    }
}
OXAbstractMapping.handleListMissingColumn = function(oxMapping) {
    return function(request,response,uniqueName) {
      if(debug) { console.log("Objects columns missing:",request,response,uniqueName, getStackTrace()); }
	  oxMapping.handleList(request,response,uniqueName);
	}
}
OXAbstractMapping.handleList = function(oxMapping) {
	return function(request,response,uniqueName) {
	   if(response && response.error) {
	   	    //OXCache.errorHandler(uniqueName,request,response);
	   	    oxMapping.errorHandler(uniqueName,request,response);
            return;
	   }
	   if(response.data && response.data.constructor == Array) {
	       if(request.length==response.data.length) {
	           for(var i=0;i<request.length;i++) {
                   oxMapping.createCacheObject(request[i].columns,
                    response.data[i], response.timestamp, 0, true);
	           }
	        }  else {
	            for(var i=0;i<request.length;i++) {
	                var deleted=true;
	                for(var i2=0;i2<response.data.length;i2++) {
	                    var key=oxMapping.createKeyFromData(response.data[i2]);
	                    if (   (key.folder_id || key.folder) ==
	                           (   request[i].object.folder_id
	                            || request[i].object.folder)
	                        && key.id == request[i].object.id
	                        && (request[i].object.recurrence_position || 0) ==
	                           (key.recurrence_position || 0))
	                    {
	                        deleted=false;
                            oxMapping.createCacheObject(request[i].columns,
                                response.data[i2],response.timestamp, 0, true);
	                        break;
	                    }   
	                }
	                if(deleted) {
	                    oxMapping.deleteObject(request[i].object);
	                } 
	            }  
	        }
		} else if (response.data) {
			for(var i in request.object) {
				if(response.data[i] === undefined) { response.data[i]=request.object[i]; }
			}
            oxMapping.createCacheObject(request.columns, response.data,
                response.timestamp, 0, true);
		} else {
			// fatal error. something's completely broken on the server.
			triggerEvent("OX_New_Error", 4, _("Internal Fatal Error: Server response contains no or invalid data. Some functions may be disabled or not useable. (OC02=" + request + ")"));
		}
	}
}
OXAbstractMapping.deleteObject = function(oxMapping) {
	return function(object,collection) {
		var smallKeyObject=OXCache.objectShortKey.get(new SmallKey(oxMapping.module,object.id))
        if(smallKeyObject) {
            for(var zaehler=0;zaehler<smallKeyObject.length;zaehler++) {
	            var tmp1=OXCache.cachedObjects.get(smallKeyObject[zaehler]);
	            if(tmp1) {
	                oxMapping.checkAddEditDeleteObject(tmp1,collection);                                
	            } else {
					oxMapping.checkAddEditDeleteObject(smallKeyObject[zaehler],collection);
				}
	            OXCache.cachedObjects.remove(smallKeyObject[zaehler]);
	        }
		}
        OXCache.objectShortKey.remove(new SmallKey(oxMapping.module,object.id));
	}
}
OXAbstractMapping.createCacheObject = function(oxMapping) {
    return function(columns,data,timestamp,key,events,executeCollection) {
		var itsupdate=false;
		var seen=false; //MAIL ONLY
		if(!key) {
		  if(data.constructor == Array) {
		      key=oxMapping.createKeyFromData(data);
		  } else {
		  	  key=oxMapping.createKeyFromObject(data);
		  }
		}
		var tmpObject=key;
		var oldObject=OXCache.cachedObjects.get(key);
		if(!oldObject) {
		  //NEWOBJECT
          if(data.constructor == Array) { 
              for(var i=0;i<columns.length;i++) {
                    var path = columns[i].split("/");
                    if (path.length == 1) {
                        tmpObject[columns[i]] = data[i];
                    } else {
                        var subObj = tmpObject[path[0]];
                        if (!subObj) subObj = tmpObject[path[0]] = {};
                        subObj[path[1]] = data[i];
                    }
              }
          } else {
              for(var i=0;i<columns.length;i++) {
                  if(data[columns[i]]|| data[columns[i]]==0 || data[columns[i]]=="") { tmpObject[columns[i]]=data[columns[i]] }
                  else if(data[columns[i]] === undefined) {
				  	if ((oxMapping.module == "mail") && (columns[i] == "attachments_plain" || columns[i] == "attachments_html" || columns[i] == "attachments_html_noimage")) {
                        tmpObject[columns[i]] = data["attachments"] || null;
						//IMAGES ARE BLOCKED
						if(data["modified"] && data["modified"] == 1) {
                            tmpObject["blocked_images"]=true;
                        } else {
                            tmpObject["blocked_images"]=false;
                        }
                    } else if(columns[i]=="blocked_images") {
                    } else {
                        tmpObject[columns[i]] = null;
                    } 
				  } 
              }
			  if(oxMapping.module == "mail" && (data["unread"] || data["unread"]== 0)) {
                    oUnreadCounterStorage.set(
                        tmpObject.folder_id || tmpObject.folder,
                        data["unread"]);
                }
          }
          tmpObject["timestamp"]=timestamp;
          tmpObject.lastRequest=new Date().getTime();
          var tmp=OXCache.cachedObjects.set(tmpObject,tmpObject);
		  if(tmp) { OXCache.cachedObjectsTrash.set(tmp.key,tmp.data); }
		  /*var myShortKey=new SmallKey(oxMapping.module,tmpObject.id);
		  if(!OXCache.objectShortKey.get(myShortKey)) {
		      OXCache.objectShortKey.set(myShortKey,new Array());
		  }
		  var tmpShortObject=OXCache.objectShortKey.get(myShortKey);
		  tmpShortObject.push(tmpObject);
		  OXCache.objectShortKey.set(myShortKey,tmpShortObject);
		  */
		  if(events) {
             oxMapping.checkAddEditDeleteObject(tmpObject,executeCollection); 
          }
          if(oxMapping.module == "mail" && data["unread"]) {
            oUnreadCounterStorage.set(tmpObject.folder_id || tmpObject.folder,
                                      data["unread"]);
          } 
	  
        } else {
            //EDIT OBJECT
            if(data.constructor == Array) { 
                for(var i=0;i<columns.length;i++) {
                    var path = columns[i].split("/");
                    if (path.length == 1) {
                        tmpObject[columns[i]] = data[i];
                    } else {
                        var subObj = tmpObject[path[0]];
                        if (!subObj) {
                            subObj = oldObject[path[0]] || {};
                            tmpObject[path[0]] = subObj;
                        }
                        subObj[path[1]] = data[i];
                    }
                }  
            } else {
                for(var i=0;i<columns.length;i++) {
				   if(data[columns[i]]|| data[columns[i]]==0 || data[columns[i]]=="") { tmpObject[columns[i]]=data[columns[i]] }
                   else if(data[columns[i]] === undefined) { 
				       if ((oxMapping.module == "mail") && (columns[i] == "attachments_plain" || columns[i] == "attachments_html" || columns[i] == "attachments_html_noimage")) {
                           tmpObject[columns[i]] = data["attachments"] || null;
						   if(data["modified"] && data["modified"] == 1) {
                               tmpObject["blocked_images"]=true;
                           } else {
                            tmpObject["blocked_images"]=false;
						   }
                       } else if(columns[i]=="blocked_images") {
                       } else if (oxMapping.module == "mail" && columns[i] == "level") {
					   } else {
                           tmpObject[columns[i]] = null;
                       } 
				   } 
                }
				//UNREAD COUNTER
                if(oxMapping.module == "mail" && (data["unread"] || data["unread"]== 0)) {
                    oUnreadCounterStorage.set(
                        tmpObject.folder_id || tmpObject.folder,
                        data["unread"]);
                } 
            }
			if(oldObject.last_modified==tmpObject.last_modified) {
			    for(var i=0;i<columns.length;i++) {
                    var path = columns[i].split("/");
                    if (path.length == 1) {
                        var oldO = oldObject, col = path[0];
                        var newvalue = tmpObject[col];
                    } else {
                        var oldO = oldObject[path[0]], col = path[1];
                        var newvalue = tmpObject[path[0]][col];
                    }
                    var value = oldO && oldO[col];
					//FIX FOR MAIL , Arrays should'nt be tested
					if(value !== undefined && newvalue !==undefined && value != newvalue && !itsupdate && typeof(value) != "object") {
						tmpObject.modified=true;
                        OXCache.modifiedObjects.push(tmpObject);
                        itsupdate=true;
					}
                    if (newvalue !== undefined) {
                        if (!oldO) oldO = oldObject[path[0]] = {};
                        oldO[col] = newvalue;
                    }
				}
			    if(!tmpObject.modified) {
				    oldObject.lastRequest=new Date().getTime();
				    if(oldObject.timestamp<timestamp) { oldObject["timestamp"]=timestamp; }
				    OXCache.cachedObjects.set(oldObject,oldObject);
				} else {
					//WORKAROUND FOR LEVEL PARAMETER IN MAIL
					if(oxMapping.module=="mail" && !tmpObject["level"] && (oldObject["level"] || oldObject["level"]==0)) {
						tmpObject["level"]=oldObject["level"];
					}
					tmpObject.lastRequest=new Date().getTime();
                    if(tmpObject.timestamp<timestamp) { tmpObject["timestamp"]=timestamp; }
					//ONLY CHECK CHANGES
					oxMapping.editObjectsInternal([tmpObject],tmpObject);
				}
            } else {
                //CHANGED OBJECT
				tmpObject.lastRequest=new Date().getTime();
				tmpObject.timestamp=timestamp;
                tmpObject.modified=true;
				OXCache.modifiedObjects.push(tmpObject);
                var tmp=OXCache.cachedObjects.set(tmpObject,tmpObject);
				if(tmp) {
                    OXCache.cachedObjectsTrash.set(tmp.key,tmp.data);
                }
                if(events) {
                    oxMapping.checkAddEditDeleteObject(tmpObject,executeCollection); 
                }
            }
        } 
        //TODO EXTRAS
    }  
}
OXAbstractMapping.getCollection = function(oxMapping) {
    return function(collection,first) {
        function search(myArray) {
            for(var colCount in myArray) {
                var tmpCollection=myArray[colCount].data;
                var same=true;
                //Check Criteria
                if(collection.criteria && tmpCollection.criteria) {
                    for(var criteriaCount in tmpCollection.criteria) {
                        if(collection.criteria[criteriaCount] != tmpCollection.criteria[criteriaCount]) {  
                            same=false; break;     
                        }
                    }
                    for(var criteriaCount in collection.criteria) {
                        if(tmpCollection.criteria[criteriaCount] != collection.criteria[criteriaCount]) {  
                            same=false; break;     
                        }
                    }
                } else if (!(!collection.criteria && !tmpCollection.criteria)) {
                    continue;
                }
                //Check Search
                if(collection.search && tmpCollection.search) {
                    for(var searchCount in tmpCollection.search) {
                        if(collection.search[searchCount] != tmpCollection.search[searchCount]) {  
                            same=false; break;     
                        }
                    }
                    for(var searchCount in collection.search) {
                        if(tmpCollection.search[searchCount] != collection.search[searchCount]) {  
                            same=false; break;     
                        }
                    }
                } else if (!(!collection.search && !tmpCollection.search)) {
                    continue;
                }
                
				if(!same) { continue; }
                
                //Check Order
                if(collection.order && tmpCollection.order) {
                    if(collection.order.length!=tmpCollection.order.length) {
                        continue;
                    }
                    var direction=0;
                    for(var i=0;i<collection.order.length;i++) {
                        var tmpObject=collection.order[i];
                        var tmpObject2=tmpCollection.order[i];
                        if(tmpObject.sort!=tmpObject2.sort) {
                            same=false;
                            break;
                        }
                        if(tmpObject.order!=tmpObject2.order) {
                            if(!direction) { direction=-1; }
                            if(direction != -1) { same=false; break; } 
                        } else {
                            if(!direction) { direction=1; }
                            if(direction != 1) { same=false; break; }
                        }
                        if(same!=true) { break; }
                    }
                    if(!same) { continue; }
                } else if (!(!collection.order && !tmpCollection.order)){
                    continue;
                }
                if(first) {
                    var tmp1=OXCache.cachedCollections.get(colCount);
                }
                return tmpCollection;
            }
        }
        return search(OXCache.cachedCollections.cache) || search(OXCache.cachedCollectionsTrash) 
    }
}
OXAbstractMapping.equalsCollection = function (oxMapping) {
	return function(collection1,collection2) {
	    if(collection1.criteria && collection2.criteria) {
            for(var criteriaCount in collection2) {
                if(collection1.criteria[criteriaCount] != collection2.criteria[criteriaCount]) {  return false;  }
            }
            for(var criteriaCount in collection1.criteria) {
                if(collection2.criteria[criteriaCount] != collection1.criteria[criteriaCount]) {  return false;  }
            }
        } else if (!(!collection1.criteria && !collection2.criteria)) { return false; }
        if(collection1.search && collection2.search) {
            for(var criteriaCount in collection2) {
                if(collection1.search[criteriaCount] != collection2.search[criteriaCount]) {  return false;  }
            }
            for(var criteriaCount in collection1.search) {
                if(collection2.search[criteriaCount] != collection1.search[criteriaCount]) {  return false;  }
            }
        } else if (!(!collection1.search && !collection2.search)) { return false; }
        
		if(collection1.order && collection2.order) {
            if(collection1.order.length!=collection2.order.length) { return false; }
            var direction=0;
            for(var i=0;i<collection1.order.length;i++) {
                var tmpObject=collection1.order[i];
                var tmpObject2=collection2.order[i];
                if(tmpObject.sort!=tmpObject2.sort) { return false; }
                if(tmpObject.order!=tmpObject2.order) {
                    if(!direction) { direction=-1; }
                    if(direction != -1) { return false; } 
                } else {
                    if(!direction) { direction=1; }
                    if(direction != 1) { return false; }
                }
            }
        }
		else if (!(!collection1.order && !collection2.order)){
            return false;
        }
        return true;	
	}
}
OXAbstractMapping.getSubCollections = function(oxMapping) {
    return function(collection,ignoreChecked) {
        function search(myArray) {
            var tmpArray=new Array();
            for(var colCount in myArray) {
                var tmpCollection=myArray[colCount].data;
                if(ignoreChecked && tmpCollection.check) { continue; }
                var same=true;
                if(collection.criteria && tmpCollection.criteria) {
                    //Check Criteria
                    for(var criteriaCount in tmpCollection.criteria) {
                        if(collection.criteria[criteriaCount] !== undefined && collection.criteria[criteriaCount] != tmpCollection.criteria[criteriaCount]) {  
                            same=false; break;     
                        }
                    }
                 }
				 if(collection.search && tmpCollection.search) {
                    //Check Criteria
                    for(var criteriaCount in tmpCollection.search) {
                        if(collection.search[criteriaCount] !== undefined && collection.search[criteriaCount] != tmpCollection.search[criteriaCount]) {  
                            same=false; break;     
                        }
                    }
                 }
                 if(!same) { continue; }
                tmpArray.push(tmpCollection);
            }
            return tmpArray;
        }
        return search(OXCache.cachedCollections.cache).concat(search(OXCache.cachedCollectionsTrash)); 
    }
}
OXAbstractMapping.getSubCollectionsfromObject = function(oxMapping) {
    return function(object,ignoreChecked) {
        function search(myArray) {
            var tmpArray=new Array();
            for(var colCount in myArray) {
                var tmpCollection=myArray[colCount].data;
                if(ignoreChecked && tmpCollection.check) { continue; }
                var same=true;
                if(tmpCollection.criteria) {
                   for(var criteriaCount in tmpCollection.criteria) {
                        if(same!=true) { break; }
                        switch(criteriaCount) {
                            default:
                                if(object[criteriaCount] !== undefined && object[criteriaCount] != tmpCollection.criteria[criteriaCount]) {  
                                    same=false;
                                    break;     
                                }
                                break;
                        }
                    }
                 }
                 if(!same) { continue; }
                tmpArray.push(tmpCollection);
            }
            return tmpArray;
        }
        return search(OXCache.cachedCollections.cache).concat(search(OXCache.cachedCollectionsTrash)); 
    }
}

OXAbstractMapping.convertColumns = function(oxMapping) {
    return function(columns) {
   	    var retval = { columns: [] };
        for(var i = 0; i < columns.length; i++) {
            var col = trimStr(columns[i]);
            var tmpColumn=oxMapping.stringmapping[col];
            if(!tmpColumn) {
                var path = col.split("/");
                // TODO: arbitrary path lengths
                if (path.length == 2) {
                    if (!(path[0] in retval)) retval[path[0]] = [];
                    retval[path[0]].push(path[1]);
                    continue;
                }
                if (oxMapping.alternative) {
                    tmpColumn=oxMapping.alternative[col];
                }
                if(typeof tmpColumn == "function") tmpColumn = false;
			}
			if(!tmpColumn && tmpColumn != false) { 
			    if(debug) { alert("Column "+columns[i]+" not found in Mapping")}
			}
            retval.columns.push(tmpColumn);
		}
		return retval;
    }
}
OXAbstractMapping.setTag = function(oxMapping) {
	return function(tag,ids) {
		var multipleObject = [];
		for(var i = 0; i < ids.length; i++) {
            multipleObject.push({ 
                 action    : "update", 
                 module    : ids[i].module, 
                 timestamp : ids[i].timestamp || 0, 
                 id        : ids[i].id, 
                 folder    : ids[i].folder_id || ids[i].folder,
                 data      : { color_label : tag } 
            });
			if(ids[i].recurrence_position && ids[i].recurrence_position > 0) {
                multipleObject[i] = ids[i].recurrence_position;
            }
        }
        json.put(AjaxRoot + "/multiple?session=" + session + "&continue=true",multipleObject,null,
            function(callback){
            	var timestamp = 0;
                for(var i=0;i<callback.length;i++) {
                    if(callback[i].error) {
						newServerError(callback[i]);
                        return;
                    } else if (callback[i].timestamp && callback[i].timestamp > timestamp) {
                            timestamp = callback[i].timestamp;
                    }
                }
                oxMapping.editObjectsInternal(ids,{ color_label : tag , timestamp : timestamp });                       
            }
        );
 	}
}




  /////////////////////////////////////
 //   OX Mapping Implementation     //
/////////////////////////////////////
function OXTaskMapping() {}
OXTaskMapping.mandatoryfields=["folder_id","id","last_modified", "created_by", "creation_date"];
OXTaskMapping.idmapping= {
    "1": "id", "2": "created_by", "3": "modified_by", "4": "creation_date", "5": "last_modified", 
    "20": "folder_id", "100": "categories", "101": "private_flag", "104" : "number_of_attachments", "200": "title", "201": "start_date", 
    "202": "end_date", "203": "note", "204": "alarm",   
    "209": "recurrence_type", "212": "days", "213": "days_in_month", 
    "214": "month", "215": "internal", "216": "until",  "220": "participants", "221" : "users",
    "300": "status", "301": "percent_completed", "302": "actual_costs", 
    "303": "actual_duration", "305": "billing_information", 
    "307": "target_costs", "308": "target_duration", "309": "priority", 
    "312": "currency", "313": "trip_meter", "314": "companies", 
    "315": "date_completed", "102": "color_label"
} 
//"207": "recurrence_position", 208": "recurrence_date_position","217": "notification", "306": "project_id"
// ???
// "310": "duration_type", "311": "type_of_costs"

OXTaskMapping.stringmapping=switchStringObject(OXTaskMapping.idmapping);
OXTaskMapping.module="tasks";
OXTaskMapping.createKeyFromData = function(myArray,timestamp) {
     var tmp=new OXTaskObjectCache();
     tmp.module = OXTaskMapping.module;
     tmp.folder_id=myArray[0];
     tmp.id=myArray[1];
     tmp.created_by = myArray[3];
	 if(timestamp) { tmp.timestamp = timestamp }
     return tmp;     
}
OXTaskMapping.createKeyFromObject = function(myObject) {
     var tmp=new OXTaskObjectCache();
     tmp.module = OXTaskMapping.module;
     tmp.folder_id=myObject["folder_id"];
     tmp.id=myObject["id"];
     tmp.created_by = myObject.created_by;
     return tmp;     
}

OXTaskMapping.objectConstructor=OXTaskObjectCache;
OXTaskMapping.get = OXAbstractMapping.get(OXTaskMapping);
OXTaskMapping.execute = OXAbstractMapping.execute(OXTaskMapping);
OXTaskMapping.buildResponse = OXAbstractMapping.buildResponse(OXTaskMapping);
OXTaskMapping.criteriaRequest= OXAbstractMapping.criteriaRequest(OXTaskMapping);
OXTaskMapping.listRequest= OXAbstractMapping.listRequest(OXTaskMapping);
OXTaskMapping.addCriteriaNotInCache = OXAbstractMapping.addCriteriaNotInCache(OXTaskMapping);
OXTaskMapping.addCriteriaExpired = OXAbstractMapping.addCriteriaExpired(OXTaskMapping);
OXTaskMapping.addCriteriaMissingColumn = OXAbstractMapping.addCriteriaMissingColumn(OXTaskMapping);
OXTaskMapping.addListNotInCache = OXAbstractMapping.addListNotInCache(OXTaskMapping);
OXTaskMapping.addListExpired = OXAbstractMapping.addListExpired(OXTaskMapping);
OXTaskMapping.addListMissingColumn = OXAbstractMapping.addListMissingColumn(OXTaskMapping);
OXTaskMapping.request = OXAbstractMapping.request(OXTaskMapping);
OXTaskMapping.handleResponse = OXAbstractMapping.handleResponse(OXTaskMapping);
OXTaskMapping.handleCriteriaNotInCache = OXAbstractMapping.handleCriteriaNotInCache(OXTaskMapping);
OXTaskMapping.handleCriteriaExpired = OXAbstractMapping.handleCriteriaExpired(OXTaskMapping);
OXTaskMapping.handleCriteriaMissingColumn = OXAbstractMapping.handleCriteriaMissingColumn(OXTaskMapping);
OXTaskMapping.handleCriteria=OXAbstractMapping.handleCriteria(OXTaskMapping);
OXTaskMapping.handleListNotInCache = OXAbstractMapping.handleListNotInCache(OXTaskMapping);
OXTaskMapping.handleListExpired = OXAbstractMapping.handleListExpired(OXTaskMapping);
OXTaskMapping.handleListMissingColumn = OXAbstractMapping.handleListMissingColumn(OXTaskMapping);
OXTaskMapping.handleList = OXAbstractMapping.handleList(OXTaskMapping);
OXTaskMapping.deleteObject = OXAbstractMapping.deleteObject(OXTaskMapping);
OXTaskMapping.getCollection= OXAbstractMapping.getCollection(OXTaskMapping);
OXTaskMapping.convertColumns = OXAbstractMapping.convertColumns(OXTaskMapping);
OXTaskMapping.createCacheObject = OXAbstractMapping.createCacheObject(OXTaskMapping);
OXTaskMapping.checkAddCriteria = OXAbstractMapping.checkAddCriteria(OXTaskMapping);
OXTaskMapping.checkAddEditDeleteObject= OXAbstractMapping.checkAddEditDeleteObject(OXTaskMapping);
OXTaskMapping.getSubCollectionsfromObject = OXAbstractMapping.getSubCollectionsfromObject(OXTaskMapping);
OXTaskMapping.getSubCollections = OXAbstractMapping.getSubCollections(OXTaskMapping);
OXTaskMapping.equalsCollection = OXAbstractMapping.equalsCollection(OXTaskMapping);
OXTaskMapping.errorHandler = OXCache.errorHandler;
OXTaskMapping.deleteObjectsInternal = function(objects) {
    var tmpFn=OXCache.join.add();
    var oxMapping=OXTaskMapping;

    // Calling all "before" callbacks.
    for(var i in OXCache.modifiedCallbacks) {
        var mc = OXCache.modifiedCallbacks[i];
        if (mc.module == oxMapping.module && mc.before) mc.before();
    }
    
    var tmpCollect=[];
    for(var mail=0;mail<objects.length;mail++) {
        var collections=oxMapping.getSubCollectionsfromObject(objects[mail],true);
        for(var count=0;count<collections.length;count++) {
            if(collections[count].check) { continue; } 
            var index=collections[count].map_objects.get(objects[mail]);
            if(index) {
                 collections[count].modified=true;
                 if(!collections[count].tmpIndexes) { collections[count].tmpIndexes=[]; }
                 collections[count].tmpIndexes.push(index);
                 tmpCollect.push(collections[count]);
                 collections[count].modifiedtmp=true;
             }               
        }
    }
    
    for(var count=0;count<tmpCollect.length;count++) {
        if(tmpCollect[count].modifiedtmp) {
            var indexes=tmpCollect[count].tmpIndexes;
            indexes.sort(function(a,b) { return a-b; });
            for(var i=indexes.length-1;i>=0;i--) {
                tmpCollect[count].objects.splice(indexes[i]-1,1);
            }
            delete tmpCollect[count].tmpIndexes;
            tmpCollect[count].map_objects=new LRUKeyList();
            for(var index=0;index<tmpCollect[count].objects.length;index++) { 
                tmpCollect[count].map_objects.set(tmpCollect[count].objects[index],index+1) 
            }
            tmpCollect[count].modifiedtmp=false;
        }
    }
    tmpFn();
}

OXTaskMapping.deleteObjects = function(objects) {
    var oxMapping=OXTaskMapping;
    for(var i=0;i<objects.length;i++) {
        var tmpTime=objects[i]["timestamp"]; 
        if (!tmpTime) {
            var tmpObject = OXCache.cachedObjects.get(objects[i]);
            if(tmpObject) {
                tmpTime=tmpObject.timestamp;
            }
        }
		var request = new Array();
        for (var i in objects) {
            request.push({
                module: "tasks", 
                action: "delete",
                folder: objects[i]["folder_id"],
                timestamp: objects[i]["timestamp"],
                data: { folder : objects[i]["folder_id"] , id : objects[i]["id"] }
            });
        }
    }
    json.put(AjaxRoot + "/multiple?session=" + session + "&continue=true",
        request, null, function(reply) {
            var tmpFn=OXCache.join.add();
            var error=new Array();
            for(var i=0;i<reply.length;i++) {
                if(reply[i].error) {
                    newServerError(reply[i]);
                    error.push(i);   
                }  
            }
            if(!error.length) { oxMapping.deleteObjectsInternal(objects); }
            tmpFn();        
        }
    );
}
OXTaskMapping.editObjectsInternal = function(oldObjects,changes,checkExist) {
    var oxMapping=OXTaskMapping;
    var tmpFn=OXCache.join.add();
    for(var myObject in oldObjects) {
        var tmpKeyOld=oxMapping.createKeyFromObject(oldObjects[myObject]);
        var collections=oxMapping.getSubCollectionsfromObject(oldObjects[myObject],true);
        //TODO Check Changes in Collection
        for(var i=0;i<collections.length;i++) {
            if(!collections[i].check) {
                collections[i].check=true;
            } 
        }
        for(var i in OXCache.modifiedCallbacks) {
            if(OXCache.modifiedCallbacks[i].module == oxMapping.module && !(OXCache.modifiedCallbacks[i].collection.criteria)) {
                for(var count=0;count<OXCache.modifiedCallbacks[i].collection.objects.length;count++) {
                    if(tmpKeyOld.equals(OXCache.modifiedCallbacks[i].collection.objects[count])) {
                        var mc = OXCache.modifiedCallbacks[i];
                        if (!mc.modified && mc.before) mc.before();
                        mc.modified=true;
                    }
                }
            }
        }
        var tmpObjectOld=OXCache.cachedObjects.get(tmpKeyOld);
        if(tmpObjectOld) {
           if(changes) {
              for(var i in changes) {
                  if(!checkExist || tmpObjectOld[i]!== undefined) {
                      if(i != "notification") {
                          tmpObjectOld[i]=changes[i];
                      }
                  }
              }
           }
       }
       OXCache.cachedObjects.set(tmpKeyOld,tmpObjectOld);    
    }  
    //TODO CHECK CHANGE FOLDER
    tmpFn();
}

OXTaskMapping.editObjects = function(oldObjects,changes) {
    setTimeout(function() {
       var tmpFn=OXCache.join.add();
       OXTaskMapping.editObjectsInternal(oldObjects,changes);
       tmpFn(); 
    },0);
}
OXTaskMapping.createObject = function(newObject) {
    setTimeout(function() {
        var tmpFn=OXCache.join.add();
        OXTaskMapping.createObjectsInternal([newObject]);
        tmpFn();
    },0);
}
OXTaskMapping.createObjectsInternal = function(newObjects) {
    setTimeout(function() {
        var oxMapping=OXTaskMapping;

        // Calling all "before" callbacks.
        for(var i in OXCache.modifiedCallbacks) {
            var mc = OXCache.modifiedCallbacks[i];
            if (mc.module == oxMapping.module && mc.before) mc.before();
        }
        
        var tmpFn=OXCache.join.add();
        for(var object in newObjects) {
            var collections=oxMapping.getSubCollectionsfromObject(newObjects[object],true);
            for(var i=0;i<collections.length;i++) {
                if(!collections[i].check) {
                    collections[i].check=true;
                }
            }
        }
        tmpFn();
    },0);
}
OXTaskMapping.setTag = OXAbstractMapping.setTag(OXTaskMapping);
OXCache.setMapping("tasks",OXTaskMapping);




function OXCalendarMapping() {}
OXCalendarMapping.mandatoryfields=["folder_id", "id", "recurrence_position",
                                   "last_modified", "created_by"];
OXCalendarMapping.idmapping= { 
    "1": "id", "2": "created_by", "3": "modified_by", "4": "creation_date", "5":"last_modified",
    "20": "folder_id", "100": "categories", "101": "private_flag", "102":"color_label", 
    "104": "number_of_attachments", "200": "title", 
    "201": "start_date", "202": "end_date", "203": "note", "204": "alarm",  
    "207": "recurrence_position", "208": "recurrence_date_position", "209": "recurrence_type",
    "212": "days", "213": "days_in_month", "214": "month", "215": "interval", "216": "until",
    "220": "participants", "221": "users", "400": "location",
    "401": "full_time", "402": "shown_as"
}
//"217": notification"
OXCalendarMapping.module="calendar";
OXCalendarMapping.createKeyFromData = function(myArray,timestamp) {
     var tmp=new OXCalendarObjectCache();
     tmp.module = OXCalendarMapping.module;
     tmp.folder_id=myArray[0];
     tmp.id=myArray[1];
     tmp.recurrence_position=myArray[2] || 0;
     tmp.created_by = myArray[4];
	  if(timestamp) { tmp.timestamp = timestamp }
     return tmp;     
}
OXCalendarMapping.createKeyFromObject = function(myObject) {
     var tmp=new OXCalendarObjectCache();
     tmp.module = OXCalendarMapping.module;
     tmp.folder_id=myObject["folder_id"];
     tmp.id=myObject["id"];
	 tmp.recurrence_position=myObject.recurrence_position;
     tmp.created_by = myObject.created_by;
     return tmp;     
}

OXCalendarMapping.objectConstructor=OXCalendarObjectCache;
OXCalendarMapping.stringmapping=switchStringObject(OXCalendarMapping.idmapping);
OXCalendarMapping.get = OXAbstractMapping.get(OXCalendarMapping);
OXCalendarMapping.execute = OXAbstractMapping.execute(OXCalendarMapping);
OXCalendarMapping.buildResponse = OXAbstractMapping.buildResponse(OXCalendarMapping);
OXCalendarMapping.criteriaRequest= OXAbstractMapping.criteriaRequest(OXCalendarMapping);
OXCalendarMapping.listRequest= OXAbstractMapping.listRequest(OXCalendarMapping);
OXCalendarMapping.addCriteriaNotInCache = OXAbstractMapping.addCriteriaNotInCache(OXCalendarMapping);
OXCalendarMapping.addCriteriaExpired = OXAbstractMapping.addCriteriaExpired(OXCalendarMapping);
OXCalendarMapping.addCriteriaMissingColumn = OXAbstractMapping.addCriteriaMissingColumn(OXCalendarMapping);
OXCalendarMapping.addListNotInCache = OXAbstractMapping.addListNotInCache(OXCalendarMapping);
OXCalendarMapping.addListExpired = OXAbstractMapping.addListExpired(OXCalendarMapping);
OXCalendarMapping.addListMissingColumn = OXAbstractMapping.addListMissingColumn(OXCalendarMapping);
OXCalendarMapping.request = OXAbstractMapping.request(OXCalendarMapping);
OXCalendarMapping.handleResponse = OXAbstractMapping.handleResponse(OXCalendarMapping);
OXCalendarMapping.handleCriteriaNotInCache = OXAbstractMapping.handleCriteriaNotInCache(OXCalendarMapping);
OXCalendarMapping.handleCriteriaExpired = OXAbstractMapping.handleCriteriaExpired(OXCalendarMapping);
OXCalendarMapping.handleCriteriaMissingColumn = OXAbstractMapping.handleCriteriaMissingColumn(OXCalendarMapping);
OXCalendarMapping.handleCriteria=OXAbstractMapping.handleCriteria(OXCalendarMapping);
OXCalendarMapping.handleListNotInCache = OXAbstractMapping.handleListNotInCache(OXCalendarMapping);
OXCalendarMapping.handleListExpired = OXAbstractMapping.handleListExpired(OXCalendarMapping);
OXCalendarMapping.handleListMissingColumn = OXAbstractMapping.handleListMissingColumn(OXCalendarMapping);
OXCalendarMapping.handleList = OXAbstractMapping.handleList(OXCalendarMapping);
OXCalendarMapping.deleteObject = OXAbstractMapping.deleteObject(OXCalendarMapping);
OXCalendarMapping.getCollection= OXAbstractMapping.getCollection(OXCalendarMapping);
OXCalendarMapping.convertColumns = OXAbstractMapping.convertColumns(OXCalendarMapping);
OXCalendarMapping.createCacheObject = OXAbstractMapping.createCacheObject(OXCalendarMapping);
OXCalendarMapping.checkAddCriteria = OXAbstractMapping.checkAddCriteria(OXCalendarMapping);
OXCalendarMapping.checkAddEditDeleteObject= OXAbstractMapping.checkAddEditDeleteObject(OXCalendarMapping);
OXCalendarMapping.getSubCollectionsfromObject = OXAbstractMapping.getSubCollectionsfromObject(OXCalendarMapping);
OXCalendarMapping.getSubCollections = OXAbstractMapping.getSubCollections(OXCalendarMapping);
OXCalendarMapping.equalsCollection = OXAbstractMapping.equalsCollection(OXCalendarMapping);
OXCalendarMapping.errorHandler = OXCache.errorHandler;

OXCalendarMapping.editObjectsInternal = function(oldObjects,changes,checkExist) {
    var oxMapping=OXCalendarMapping;
    var tmpFn=OXCache.join.add();
    for(var myObject in oldObjects) {
        var tmpKeyOld=oxMapping.createKeyFromObject(oldObjects[myObject]);
        var collections=oxMapping.getSubCollectionsfromObject(oldObjects[myObject],true);
        //TODO Check Changes in Collection
        for(var i=0;i<collections.length;i++) {
            if(!collections[i].check) {
                collections[i].check=true;
            } 
        }
        for(var i in OXCache.modifiedCallbacks) {
            if(OXCache.modifiedCallbacks[i].module == oxMapping.module && !(OXCache.modifiedCallbacks[i].collection.criteria)) {
                for(var count=0;count<OXCache.modifiedCallbacks[i].collection.objects.length;count++) {
                    if(tmpKeyOld.equals(OXCache.modifiedCallbacks[i].collection.objects[count])) {
                        var mc = OXCache.modifiedCallbacks[i];
                        if (!mc.modified && mc.before) mc.before();
                        mc.modified=true;
                    }
                }
            }
        }
        var tmpObjectOld=OXCache.cachedObjects.get(tmpKeyOld);
        if(tmpObjectOld) {
           if(changes) {
              for(var i in changes) {
                  if(!checkExist || tmpObjectOld[i]!== undefined) {
                      if(i != "notification") {
                          tmpObjectOld[i]=changes[i];
                      }
                  }
              }
           }
       }
       OXCache.cachedObjects.set(tmpKeyOld,tmpObjectOld);    
    }  
    //TODO CHECK CHANGE FOLDER
    tmpFn();
}

OXCache.setMapping("calendar", OXCalendarMapping);

function OXContactMapping() {}
OXContactMapping.mandatoryfields = ["folder_id", "id", "last_modified",
                                    "created_by"];
OXContactMapping.idmapping= { 
    "1": "id", "2": "created_by", "3": "modified_by", "4": "creation_date", "5": "last_modified", 
    "20": "folder_id", "100": "categories", "101": "private_flag", "500": "display_name", "501": "first_name",
    "502": "last_name", "503": "second_name", "504": "suffix", "505": "title", "506": "street_home",
    "507": "postal_code_home", "508": "city_home", "509": "state_home", "510": "country_home", 
    "511": "birthday", "512": "marital_status", "513": "number_of_children", "514": "profession", 
    "515": "nickname", "516": "spouse_name", "517": "anniversary", "518": "note", "519": "department", 
    "520": "position", "521": "employee_type", "522": "room_number", "523": "street_business", 
    "525": "postal_code_business", "526": "city_business", "527": "state_business", "528": "country_business",
    "529": "number_of_employees", "530": "sales_volume", "531": "tax_id", "532": "commercial_register",
    "533": "branches", "534": "business_category", "535": "info", "536": "manager_name", 
    "537": "assistant_name", "538": "street_other", "539": "city_other", "540": "postal_code_other", 
    "541": "country_other", "542": "telephone_business1", "543": "telephone_business2", "544": "fax_business",
    "545": "telephone_callback", "546": "telephone_car", "547": "telephone_company", "548": "telephone_home1",
    "549": "telephone_home2", "550": "fax_home", "551": "cellular_telephone1", "552": "cellular_telephone2",
    "553": "telephone_other", "554": "fax_other", "555": "email1", "556": "email2", "557": "email3",
    "558": "url", "559": "telephone_isdn", "560": "telephone_pager", "561": "telephone_primary",
    "562": "telephone_radio", "563": "telephone_telex", "564": "telephone_ttytdd",
    "565": "instant_messenger1", "566": "instant_messenger2", "567": "telephone_ip",
    "568": "telephone_assistant", "569": "company", /* "570": "image1",*/ "571": "userfield01",
    "572": "userfield02", "573": "userfield03", "574": "userfield04", "575": "userfield05",
    "576": "userfield06", "577": "userfield07", "578": "userfield08", "579": "userfield09",
    "580": "userfield10", "581": "userfield11", "582": "userfield12", "583": "userfield13",
    "584": "userfield14", "585": "userfield15", "586": "userfield16", "587": "userfield17",
    "588": "userfield18", "589": "userfield19", "590": "userfield20",
    "592": "distribution_list", "594": "number_of_distribution_list",
    "596": "contains_image1", "597": "image_last_modified", "598": "state_other",
    "599": "file_as", "104": "number_of_attachments", "601": "image1_content_type", "602": "mark_as_distributionlist",
    "605": "default_address", "102": "color_label", "524": "internal_userid",
    "606": "image1_url"
}
OXContactMapping.stringmapping=switchStringObject(OXContactMapping.idmapping);
OXContactMapping.module="contacts";
OXContactMapping.createKeyFromData = function(myArray,timestamp) {
     var tmp=new OXContactObjectCache();
     tmp.module = OXContactMapping.module;
     tmp.folder_id=myArray[0];
     tmp.id=myArray[1];
     tmp.created_by = myArray[3];
     if(timestamp) { tmp.timestamp = timestamp }
     return tmp;     
}
OXContactMapping.createKeyFromObject = function(myObject) {
     var tmp=new OXContactObjectCache();
     tmp.module = OXContactMapping.module;
     tmp.folder_id=myObject["folder_id"];
     tmp.id=myObject["id"];
     tmp.created_by = myObject.created_by;
     return tmp;     
}


OXContactMapping.objectConstructor=OXContactObjectCache;
OXContactMapping.get = OXAbstractMapping.get(OXContactMapping);
OXContactMapping.execute = OXAbstractMapping.execute(OXContactMapping);
OXContactMapping.buildResponse = OXAbstractMapping.buildResponse(OXContactMapping);
OXContactMapping.criteriaRequest= OXAbstractMapping.criteriaRequest(OXContactMapping);
OXContactMapping.listRequest= OXAbstractMapping.listRequest(OXContactMapping);
OXContactMapping.addCriteriaNotInCache = OXAbstractMapping.addCriteriaNotInCache(OXContactMapping);
OXContactMapping.addCriteriaExpired = OXAbstractMapping.addCriteriaExpired(OXContactMapping);
OXContactMapping.addCriteriaMissingColumn = OXAbstractMapping.addCriteriaMissingColumn(OXContactMapping);
OXContactMapping.addListNotInCache = OXAbstractMapping.addListNotInCache(OXContactMapping);
OXContactMapping.addListExpired = OXAbstractMapping.addListExpired(OXContactMapping);
OXContactMapping.addListMissingColumn = OXAbstractMapping.addListMissingColumn(OXContactMapping);
OXContactMapping.request = OXAbstractMapping.request(OXContactMapping);
OXContactMapping.handleResponse = OXAbstractMapping.handleResponse(OXContactMapping);
OXContactMapping.handleCriteriaNotInCache = OXAbstractMapping.handleCriteriaNotInCache(OXContactMapping);
OXContactMapping.handleCriteriaExpired = OXAbstractMapping.handleCriteriaExpired(OXContactMapping);
OXContactMapping.handleCriteriaMissingColumn = OXAbstractMapping.handleCriteriaMissingColumn(OXContactMapping);
OXContactMapping.handleCriteria=OXAbstractMapping.handleCriteria(OXContactMapping);
OXContactMapping.handleListNotInCache = OXAbstractMapping.handleListNotInCache(OXContactMapping);
OXContactMapping.handleListExpired = OXAbstractMapping.handleListExpired(OXContactMapping);
OXContactMapping.handleListMissingColumn = OXAbstractMapping.handleListMissingColumn(OXContactMapping);
OXContactMapping.handleList = OXAbstractMapping.handleList(OXContactMapping);
OXContactMapping.deleteObject = OXAbstractMapping.deleteObject(OXContactMapping);
OXContactMapping.getCollection= OXAbstractMapping.getCollection(OXContactMapping);
OXContactMapping.convertColumns = OXAbstractMapping.convertColumns(OXContactMapping);
OXContactMapping.createCacheObject = OXAbstractMapping.createCacheObject(OXContactMapping);
OXContactMapping.checkAddCriteria = OXAbstractMapping.checkAddCriteria(OXContactMapping);
OXContactMapping.checkAddEditDeleteObject= OXAbstractMapping.checkAddEditDeleteObject(OXContactMapping);
OXContactMapping.getSubCollectionsfromObject = OXAbstractMapping.getSubCollectionsfromObject(OXContactMapping);
OXContactMapping.getSubCollections = OXAbstractMapping.getSubCollections(OXContactMapping);
OXContactMapping.equalsCollection = OXAbstractMapping.equalsCollection(OXContactMapping);
OXContactMapping.errorHandler = OXCache.errorHandler;

OXContactMapping.deleteObjectsInternal = function(objects) {
    var tmpFn=OXCache.join.add();
    var oxMapping=OXContactMapping;

    // Calling all "before" callbacks.
    for(var i in OXCache.modifiedCallbacks) {
        var mc = OXCache.modifiedCallbacks[i];
        if (mc.module == oxMapping.module && mc.before) mc.before();
    }
    
    var tmpCollect=[];
    for(var mail=0;mail<objects.length;mail++) {
        var collections=oxMapping.getSubCollectionsfromObject(objects[mail],true);
        for(var count=0;count<collections.length;count++) {
            if(collections[count].check) { continue; } 
            var index=collections[count].map_objects.get(objects[mail]);
            if(index) {
                 collections[count].modified=true;
                 if(!collections[count].tmpIndexes) { collections[count].tmpIndexes=[]; }
                 collections[count].tmpIndexes.push(index);
                 tmpCollect.push(collections[count]);
                 collections[count].modifiedtmp=true;
             }               
        }
    }
    for(var count=0;count<tmpCollect.length;count++) {
        if(tmpCollect[count].modifiedtmp) {
                var indexes=tmpCollect[count].tmpIndexes;
                indexes.sort(function(a,b) { return a-b; });
                for(var i=indexes.length-1;i>=0;i--) {
                    tmpCollect[count].objects.splice(indexes[i]-1,1);
                }
                delete tmpCollect[count].tmpIndexes;
            tmpCollect[count].map_objects=new LRUKeyList();
            for(var index=0;index<tmpCollect[count].objects.length;index++) { 
                tmpCollect[count].map_objects.set(tmpCollect[count].objects[index],index+1) 
            }
            tmpCollect[count].modifiedtmp=false;
        }
    }
    tmpFn();
}

OXContactMapping.deleteObjects = function(objects) {
    var oxMapping=OXContactMapping;
    var request = new Array();
    for (var i in objects) {
		var tmpTime=objects[i]["timestamp"]; 
        if (!tmpTime) {
            var tmpObject = OXCache.cachedObjects.get(objects[i]);
            if(tmpObject) {
                tmpTime=tmpObject.timestamp;
            }
        }
        request.push({
            module: oxMapping.module, 
            action: "delete",
            folder: objects[i]["folder_id"],
            timestamp: tmpTime ,
            data: { folder : objects[i]["folder_id"] , id : objects[i]["id"] }
        });
    }
    json.put(AjaxRoot + "/multiple?session=" + session + "&continue=true",
        request, null, function(reply) {
            var tmpFn=OXCache.join.add();
            var error=new Array();
            for(var i=0;i<reply.length;i++) {
                if(reply[i].error) {
                    newServerError(reply[i]);
                    error.push(i);   
                }  
            }
            if(!error.length) {
                oxMapping.deleteObjectsInternal(objects);
            }
            tmpFn();            
        }
    );
}
OXContactMapping.editObjects = function(oldObjects,changes) {
    setTimeout(function() {
       var tmpFn=OXCache.join.add();
       OXContactMapping.editObjectsInternal(oldObjects,changes);
       tmpFn(); 
    },0);
}
OXContactMapping.editObjectsInternal = function(oldObjects,changes,checkExist) {
    var oxMapping=OXContactMapping;
    var tmpFn=OXCache.join.add();
    for(var myObject in oldObjects) {
        var tmpKeyOld=oxMapping.createKeyFromObject(oldObjects[myObject]);
        var collections=oxMapping.getSubCollectionsfromObject(oldObjects[myObject],true);
        //TODO Check Changes in Collection
        for(var i=0;i<collections.length;i++) {
            if(!collections[i].check) {
                collections[i].check=true;
            } 
        }
        for(var i in OXCache.modifiedCallbacks) {
            if(OXCache.modifiedCallbacks[i].module == oxMapping.module && !(OXCache.modifiedCallbacks[i].collection.criteria)) {
                for(var count=0;count<OXCache.modifiedCallbacks[i].collection.objects.length;count++) {
                    if(tmpKeyOld.equals(OXCache.modifiedCallbacks[i].collection.objects[count])) {
                        var mc = OXCache.modifiedCallbacks[i];
                        if (!mc.modified && mc.before) mc.before();
                        mc.modified=true;    
                    }
                }
            }
        }
        var tmpObjectOld=OXCache.cachedObjects.get(tmpKeyOld);
        if(tmpObjectOld) {
           if(changes) {
              for(var i in changes) {
                  if(!checkExist || tmpObjectOld[i]!== undefined) {
                      if(i != "notification") {
                          tmpObjectOld[i]=changes[i];
                      }
                  }
              }
           }
       }
       OXCache.cachedObjects.set(tmpKeyOld,tmpObjectOld);    
    }  
    //TODO CHECK CHANGE FOLDER
    tmpFn();
}

OXContactMapping.createObject = function(newObject) {
    setTimeout(function() {
        var tmpFn=OXCache.join.add();
        OXContactMapping.createObjectsInternal([newObject]);
        tmpFn();
    },0);
}
OXContactMapping.createObjectsInternal = function(newObjects) {
    setTimeout(function() {
        var oxMapping=OXContactMapping;

        // Calling all "before" callbacks.
        for(var i in OXCache.modifiedCallbacks) {
            var mc = OXCache.modifiedCallbacks[i];
            if (mc.module == oxMapping.module && mc.before) mc.before();
        }
        
        var tmpFn=OXCache.join.add();
        for(var object in newObjects) {
            var collections=oxMapping.getSubCollectionsfromObject(newObjects[object],true);
            for(var i=0;i<collections.length;i++) {
                if(!collections[i].check) {
                    collections[i].check=true;
                }
            }
        }
        tmpFn();
    },0);
}
OXContactMapping.setTag = OXAbstractMapping.setTag(OXContactMapping);

OXCache.setMapping("contacts",OXContactMapping);

function OXMailMapping() {} 
OXMailMapping.mandatoryfields=["id","folder_id"];
OXMailMapping.idmapping = {
    "600": "id", "601": "folder_id", "602": "attachment", "603": "from", "604": "to", "605": "cc", 
    "606": "bcc", "607": "subject", "608": "size", "609": "sent_date", "610": "received_date", 
    "611": "flags", "612": "level", "614": "priority", "102": "color_label", "652": "account_name"  
}
OXMailMapping.alternative = {
	"user" : emptyFunction, "attachments": emptyFunction,
	"nested_msgs": emptyFunction, "attachments_html": function() {
        return configGetKey("mail.inlineattachments") &&
            configGetKey("modules.mail.allowhtmlimages");
    }, "attachments_html_noimage": function() {
        return configGetKey("mail.inlineattachments") &&
            !configGetKey("modules.mail.allowhtmlimages");
    }, "attachments_plain": function() {
        return !configGetKey("mail.inlineattachments");
    }, "blocked_images": emptyFunction, "mailtext": "-1",
    "disp_notification_to": "613", "flags_sort": "651", "headers": emptyFunction 
};
OXMailMapping.GETHTML = [
    "id","folder_id","attachment","attachments_html","from","to","cc","bcc","subject","size","sent_date","received_date","flags",
    "level","priority","color_label","account_name","user","nested_msgs","disp_notification_to",
    "flags_sort", "headers" 
];
OXMailMapping.GETPLAIN = [
    "id","folder_id","attachment","attachments_plain","from","to","cc","bcc","subject","size","sent_date","received_date","flags",
    "level","priority","color_label","account_name","user","nested_msgs","disp_notification_to",
    "flags_sort", "headers" 
];
OXMailMapping.GETHTMLNOIMAGE = [
    "id","folder_id","attachment","attachments_html_noimage","blocked_images","from","to","cc","bcc","subject","size","sent_date","received_date","flags",
    "level","priority","color_label","account_name","user","nested_msgs","disp_notification_to",
    "flags_sort", "headers"  
];

OXMailMapping.flagmapping = { "seen" : { flag : 32 , bool : true} , "unseen" : { flag : 32 , bool : false} ,
                            "answer" : { flag : 1 , bool : true} , "answered" : { flag : 1 , bool : false},
                            "delete" : { flag : 2 , bool : true} , "undelete" : { flag : 2 , bool : false},
                            "spam" : { flag : 128 , bool : true} , "ham" : { flag : 128 , bool : false} };

OXMailMapping.module="mail";
OXMailMapping.createKeyFromData = function(myArray,timestamp) {
     var tmp=new OXMailObjectCache();
     tmp.module = OXMailMapping.module;
     tmp.folder_id=myArray[1];
     tmp.id=myArray[0];
     return tmp;     
}
OXMailMapping.createKeyFromObject = function(myObject) {
     var tmp=new OXMailObjectCache();
     tmp.module = OXMailMapping.module;
     tmp.folder_id=myObject["folder_id"];
     tmp.id=myObject["id"];
     return tmp;     
}


OXMailMapping.stringmapping=switchStringObject(OXMailMapping.idmapping);
OXMailMapping.objectConstructor=OXMailObjectCache;
OXMailMapping.stringmapping=switchStringObject(OXMailMapping.idmapping);
OXMailMapping.get = OXAbstractMapping.get(OXMailMapping);
OXMailMapping.execute = OXAbstractMapping.execute(OXMailMapping);
OXMailMapping.buildResponse = OXAbstractMapping.buildResponse(OXMailMapping);
OXMailMapping.criteriaRequest= OXAbstractMapping.criteriaRequest(OXMailMapping);
OXMailMapping.listRequest= OXAbstractMapping.listRequest(OXMailMapping);
OXMailMapping.addCriteriaNotInCache = OXAbstractMapping.addCriteriaNotInCache(OXMailMapping);
OXMailMapping.addCriteriaExpired = OXAbstractMapping.addCriteriaExpired(OXMailMapping);
OXMailMapping.addCriteriaMissingColumn = OXAbstractMapping.addCriteriaMissingColumn(OXMailMapping);
OXMailMapping.addListNotInCache = OXAbstractMapping.addListNotInCache(OXMailMapping);
OXMailMapping.addListExpired = OXAbstractMapping.addListExpired(OXMailMapping);
OXMailMapping.addListMissingColumn = OXAbstractMapping.addListMissingColumn(OXMailMapping);
OXMailMapping.request = OXAbstractMapping.request(OXMailMapping);
OXMailMapping.handleResponse = OXAbstractMapping.handleResponse(OXMailMapping);
OXMailMapping.handleCriteriaNotInCache = OXAbstractMapping.handleCriteriaNotInCache(OXMailMapping);
OXMailMapping.handleCriteriaExpired = OXAbstractMapping.handleCriteriaExpired(OXMailMapping);
OXMailMapping.handleCriteriaMissingColumn = OXAbstractMapping.handleCriteriaMissingColumn(OXMailMapping);
OXMailMapping.handleCriteria=OXAbstractMapping.handleCriteria(OXMailMapping);
OXMailMapping.handleListNotInCache = OXAbstractMapping.handleListNotInCache(OXMailMapping);
OXMailMapping.handleListExpired = OXAbstractMapping.handleListExpired(OXMailMapping);
OXMailMapping.handleListMissingColumn = OXAbstractMapping.handleListMissingColumn(OXMailMapping);
OXMailMapping.handleList = OXAbstractMapping.handleList(OXMailMapping);
OXMailMapping.deleteObject = OXAbstractMapping.deleteObject(OXMailMapping);
OXMailMapping.getCollection= OXAbstractMapping.getCollection(OXMailMapping);
OXMailMapping.convertColumns = OXAbstractMapping.convertColumns(OXMailMapping);
OXMailMapping.createCacheObject = OXAbstractMapping.createCacheObject(OXMailMapping);
OXMailMapping.checkAddCriteria = OXAbstractMapping.checkAddCriteria(OXMailMapping);
OXMailMapping.checkAddEditDeleteObject= OXAbstractMapping.checkAddEditDeleteObject(OXMailMapping);
OXMailMapping.getSubCollectionsfromObject = OXAbstractMapping.getSubCollectionsfromObject(OXMailMapping);
OXMailMapping.getSubCollections = OXAbstractMapping.getSubCollections(OXMailMapping);
OXMailMapping.equalsCollection = OXAbstractMapping.equalsCollection(OXMailMapping);
OXMailMapping.errorHandler = function(uniqueName,request,response,response2) {
    if(debug) { 
    	console.log("Error",uniqueName,request,response,response2); 
    }
    if (response.error) {
    	// overwrite the default error handler at some special cases
        switch (response.code) {
        	 case "MSG-0032":
        	   // message has been already deleted so we remove it from the cache
        	   // and ignore the error  
    	       OXMailMapping.deleteObjectsInternal([ request.object ]);
        	   break;
        	 default:
        	   // calling the default error handler
        	   OXCache.errorHandler(uniqueName,request,response,response2);
        	   break;        	
        }
    }
    return;
};

OXMailMapping.deleteObjectsInternal = function(objects) {
	var objects_tmp = new Array(objects.length);
	for (var i=0; i<objects.length; i++) {
		objects_tmp[i] = objects[i];
	}
	
    var tmpFn=OXCache.join.add();
    var oxMapping=OXMailMapping;

    // Calling all "before" callbacks.
    for(var i in OXCache.modifiedCallbacks) {
        var mc = OXCache.modifiedCallbacks[i];
        if (mc.module == oxMapping.module && mc.before) mc.before();
    }
    
    var tmpCollect=[];
    for(var mail=0;mail<objects_tmp.length;mail++) {
        var collections=oxMapping.getSubCollectionsfromObject(objects_tmp[mail],true);
        for(var count=0;count<collections.length;count++) {
            if(collections[count].check) { continue; } 
            var index=collections[count].map_objects.get(objects_tmp[mail]);
            if(index) {
                 collections[count].modified=true;
                 if(!collections[count].tmpIndexes) { collections[count].tmpIndexes=[]; }
                 collections[count].tmpIndexes.push(index);
                 tmpCollect.push(collections[count]);
                 collections[count].modifiedtmp=true;
             }               
        }
    }
    
    for(var count=0;count<tmpCollect.length;count++) {
        if(tmpCollect[count].modifiedtmp) {
                var indexes=tmpCollect[count].tmpIndexes;
                indexes.sort(function(a,b) { return a-b; });
                for(var i=indexes.length-1;i>=0;i--) {
                    tmpCollect[count].objects.splice(indexes[i]-1,1);
                }
                delete tmpCollect[count].tmpIndexes;
            tmpCollect[count].map_objects=new LRUKeyList();
            for(var index=0;index<tmpCollect[count].objects.length;index++) { 
                tmpCollect[count].map_objects.set(tmpCollect[count].objects[index],index+1) 
            }
            tmpCollect[count].modifiedtmp=false;
        }
    }

    for(var i2 in OXCache.modifiedCallbacks) {
        var mc = OXCache.modifiedCallbacks[i2];
        if (mc.module == oxMapping.module && !(mc.collection.criteria)) {
            for (var count = 0; count < mc.collection.objects.length; count++) {
                for (var i=0;i<objects_tmp.length;i++) {
                    if (mc.collection.objects[count].equals(objects_tmp[i])) {
                        mc.collection.objects.splice(count,1);
                        count--;
                        mc.modified=true;
                        break;
                   }
                }
            }
        }
    }
    tmpFn();
}
OXMailMapping.deleteObjects = function(mailObjects,harddelete,optCb) {
    var oxMapping=OXMailMapping;
    var request=new Array();
    var idArray=new Array();
    for(var i=0;i<mailObjects.length;i++) {
        idArray.push({id : mailObjects[i].id , folder : mailObjects[i].folder_id});
    }
    if(idArray.length) {
        var req = {
            module : "mail",
            action : "delete",
            folder : idArray[0].folder,
            data : idArray
        };
        if (harddelete) req.harddelete = 1;
        request.push(req);
    }
    var tmpFn=OXCache.join.add();
    var callit=false;
	json.put(AjaxRoot + "/multiple?session=" + session + "&continue=true",
        request, null, function(reply) {
            var tmpCollect=new Array();
            for(var i=0;i<reply.length;i++) {
                if(reply[i].error) {
                    if (reply[i].code == "MSG-0039") {
                        function cbyes() {
                            oxMapping.deleteObjects(mailObjects, true, optCb);
                        }
                        newConfirm(_("Quota Exceed"),_("Permanently remove your deleted E-Mails?"),AlertPopup.YESNO,null,null,cbyes,null);          
                    } else {
                       newServerError(reply[i]);
                    }
                } else {
                    oxMapping.deleteObjectsInternal(mailObjects);
                    oxMapping.createObjectsInternal([{ 
                        folder_id: configGetKey("mail.folder.trash"), 
                        module: "mail" 
                    }]);
					callit=true;		
                }
            }
            tmpFn();            
			if(optCb && callit) optCb();
        }
    );
}
OXMailMapping.setMailFlags = function(flag,objects) {
    var oxMapping=OXMailMapping;
    var mailFlagsParam = OXMailMapping.flagmapping[flag];
    var Self = this;
    var tmpObject = {};
    if(objects.length != 0) {
        var tmpFn=OXCache.join.add();
        var multipleObject = [];
        for (var i = 0; i < objects.length; i++) {
            multipleObject.push({ 
                action : "update", module : "mail", timestamp : 0, id : objects[i].id, 
                folder : (objects[i].folder || objects[i].folder_id), data : { flags : mailFlagsParam.flag , value :  mailFlagsParam.bool }
            }); 
         }
         (new JSON).put(AjaxRoot + "/multiple?session=" + session + "&continue=true",multipleObject,null,
         function(daten){
            if (flag == "spam" || flag == "ham") {
                oxMapping.deleteObjectsInternal(objects);
            } else {
                oxMapping.editObjectsInternal(objects,null,true,[mailFlagsParam]);
            }
            tmpFn();

         });
     }
}
OXMailMapping.editObjectsInternal = function(oldObjects,changes,checkExist,optionalFlags) {
    var oxMapping=OXMailMapping;
    var tmpFn=OXCache.join.add();
    for(var myObject in oldObjects) {
        var tmpKeyOld=OXMailMapping.createKeyFromObject(oldObjects[myObject]);
        var collections=oxMapping.getSubCollectionsfromObject(oldObjects[myObject],true);
        //TODO Check Changes in Collection
        for(var i=0;i<collections.length;i++) {
            if(!collections[i].check) {
                //TODO SET CHECK IF CHANGES IN ORDER COLUMN
                //collections[i].check=true;
				collections[i].modified = true;
            } 
        }
        for(var i in OXCache.modifiedCallbacks) {
            if(OXCache.modifiedCallbacks[i].module == oxMapping.module && !(OXCache.modifiedCallbacks[i].collection.criteria)) {
                for(var count=0;count<OXCache.modifiedCallbacks[i].collection.objects.length;count++) {
                    if(tmpKeyOld.equals(OXCache.modifiedCallbacks[i].collection.objects[count])) {
                        var mc = OXCache.modifiedCallbacks[i];
                        if (!mc.modified && mc.before) mc.before();
                        mc.modified=true;    
                    }
                }
            }
        }
        var tmpObjectOld=OXCache.cachedObjects.get(tmpKeyOld);
        if(tmpObjectOld) {
           if(changes) {
              for(var i in changes) {
                  if(!checkExist || tmpObjectOld[i]!== undefined) {
                      tmpObjectOld[i]=changes[i];
                  }
              }
           }
           if(optionalFlags) {
              if(!checkExist || tmpObjectOld["flags"]!== undefined) {
                 for(var i in optionalFlags) {
                      if (optionalFlags[i].flag & 32) {
                          var oldcount = oUnreadCounterStorage.count_hashmap[tmpObjectOld.folder_id];
                          if (oldcount >= 0) {
                              oUnreadCounterStorage.set(tmpObjectOld.folder_id,
                                  oldcount + ((tmpObjectOld.flags & 32) >> 5)
                                           - optionalFlags[i].bool);
                          }
                      }
                      if((tmpObjectOld["flags"] & optionalFlags[i].flag) && !optionalFlags[i].bool) {
                        tmpObjectOld["flags"]=tmpObjectOld["flags"]-optionalFlags[i].flag;
                      } else if(!(tmpObjectOld["flags"] & optionalFlags[i].flag) && optionalFlags[i].bool) {
                        tmpObjectOld["flags"]=tmpObjectOld["flags"]+optionalFlags[i].flag;
                      }                        
                 }
              }
           }
       }
    }  
    OXCache.cachedObjects.set(tmpKeyOld,tmpObjectOld);
    //TODO CHECK CHANGE FOLDER
    tmpFn();
}
OXMailMapping.createObjectsInternal = function(newObjects) {
    setTimeout(function() {
        var oxMapping=OXMailMapping;

        // Calling all "before" callbacks.
        for(var i in OXCache.modifiedCallbacks) {
            var mc = OXCache.modifiedCallbacks[i];
            if (mc.module == oxMapping.module && mc.before) mc.before();
        }
        
        var tmpFn=OXCache.join.add();
        for(var object in newObjects) {
            var collections=oxMapping.getSubCollectionsfromObject(newObjects[object],true);
            for(var i=0;i<collections.length;i++) {
                if(!collections[i].check) {
                    collections[i].check=true;
                }
            }
        }
        tmpFn();
    },0);
}
OXMailMapping.editObject = function(oldObject,newObject) {
    setTimeout(function() {
        var oxMapping=OXMailMapping;
        var tmpFn=OXCache.join.add();
        var tmpKeyOld=OXMailMapping.createKeyFromObject(oldObject);
        var collections=oxMapping.getSubCollectionsfromObject(oldObject,true);
        for(var i=0;i<collections.length;i++) {
            if(!collections[i].check) {
                collections[i].check=true;
            }
        }
        for(var i in OXCache.modifiedCallbacks) {
            if(OXCache.modifiedCallbacks[i].module == oxMapping.module && !(OXCache.modifiedCallbacks[i].collection.criteria)) {
                for(var count=0;count<OXCache.modifiedCallbacks[i].collection.objects.length;count++) {
                    if(tmpKeyOld.equals(OXCache.modifiedCallbacks[i].collection.objects[count])) {
                        var mc = OXCache.modifiedCallbacks[i];
                        if (!mc.modified && mc.before) mc.before();
                        mc.modified=true;  
                    }
                }
            }
        }
        var tmpObjectOld=OXCache.cachedObjects.get(tmpKeyOld);
        OXCache.cachedObjects.set(tmpKeyOld,tmpObjectOld);
        //TODO CHECK CHANGE FOLDER
        tmpFn();
    },0);
}
OXMailMapping.createObject = function(newObject) {
    setTimeout(function() {
        var oxMapping=OXTaskMapping;

        // Calling all "before" callbacks.
        for(var i in OXCache.modifiedCallbacks) {
            var mc = OXCache.modifiedCallbacks[i];
            if (mc.module == oxMapping.module && mc.before) mc.before();
        }
        
        var tmpFn=OXCache.join.add();
        var collections=oxMapping.getSubCollectionsfromObject(newObject,true);
        for(var i=0;i<collections.length;i++) {
            if(!collections[i].check) {
                collections[i].check=true;
            }
        }
        tmpFn();
    },0);
}
OXMailMapping.setTag = OXAbstractMapping.setTag(OXMailMapping);

OXCache.setMapping("mail",OXMailMapping);




function OXInfoStoreMapping() {}
OXInfoStoreMapping.mandatoryfields=["folder_id","id","last_modified","created_by", "creation_date"];
OXInfoStoreMapping.idmapping= {
    "1": "id", "2": "created_by", "3": "modified_by", "4": "creation_date", "5": "last_modified", 
    "20": "folder_id", "100": "categories", "700": "title", "701": "url", 
    "702": "filename", "703": "file_mimetype", "704": "file_size", "705": "version", "706": "description", 
    "707": "locked_until", "708": "file_md5sum", "709": "version_comment", "710": "current_version", 
    "711": "number_of_versions", "102": "color_label"
}
// "101": "private_flag",
OXInfoStoreMapping.stringmapping=switchStringObject(OXInfoStoreMapping.idmapping);
OXInfoStoreMapping.module="infostore";
OXInfoStoreMapping.createKeyFromData = function(myArray,timestamp) {
     var tmp=new OXInfoStoreObjectCache();
     tmp.module = OXInfoStoreMapping.module;
     tmp.folder_id=myArray[0];
     tmp.id=myArray[1];
     tmp.created_by = myArray[3];
     if(timestamp) { tmp.timestamp = timestamp }
     return tmp;     
}
OXInfoStoreMapping.createKeyFromObject = function(myObject) {
     var tmp=new OXInfoStoreObjectCache();
     tmp.module = OXInfoStoreMapping.module;
     tmp.folder_id=myObject["folder_id"];
     tmp.id=myObject["id"];
     tmp.created_by = myObject.created_by;
     return tmp;     
}

OXInfoStoreMapping.objectConstructor=OXInfoStoreObjectCache;
OXInfoStoreMapping.stringmapping=switchStringObject(OXInfoStoreMapping.idmapping);
OXInfoStoreMapping.get = OXAbstractMapping.get(OXInfoStoreMapping);
OXInfoStoreMapping.execute = OXAbstractMapping.execute(OXInfoStoreMapping);
OXInfoStoreMapping.buildResponse = OXAbstractMapping.buildResponse(OXInfoStoreMapping);
OXInfoStoreMapping.criteriaRequest= OXAbstractMapping.criteriaRequest(OXInfoStoreMapping);
OXInfoStoreMapping.listRequest= OXAbstractMapping.listRequest(OXInfoStoreMapping);
OXInfoStoreMapping.addCriteriaNotInCache = OXAbstractMapping.addCriteriaNotInCache(OXInfoStoreMapping);
OXInfoStoreMapping.addCriteriaExpired = OXAbstractMapping.addCriteriaExpired(OXInfoStoreMapping);
OXInfoStoreMapping.addCriteriaMissingColumn = OXAbstractMapping.addCriteriaMissingColumn(OXInfoStoreMapping);
OXInfoStoreMapping.addListNotInCache = OXAbstractMapping.addListNotInCache(OXInfoStoreMapping);
OXInfoStoreMapping.addListExpired = OXAbstractMapping.addListExpired(OXInfoStoreMapping);
OXInfoStoreMapping.addListMissingColumn = OXAbstractMapping.addListMissingColumn(OXInfoStoreMapping);
OXInfoStoreMapping.request = OXAbstractMapping.request(OXInfoStoreMapping);
OXInfoStoreMapping.handleResponse = OXAbstractMapping.handleResponse(OXInfoStoreMapping);
OXInfoStoreMapping.handleCriteriaNotInCache = OXAbstractMapping.handleCriteriaNotInCache(OXInfoStoreMapping);
OXInfoStoreMapping.handleCriteriaExpired = OXAbstractMapping.handleCriteriaExpired(OXInfoStoreMapping);
OXInfoStoreMapping.handleCriteriaMissingColumn = OXAbstractMapping.handleCriteriaMissingColumn(OXInfoStoreMapping);
OXInfoStoreMapping.handleCriteria=OXAbstractMapping.handleCriteria(OXInfoStoreMapping);
OXInfoStoreMapping.handleListNotInCache = OXAbstractMapping.handleListNotInCache(OXInfoStoreMapping);
OXInfoStoreMapping.handleListExpired = OXAbstractMapping.handleListExpired(OXInfoStoreMapping);
OXInfoStoreMapping.handleListMissingColumn = OXAbstractMapping.handleListMissingColumn(OXInfoStoreMapping);
OXInfoStoreMapping.handleList = OXAbstractMapping.handleList(OXInfoStoreMapping);
OXInfoStoreMapping.deleteObject = OXAbstractMapping.deleteObject(OXInfoStoreMapping);
OXInfoStoreMapping.getCollection= OXAbstractMapping.getCollection(OXInfoStoreMapping);
OXInfoStoreMapping.convertColumns = OXAbstractMapping.convertColumns(OXInfoStoreMapping);
OXInfoStoreMapping.createCacheObject = OXAbstractMapping.createCacheObject(OXInfoStoreMapping);
OXInfoStoreMapping.checkAddCriteria = OXAbstractMapping.checkAddCriteria(OXInfoStoreMapping);
OXInfoStoreMapping.checkAddEditDeleteObject= OXAbstractMapping.checkAddEditDeleteObject(OXInfoStoreMapping);
OXInfoStoreMapping.getSubCollectionsfromObject = OXAbstractMapping.getSubCollectionsfromObject(OXInfoStoreMapping);
OXInfoStoreMapping.getSubCollections = OXAbstractMapping.getSubCollections(OXInfoStoreMapping);
OXInfoStoreMapping.equalsCollection = OXAbstractMapping.equalsCollection(OXInfoStoreMapping);
OXInfoStoreMapping.errorHandler = OXCache.errorHandler;
OXInfoStoreMapping.editObjects = function(oldObjects,changes) {
    setTimeout(function() {
       var tmpFn=OXCache.join.add();
       OXInfoStoreMapping.editObjectsInternal(oldObjects,changes);
       tmpFn(); 
    },0);
}
OXInfoStoreMapping.editObjectsInternal = function(oldObjects,changes,checkExist) {
    var oxMapping=OXInfoStoreMapping;
    var tmpFn=OXCache.join.add();
    for(var myObject in oldObjects) {
        var tmpKeyOld=oxMapping.createKeyFromObject(oldObjects[myObject]);
        var collections=oxMapping.getSubCollectionsfromObject(oldObjects[myObject],true);
        //TODO Check Changes in Collection
        for(var i=0;i<collections.length;i++) {
            if(!collections[i].search && !collections[i].check) {
                collections[i].check=true;
            } 
        }
        for(var i in OXCache.modifiedCallbacks) {
            if(OXCache.modifiedCallbacks[i].module == oxMapping.module && !(OXCache.modifiedCallbacks[i].collection.criteria)) {
                for(var count=0;count<OXCache.modifiedCallbacks[i].collection.objects.length;count++) {
                    if(tmpKeyOld.equals(OXCache.modifiedCallbacks[i].collection.objects[count])) {
                        var mc = OXCache.modifiedCallbacks[i];
                        if (!mc.modified && mc.before) mc.before();
                        mc.modified=true; 
                    }
                }
            }
        }
        var tmpObjectOld=OXCache.cachedObjects.get(tmpKeyOld);
        if(tmpObjectOld) {
           if(changes) {
              for(var i in changes) {
                  if(!checkExist || tmpObjectOld[i]!== undefined) {
                      if(i != "notification") {
                          tmpObjectOld[i]=changes[i];
                      }
                  }
              }
           }
       }
       OXCache.cachedObjects.set(tmpKeyOld,tmpObjectOld);    
    }  
    //TODO CHECK CHANGE FOLDER
    tmpFn();
}
OXInfoStoreMapping.deleteObjects = function(objects, callback) {
    var oxMapping=OXInfoStoreMapping;
    var request = new Array();
    for (var i in objects) {
        var tmpTime=objects[i]["timestamp"]; 
        if (!tmpTime) {
            var tmpObject = OXCache.cachedObjects.get(objects[i]);
            if(tmpObject) {
                tmpTime=tmpObject.timestamp;
            }
        }
		request.push({
            module: oxMapping.module, 
            action: "delete",
            timestamp: tmpTime,
            data: [{ folder : objects[i]["folder_id"] , id : objects[i]["id"] }]
        });
    }
    json.put(AjaxRoot + "/multiple?session=" + session + "&continue=true",
        request, null, function(reply) {
            var tmpFn=OXCache.join.add();
            var error=new Array();
            for(var i=0;i<reply.length;i++) {
                if(reply[i].error) {
                    newServerError(reply[i]);
                    error.push(i);   
                }  
            }
            if(!error.length) {
                oxMapping.deleteObjectsInternal(objects);
            }
            tmpFn();
            if (callback) callback();
        }
    );
}
OXInfoStoreMapping.deleteObjectsInternal = function(objects) {
    var tmpFn=OXCache.join.add();
    var oxMapping=OXInfoStoreMapping;

    // Calling all "before" callbacks.
    for(var i in OXCache.modifiedCallbacks) {
        var mc = OXCache.modifiedCallbacks[i];
        if (mc.module == oxMapping.module && mc.before) mc.before();
    }
    
    var tmpCollect=[];
    for(var mail=0;mail<objects.length;mail++) {
        var collections=oxMapping.getSubCollectionsfromObject(objects[mail],true);
        for(var count=0;count<collections.length;count++) {
            if(collections[count].check) { continue; } 
            var index=collections[count].map_objects.get(objects[mail]);
            if(index) {
                 collections[count].modified=true;
                 if(!collections[count].tmpIndexes) { collections[count].tmpIndexes=[]; }
                 collections[count].tmpIndexes.push(index);
                 tmpCollect.push(collections[count]);
                 collections[count].modifiedtmp=true;
             }               
        }
    }
    for(var count=0;count<tmpCollect.length;count++) {
        if(tmpCollect[count].modifiedtmp) {
                var indexes=tmpCollect[count].tmpIndexes;
                indexes.sort(function(a,b) { return a-b; });
                for(var i=indexes.length-1;i>=0;i--) {
                    tmpCollect[count].objects.splice(indexes[i]-1,1);
                }
                delete tmpCollect[count].tmpIndexes;
            tmpCollect[count].map_objects=new LRUKeyList();
            for(var index=0;index<tmpCollect[count].objects.length;index++) { 
                tmpCollect[count].map_objects.set(tmpCollect[count].objects[index],index+1) 
            }
            tmpCollect[count].modifiedtmp=false;
        }
    }
    tmpFn();
}
OXInfoStoreMapping.createObject = function(newObject) {
    setTimeout(function() {
        var tmpFn=OXCache.join.add();
        OXInfoStoreMapping.createObjectsInternal([newObject]);
        tmpFn();
    },0);
}
OXInfoStoreMapping.createObjectsInternal = function(newObjects) {
    setTimeout(function() {
        var oxMapping=OXInfoStoreMapping;

        // Calling all "before" callbacks.
        for(var i in OXCache.modifiedCallbacks) {
            var mc = OXCache.modifiedCallbacks[i];
            if (mc.module == oxMapping.module && mc.before) mc.before();
        }
        
        var tmpFn=OXCache.join.add();
        for(var object in newObjects) {
            var collections=oxMapping.getSubCollectionsfromObject(newObjects[object],true);
            for(var i=0;i<collections.length;i++) {
                if(!collections[i].search && !collections[i].check) {
                    collections[i].check=true;
                }
            }
        }
        tmpFn();
    },0);
}
OXInfoStoreMapping.setTag = OXAbstractMapping.setTag(OXInfoStoreMapping);
OXCache.setMapping("infostore",OXInfoStoreMapping);
function OXAbstractObject() {   
    var Self=this;
    this.type="OXObject";
}
OXAbstractObject.prototype.hasColumns = function(stringobjects) {
	for(var i in stringobjects) {
	    var path = stringobjects[i].split("/");
	    if (path.length == 1) {
	        if (this[stringobjects[i]] === undefined) return false;
	    } else {
	        if (!this[path[0]] || this[path[0]][path[1]] === undefined) {
	            return false;
	        }
	    }
    }
	return true;
}
OXAbstractObject.prototype.getColumns = function() {
    var fields=new Array();
    if(this.mapping) {
	   for(var i in this.mapping.stringmapping) {
	       if(this.i) {
                filledfields.push(i);
            } 
        }
    }
	return filledfields;
}
function OXTaskObjectCache() {
    this.module="tasks";
	this.mapping=OXTaskMapping;
}
OXTaskObjectCache.createfromObject = Key.createfromObject;
OXTaskObjectCache.prototype.equals=Key.prototype.equals;
OXTaskObjectCache.prototype.hashCode=Key.prototype.hashCode;
OXTaskObjectCache.prototype.hasColumns=OXAbstractObject.prototype.hasColumns;
OXTaskObjectCache.prototype.getColumns=OXAbstractObject.prototype.getColumns;
function OXCalendarObjectCache() {
    this.module="calendar";
	this.mapping=OXCalendarMapping;
}
OXCalendarObjectCache.createfromObject = Key.createfromObject;
OXCalendarObjectCache.prototype.equals=Key.prototype.equals;
OXCalendarObjectCache.prototype.hashCode=Key.prototype.hashCode;
OXCalendarObjectCache.prototype.hasColumns=OXAbstractObject.prototype.hasColumns;
OXCalendarObjectCache.prototype.getColumns=OXAbstractObject.prototype.getColumns;
function OXContactObjectCache() {
    this.module="contacts";
	this.mapping=OXContactMapping;
}
OXContactObjectCache.createfromObject = Key.createfromObject;
OXContactObjectCache.prototype.equals=Key.prototype.equals;
OXContactObjectCache.prototype.hashCode=Key.prototype.hashCode;
OXContactObjectCache.prototype.hasColumns=OXAbstractObject.prototype.hasColumns;
OXContactObjectCache.prototype.getColumns=OXAbstractObject.prototype.getColumns;
function OXMailObjectCache() {
    this.module="mail";
	this.mapping=OXMailMapping;
}
OXMailObjectCache.createfromObject = Key.createfromObject;
OXMailObjectCache.prototype.equals=Key.prototype.equals;
OXMailObjectCache.prototype.hashCode=Key.prototype.hashCode;

OXMailObjectCache.prototype.hasColumns=OXAbstractObject.prototype.hasColumns;
OXMailObjectCache.prototype.getColumns=OXAbstractObject.prototype.getColumns;
function OXInfoStoreObjectCache() {
    this.module="infostore";
	this.mapping=OXInfoStoreMapping;
}
OXInfoStoreObjectCache.createfromObject = Key.createfromObject;
OXInfoStoreObjectCache.prototype.equals=Key.prototype.equals;
OXInfoStoreObjectCache.prototype.hashCode=Key.prototype.hashCode;
OXInfoStoreObjectCache.prototype.hasColumns=OXAbstractObject.prototype.hasColumns;
OXInfoStoreObjectCache.prototype.getColumns=OXAbstractObject.prototype.getColumns;


  /////////////////////////////////////
 //   Tests and others              //
/////////////////////////////////////
var first=false;
function etest1(arg) {
    for(var i=0;i<arg;i++) {
    (new JSON()).put(AjaxRoot + "/tasks?action=new&session=" + session, {
        "title" : "Aufgabe Nr:"+i,
        "folder_id" : 3009
    }, null, function(cb){
    });
    }
}
var tmptime=new Date().getTime();
var times=new Array();   
var times2=new Array();  

register("OX_Refresh", function() {
	OXCache.update();
}) 
register("OX_Folder_Cleared",function(folder_id) {
	var tmpFn=OXCache.join.add();
	var allCollections=OXMailMapping.getSubCollectionsfromObject({ folder_id : folder_id });
	for (var i = 0; i < allCollections.length; i++) {
		OXMailMapping.deleteObjectsInternal(allCollections[i].objects);
	}	
	tmpFn();
});     
/*
 * All content on this website (including text, images, source
 * code and any other original works), unless otherwise noted,
 * is licensed under a Creative Commons License.
 * 
 * http://creativecommons.org/licenses/by-nc-sa/2.5/
 * 
 * Copyright (C) 2004-2010 Open-Xchange, Inc.
 * Mail: info@open-xchange.com 
 * 
 * @author Viktor Pracht <Viktor.Pracht@open-xchange.org>
 */

/**
 * Creates a finite state machine (FSM).
 * The FSM is defined by an array of transition objects. A transition object can
 * be created and modified by the static methods of the class FSM. The set of
 * states is implicitly defined as all start and end states of all transitions.
 * @param {String} startState The initial state of the FSM.
 * @param {Array} transitions An array with all transitions of the FSM.
 * Each element of the array is a transition object, which can be created and
 * modified by the static methods of the class FSM.
 * @type Function
 * @return An object which has the following methods:
 * <dl><dt>enable</dt><dd>takes no parameters and sets up all event handlers
 * for the current state.</dd>
 * <dt>disable</dt><dd>takes no parameters and removes all event handlers for
 * the current state.</dd>
 * <dt>enter</dt><dd>takes a state name as parameter and enters this state. This
 * method is less efficient than FSM transactions.</dd>
 * <dt>reset</dt><dd>takes no parameters and enters the start state.</dd>
 * Methods of this object should not be called from a transition callback.
 */
function FSM(startState, transitions) {
	var states = {};
	for (var i in transitions) {
		var t = transitions[i];
		if (!states[t.from]) states[t.from] = {};
		t.from = states[t.from];
		for (var j in t.to) {
			var s = t.to[j];
			if (!states[s]) states[s] = {};
			t.to[j] = states[s];
		}
		t.from[i] = t;
		t.fire = (function(t) {
			return function(e) {
				if (t.callback(e) === false) return;
				var ix = t.condition();
				var actions = t.actions[ix];
				for (var i = 0; i < actions.length; i++) actions[i]();
				current = t.to[ix];
			};
		})(t);
	}
	for (var i in transitions) {
		var t = transitions[i];
		t.actions = new Array(t.to.length);
		for (var i = 0; i < t.to.length; i++) {
			var actions = t.actions[i] = [];
			var to = t.to[i];
			if (t.from == to) {
				for (var j in t.from)
					if (t.from[j].reset) actions.push(t.from[j].reset);
			} else {
				for (var j in t.from) actions.push(t.from[j].disable);
				for (var j in to) actions.push(to[j].enable);
			}
		}
	}
	var current = states[startState];
	var enabled = false;
	var fsm = {
		enable: function() {
			if (!enabled) for (var i in current) current[i].enable();
			enabled = true;
		},
		disable: function() {
			if (enabled) for (var i in current) current[i].disable();
			enabled = false;
		},
		enter: function(state) {
			this.disable();
			current = states[state];
			this.enable();
		},
		reset : function() { this.enter(startState); }
	};
	return fsm;
}

/**
 * Creates a transition which is triggered by calls from JavaScript. After being
 * passed to FSM, the field <code>fire</code> of the transaction object contains
 * the callback function which can be called to trigger the transition. It only
 * has an effect when the FSM is in the start state of the transition.
 * @param {Function} callback The event handler which is called when
 * the transition fires. If the callback returns false, the transition does not
 * fire. Type conversion is suppressed, so only a real Boolean value false
 * will cancel the transaction. Values of any other type (in particular,
 * the default of undefined when there is no or an empty return statement) are
 * interpreted as true.
 * @type Object
 * @return The newly created transition object.
 */
FSM.Callback = function(callback) {
    var enabled = false;
    return {
        callback: function(e) { return enabled && callback(e); },
        enable: function() { enabled = true; },
        disable: function() { enabled = false; }
    };
};

/**
 * Creates a transition which is triggered by a DOM event.
 * When the FSM enters the source state of the transition, an event handler is
 * attached to the specified node with addDOMEvent, and is removed with
 * removeDOMEvent when FSM leaves the source state.
 * @param {Object} node The DOM node to which the event handler is attached.
 * @param {String} name The name of the DOM event, without the leading "on".
 * @param {Function} callback The event handler which is called when
 * the transition fires. If the callback returns false, the transition does not
 * fire. Type conversion is suppressed, so only a real Boolean value false
 * will cancel the transaction. Values of any other type (in particular,
 * the default of undefined when there is no or an empty return statement) are
 * interpreted as true.
 * @type Object
 * @return The newly created transition object.
 */
FSM.Event = function(node, name, callback) {
	var Self = {
		callback: callback,
		enable: function() {
			addDOMEvent(node, name, Self.fire);
		},
		disable: function() {
			removeDOMEvent(node, name, Self.fire);
		}
	};
	return Self;
};

/**
 * Creates a transition which fires after a timeout.
 * When the FSM enters the source state of the transition, a timeout is started
 * with setTimeout and is cancelled with clearTimeout when the FSM leaves the
 * source state. Transitions which do not leave the source state of this
 * transition do not reset the timer by default (see FSM.Reset for that).
 * @param {Function} delay A function which returns the timeout in milliseconds. 
 * @param {Function} callback A callback function which is called after
 * the timeout. If the callback returns false, the transition does not
 * fire. No type conversion is performed, so only a real Boolean value false
 * will cancel the transaction. Values of any other type (in particular,
 * the default of undefined when there is no or an empty return statement) are
 * interpreted as true.
 * @type Object
 * @return The newly created transition object.
 */
FSM.Timeout = function(delay, callback) {
	var timer = null;
	var Self = {
		callback: callback,
		enable: function() {
			if (timer !== null) clearTimeout(timer);
			timer = setTimeout(Self.fire, delay());
		},
		disable: function() {
			if (timer !== null) clearTimeout(timer);
			timer = null;
		}
	};
	return Self;
};

/**
 * Configures a transition object with a source and a destination state.
 * Every transition object must be modified by this function to specify
 * the source and destination states, before being passed to FSM().
 * The modification occurs in-place, so that a transition object can not be
 * reused for multiple transitions.
 * @param {String} from The source state of the transition. The transition can
 * only fire when the FSM is in this state.
 * @param {String} to The destination state of the transition. The FSM enters
 * this state when the transition fires.
 * @param {Object} transition An unmodified transition object which does not
 * have any states associated with it yet.
 * @type Object
 * @return The same transition object as passed in the last parameter, but now
 * it is associated with a source and a destination state and can be passed to
 * FSM().
 */
FSM.Trans = function(from, to, transition) {
	transition.from = from;
	transition.condition = FSM.zero;
	transition.to = [to];
	return transition;
};

/**
 * @private
 */
FSM.zero = function() { return 0; };

/**
 * Configures a transition object with a source state and a list of possible
 * destination states. The actual destination state is selected by the specified
 * callback when the transition fires.
 * @param {String} from The source state of the transition. The transition can
 * only fire when the FSM is in this state.
 * @param {Function} callback A callback function which is called when
 * the transaction fires and returns an index into the array of possible
 * destination states to select the next state of the FSM.
 * @param {Array} to An array with possible destination states as strings.
 * The FSM enters one of these states when the transaction fires. The actual
 * state is selected by the specified callback function.
 * @param {Object} transition An unmodified transition object which does not
 * have any states associated with it yet.
 * @type Object
 * @return The same transition object as passed in the last parameter, but now
 * it is associate with a source state and a list of destination states, and can
 * be passed to FSM().
 */
FSM.Condition = function(from, condition, to, transition) {
	transition.from = from;
	transition.condition = condition;
	transition.to = to;
	return transition;
}

/**
 * Modifies a transition to be reset when another transition fires but does not
 * change the FSM state.
 * By default, all transition objects do nothing when a transition fires and
 * the new FSM state is the same as the old state. In the case that a transition
 * must be reset (e. g. restarting a timeout transition), the transition which
 * needs restarting should be modified by passing it to this function.
 * The relative order of modifications of the same transition object is not
 * relevant.
 * @param {Object} transition A transition object which should be modified.
 * @type Object
 * @return The same object as passed in the first parameter, but now it will be
 * reset whenever other transitions fire without changing the FSM state.
 */
FSM.Reset = function(transition) {
	transition.reset = function() {
		transition.disable();
		transition.enable();
	};
	return transition;
};

/**
 * Modifies a callback function for mouseout events.
 * The result of this function is intended for use as the third parameter to
 * FSM.Event when the second parameter is "mouseout". It ensures that the event
 * fires only when the mouse leaves the target node completely, and not merely
 * switches from one child to another.
 * @param {Function} callback
 * @type Function
 */
FSM.MouseOut = function(callback) {
    return function(e) {
        for (var n = e.relatedTarget || e.toElement; n; n = n.parentNode)
            if (n == parent) return false;
        return callback();
    };
}
var NV_HOST = 'www.netvibes.com';
var NV_PATH = '/';
var NV_STATIC = '';
var NV_MODULES = 'nvmodules.netvibes.com';


function UWAWidget(parameters){
	
	this.url_params = {};
	this.configuration = {	title : "UWA Widget",
							height: "250px",
							border: "0px",
							color:	"#000000",
							displayTitle: false,
							displayFooter: false,
							autoresize: false	};
	for(key in parameters) {
		this[key] = parameters[key];
	}
  	if(typeof this.container == undefined) {
		triggerEvent('OX_New_Error', 2 ,"No container defined for one or more UWA widgets");			
  	}
  	if(typeof this.moduleUrl == undefined) {
		triggerEvent('OX_New_Error', 2 ,"No URI defined for one or more UWA widgets");			  		
  	}	
  	if(typeof this.id == undefined) {
		triggerEvent('OX_New_Error', 2 ,"No id defined for one or more UWA widgets");			  		
  	}	  	
	this.iframe;
	this.renderWidget();  
	this.updateLayout();
}
UWAWidget.prototype = {

	setConfiguration : function (oConfiguration){
	    for(var sProp in oConfiguration) {
    	  this.configuration[sProp] = oConfiguration[sProp];
		 }
	    this.updateLayout();		
	},
	setBorderWidth : function (){
		
	},
	setBorder : function (){
		
	},
	setColor : function (){
		
	},
	setTitle : function (){
		
	},
	setHeight : function (sHeight){
		this.configuration.height = sHeight;
		this.updateLayout();
	},
	setCommUrl : function (){
		
	},
	setDatas : function (){
		
	},
	setPreferencesValues : function (url_params){
		this.url_params = url_params;
		this.updateIframeUrl();
	},
	diplayTitle : function (){
		
	},
	displayFooter : function (){
		
	},
	updateIframeUrl : function (){
		this.iframe.src = this.getIframeUrl();
	},
	getIframeUrl : function (){
		var sUrl = "";
		if (typeof this.standalone == "boolean" && this.standalone) {
			sUrl = this.moduleUrl + "?id=" + (this.id + (new Date()).getTime());
		} else {
			sUrl = "http://" + NV_HOST + "/api/uwa/frame/blog.php?id=" + (this.id + (new Date()).getTime()) + "&moduleUrl=" + this.moduleUrl;
		}					 
		for(var sParam in this.url_params) {
			sUrl += ('&' + sParam + '=' + this.url_params[sParam].format());
		}
		return sUrl;		
	},
	updateLayout : function (){
		//this.iframe.style.height = this.configuration.height;
		this.iframe.style.border =  this.configuration.border;
		this.iframe.style.color = this.configuration.color;
		this.iframe.setAttribute('height', this.configuration.height);
		this.iframe.setAttribute('frameBorder', 0);
	    this.iframe.setAttribute('scrolling', 'auto');
		this.iframe.style.width = '100%';
	},
	renderWidget : function (){
		this.iframe = newnode("iframe",0,{src:this.getIframeUrl()});
		this.container.appendChild(this.iframe);
	}
};
/**
 * 
 * All content on this website (including text, images, source
 * code and any other original works), unless otherwise noted,
 * is licensed under a Creative Commons License.
 * 
 * http://creativecommons.org/licenses/by-nc-sa/2.5/
 * 
 * Copyright (C) 2004-2010 Open-Xchange, Inc.
 * Mail: info@open-xchange.com 
 * 
 * @author Matthias Biggeleben <matthias.biggeleben@open-xchange.com>
 * 
 */

/*jslint white: true, browser: true, devel: true, evil: true, forin: true, undef: true, eqeqeq: true, immed: true */

/*global ox, jQuery, window, console, newWindow, urlify, activefolder : true, activemodule : true,
corewindow, json, AjaxRoot, session, OXMailMapping, _, triggerEvent, 
register, unregister, currentCalendarView, configGetKey, oMainFolderTree, config,
mail_printSingleMail, activeYear, activeMonth, activeDay, calendar_print,
getDayInSameWeek, calendar_getAllFoldersAttribute, getWWStartTime, getWWEndTime, 
showNode, changeView, setActiveFolder, mail_accounts, escapeRegExp, newAlert,
configuration_askforSave, isEmpty, configSetKey, configContainsKey, logout_location,
formatError, newConfirm, AlertPopup */

if (window.ox === undefined) {
    // required to run in "lightweight" windows
    /**
     * @name ox
     * @namespace
     */
    window.ox = {};
}

/**
 * @name ox.browser
 * @namespace
 */
ox.browser = (function () {
    // adopted from prototype.js
    var ua = navigator.userAgent;
    var isOpera = Object.prototype.toString.call(window.opera) === "[object Opera]";
    return {
        /** @lends ox.browser */
        /** is IE? */
        IE:     !!window.attachEvent && !isOpera,
        /** is Opera? */
        Opera:  isOpera,
        /** is WebKit? */
        WebKit: ua.indexOf('AppleWebKit/') > -1,
        /** is Gecko/Firefox? */
        Gecko:  ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1
    };
}());

/**
 * @name ox.test
 * @namespace
 */
ox.test = (function () {

    var $ = jQuery;
    
    return {
        /** @lends ox.test */
        
        /**
         * Execute JavaScript code via eval() in current window (helps SELENIUM tests).
         * Otherwise, internal tests, e.g. using "object.constructor", will fail due to permission problems.
         * @param {string} code JavaScript code
         * @returns {Object} Result of eval()
         * @example
         * // API call inside SELENIUM unit test
         * ox.test.evalScript("ox.api.calendar.compose({ data: { title: 'Test' } });");
         */
        evalScript: function (code) {
            return eval(code);
        },
        
        /**
         * Trigger an event in a nested window. First parameter is the window's GUID, second the event name.
         * Any further parameter is passed to the corresponding event handlers.
         * @param {string} guid Window GUID. "OX.#" refers to last window.
         * @param {string} name Event name
         * @example
         * ox.test.trigger("OX.1", "OX_Cancel_Object");
         */
        trigger: function (guid, name) {
            // exists?
            if (ox.api.window.exists(guid)) {
                // get arguments
                var args = $.makeArray(arguments);
                // remove guid
                args.shift();
                // get window & trigger event
                var w = ox.api.window.get(guid).window;
                w.triggerEvent.apply(w, args);
            }
        },
        
        /**
         * Trigger save event in a window (convenience function)
         * @param {string} guid Window GUID. "OX.#" refers to last window.
         * @example
         * // save object
         * ox.test.triggerSave("OX.1");
         */
        triggerSave: function (guid) {
            this.trigger(guid, ox.api.event.common.SaveObject);
        },
        
        /**
         * Trigger cancel event in a window (convenience function)
         * @param {string} guid Window GUID. "OX.#" refers to last window.
         * @example
         * // cancel object (might trigger dialog "Do you really want to cancel...?")
         * ox.test.triggerCancel("OX.1");
         */
        triggerCancel: function (guid) {
            this.trigger(guid, ox.api.event.common.CancelObject);
        }
    };
}());

/**
 * @name ox.util
 * @namespace
 */
ox.util = {
    
    /**
      * Get a new empty array (constructed by current window; useful in IE for cross-window issues)
      * @returns {Array} New empty array
      * @example
      * var tmp = ox.util.getArray(); // simply returns []
      */
    getArray: function () {
        return [];
    },
    
    /**
     * Serialize object (key/value pairs) to fit into URLs (e.g. a=1&b=2&c=HelloWorld)
     * @param {Object} obj Object to serialize
     * @param {string} [delimiter] Delimiter
     * @returns {string} Serialized object
     * @example
     * ox.util.serialize({ a: 1, b: 2, c: "text" });
     */
    serialize: function (obj, delimiter) {
        var tmp = [], e = encodeURIComponent;
        if (typeof obj === "object") {
            for (var id in (obj || {})) {
                if (obj[id] !== undefined) {
                    tmp.push(e(id) + "=" + e(obj[id]));
                }
            }
        }
        return tmp.join(delimiter === undefined ? "&" : delimiter);
    },
    
    /**
     * Deserialize object (key/value pairs)
     * @param {string} str String to deserialize
     * @param {string} [delimiter] Delimiter
     * @returns {Object} Deserialized object
     * @function
     * @name ox.util.deserialize
     * @example
     * ox.util.deserialize("a=1&b=2&c=text");
     */
    deserialize: function (str, delimiter) {
        var pairs = (str || "").split(delimiter === undefined ? "&" : delimiter);
        var i = 0, $l = pairs.length, pair, obj = {}, d = decodeURIComponent;
        for (; i < $l; i++) {
            pair = pairs[i];
            var keyValue = pair.split(/\=/), key = keyValue[0], value = keyValue[1];
            if (key !== "" || value !== undefined) {
                obj[d(key)] = d(value);
            }
        }
        return obj;
    },
    
    /**
     * This function simply writes its parameters to console.
     * Useful to debug callbacks, e.g. event handlers.
     * @example
     * ox.api.calendar.httpGet({ params: { id: 158302 }, success: ox.util.inspect });
     */
    inspect: function () {
        var args = jQuery.makeArray(arguments); // $ is not jQuery here
        args.unshift("Inspect");
        console.log.apply(console, args);
    },
    
    /**
     * Create absolute URL
     * @param {string} url (relative/absolute) URL
     * @returns {string} Absolute URL
     * @example
     * ox.util.getAbsoluteURL("index.html"); // returns protocol://host/path/index.html
     * ox.util.getAbsoluteURL("/index.html"); // returns protocol://host/index.html
     * ox.util.getAbsoluteURL("http://localhost/foo/index.html"); // returns http://localhost/foo/index.html
     */
    getAbsoluteURL: function (url) {
        // change?
        if (url.search(/^http/) !== -1) {
            return url;
        } else {
            var l = window.location;
            if (url.substr(0, 1) === "/") {
                // add protocol & host
                return l.protocol + "//" + l.host + url;
            } else {
                // strip file & inject version
                return l.href.replace(/\/[^\/]+$/, "/") + ox.api.window.core.oxProductInfo.build + "/" + url;
            }
        }
    },
    
    /**
     * Check whether an object is an array
     * @params {Object} obj The object to test
     * @returns {boolean} True/False
     */
    isArray: function (obj) {
        // we cannot use 'elem.constructor === array' under SELENIUM (permission denied error).
        // so, we simply rely on the existence of splice, unshift, and push.
        // We even cannot test if they are functions (does not work in IE).
        // Same reason: Object.prototype.toString.call(elem) == "[Object Array]" does not work.
        return obj.splice !== undefined && obj.unshift !== undefined && obj.push !== undefined;
    },
    
    /**
     * Simple test to check whether an object is an instance of Window or a DOM node
     * @params {Object} obj The object to test
     * @returns {boolean} True/False
     */
    isDOMObject: function (obj) {
        // not null, not undefined, but DOM node or window 
        return obj && (obj.nodeType || obj.setInterval);
    }
};

/** @namespace */
ox.api = (function () {
    
    // ref
    var $ = jQuery;

    // api
    var that;
    
    // debug
    var debugEnabled = false;
    var debug = !debugEnabled ? $.noop : function () {
        console.log.apply(this, arguments);
    };
    
    // clone
    var clone = function clone(elem) {
        if (typeof elem !== "object") {
            return elem;
        } else {
            var isArray = ox.util.isArray;
            var isDOMObject = ox.util.isDOMObject;
            var subclone = function (elem) {
                if (!elem) {
                    return null;
                } else {
                    var tmp = isArray(elem) ? [] : {}, prop;
                    for (var i in elem) {
                        prop = elem[i];
                        tmp[i] = typeof prop === "object" && !isDOMObject(prop) ? subclone(prop) : prop;
                    }
                    return tmp;
                }
            };
            return subclone(elem);
        }
    };
    
    // get hash & query
    var queryData = ox.util.deserialize(document.location.search.substr(1), /&/);
    var hashData = ox.util.deserialize(document.location.hash.substr(1), /&/);
    
    // default window options
    var defaultWindowOptions = {
        resizable: "yes",
        menubar: "no",
        toolbar: "no",
        status: "no",
        location: "yes",
        scrollbars: "no"
    };
    
    /**
     * Default window factory (can be overridden).
     * A window factory is a function that produces a new window (e.g. via window.open) or
     * an iframe. The function receives an options objects. After creating the new window,
     * the function calls registerWindow(guid, window) and calls the callback function options.success
     * with the same two parameters: 1) the new window object, and 2) the GUID of that window.
     * @public
     * @param {Object} opt Factory options
     * @param {string} opt.url Window URL
     * @param {string} opt.guid Window GUID
     * @param {Object} opt.options Window options
     */
    var windowFactory = function (opt) {
        if (opt.guid !== undefined) {
            // open
            var win = newWindow(ox.util.getAbsoluteURL(opt.url), that.window.serializeOptions(opt.options), opt.id);
            /// register window
            that.window.register(opt.guid, win);
            // set guid
            win.guid = opt.guid;
            // focus
            win.focus();
            // continuation
            opt.success(win, opt.guid);
        }
        else {
            console.error("windowFactory: guid is missing!");
        }
    };
    
    /**
     * Default window closer (can be overridden)
     * @public
     */
    var windowCloser = function (obj) {
        obj.window.close();
    };
    
    /**
     * Default window positioner
     * @name ox.api-windowPositioner
     * @public
     * @returns {Object} Bounds
     * @example
     * function () {
     *   return { x: 0, y: 0, width: 400, height: 300 };
     * }
     */
    var windowPositioner = function () {
        // width & height
        var width = screen.availWidth <= 1024 ? 800 : 1000;
        var height = screen.availHeight <= 800 ? 700 : Math.max(700, $(document).height());
        // return bounds
        return {
            x: Math.round((screen.width - width) / 2),
            y: Math.round((screen.height - height) / 2) - 25,
            width: width,
            height: height
        };
    };
    
    /**
     * Internal window preprocessor
     * @name ox.api-constructWindow
     * @function
     * @public
     * @param {string} urlPrefix URL path
     * @param {Object} o Options
     * @param {Object} o.data Data passed to the new window
     * @param {Object} o.params Params added to the url as hash (#anchor)
     * @param {function ()} o.success Called when the new window is created
     * @param {function ()} o.closer Custom closer function (called when closing the window)
     * @param {Object} o.bounds Window bounds {@link ox.api-windowPositioner}
     * @param {Object} o.options Window options {@link ox.api.setDefaultWindowOptions}
     * @returns {string} New window GUID
     */
    var constructWindow = function (urlPrefix, o) {
        // set data
        var guid = o.params.guid = that.window.setData(o.data);
        // build url
        var url = urlPrefix + "#" + ox.util.serialize(o.params);
        // open new window
        var opt = {
            guid: guid,
            url: url,
            options: $.extend(o.bounds, o.options),
            success: o.success, 
            closer: o.closer,
            id: o.id
        };
        debug("constructWindow", opt);
        windowFactory(opt);
        return guid;
    };
    
    // process options
    var processOptions = function (d, o) {
        // general defaults
        var defaults = {
            params: {},
            options: {},
            data: {},
            bounds: windowPositioner(),
            success: $.noop,
            closer: $.noop
        };
        // merge general defaults, local default, and specific options
        return $.extend(true, defaults, d || {}, o || {});
    };
    
    // process select options
    var processSelectOptions = function (module, o) {
        var defaultFolder = module === "mail" ? configGetKey("mail.folder.inbox") : configGetKey("folder")[module];
        var opt = $.extend({
            module: module,
            folder: defaultFolder,
            id: undefined,
            success: $.noop
        }, o || {});
        // copy
        opt.folder_id = opt.folder;
        return opt;
    };
    
    var processSelect = function (module, options) {
        // options
        var o = processSelectOptions(module, options);
        // change view (always; all users have mail)
        changeView("mail");
        // trigger event
        triggerEvent("OX_Direct_Linking", module, o);
        // call back
        o.success(o);
    };
    
    var processHTTPOptions = function (module, defaultParams, options, defaultOptions) {
        // options
        var o = $.extend(defaultOptions || {}, options || { params: {} });
        // params
        o.params = $.extend(defaultParams || {}, o.params || {});
        // set module
        o.module = module;
        // done
        return o;
    };
    
    // helper for setModal
    var modal = false;
    
    that = {
        /** @lends ox.api */

        /**
         * @name ox.api.event
         * @namespace
         */
        event: {
        
            /**
             * Register for global event
             * @param {string} name Event name
             * @param {function ()} fn Event handler
             * @example
             * ox.api.event.register(ox.api.event.common.ViewChanged, ox.util.inspect);
             */
            register: function (name, fn) {
                register(name, fn);
            },

            /**
             * Unregister for global event
             * @param {string} name Event name
             * @param {function ()} fn Event handler
             * @example
             * ox.api.event.unregister(ox.api.event.common.ViewChanged, ox.util.inspect);
             */
            unregister: function (name, fn) {
                unregister(name, fn);
            },
            
            /**
             * Trigger an event. First parameter is the event name.
             * Any further parameter is passed to the corresponding event handlers.
             * @param {string} name Event name
             */
            trigger: function (name) {
                triggerEvent.apply(window, arguments);
            },
            
            /**
             * A list of common events used throughout the UI
             * @name ox.api.event.common
             * @namespace
             */
            common: {
                /** @type string */
                ViewChanged: "OX_View_Changed",
                /** @type string */
                Refresh: "OX_Refresh",
                /** @type string */
                ConfigurationLoadedComplete: "OX_Configuration_Loaded_Complete",
                /** @type string */
                ConfigurationChanged: "OX_Configuration_Changed",
                /** @type string */
                LanguageChanged: "LanguageChanged",
                /** @type string */
                Logout: "Logout",
                /** @type string */
                SaveObject: "OX_SAVE_OBJECT",
                /** @type string */
                CancelObject: "OX_Cancel_Object",
                /** @type string */
                NewUnreadMail: "OX_New_Unread_Mail",
                /** @type string */
                Ready: "Ready"
            }
        },

        /**
         * @name ox.api.config
         * @namespace
         */
        config: (function () {
            
            return {
                /** @lends ox.api.config */
                
                /**
                 * Get configuration value
                 * @params {string} path Configuration path
                 * @params {Object} [defaultValue] Default value to be returned if the path does not exist 
                 * @returns {Object} Value
                 * @example
                 * // get team view zoom level
                 * ox.api.config.get("gui.calendar.teamview.zoom");
                 * // use default value if the configuration does not contain the given path
                 * ox.api.config.get("gui.calendar.teamview.zoom", 100);
                 */
                get: function (path, defaultValue) {
                    if (!path) { // undefined, null, ""
                        return config;
                    } else {
                        if (defaultValue === undefined) {
                            return configGetKey(path);
                        } else {
                            return this.contains(path) ? configGetKey(path) : defaultValue;
                        }
                    }
                },
                
                /**
                 * Set configuration value
                 * @params {string} path Configuration path
                 * @params {Object} value Value
                 * @example
                 * // set "save configuration after logout" to "true"
                 * ox.api.config.set("gui.global.save", 1);
                 */
                set: function (path, value) {
                    if (path) {
                        configSetKey(path, value);
                    }
                },
             
                /**
                 * Check if configuration contains a specific path
                 * @params {string} path Configuration path
                 * @retuns {boolean} True/False
                 * @example
                 * ox.api.config.contains("gui.global.save");
                 */
                contains: function (path) {
                    return configContainsKey(path);
                },
                
                /**
                 * Save configuration
                 * @params {Object} [options] Options
                 * @params {Object} [options.force] Force save
                 * @params {Object} [options.silent] If true no message will be shown
                 * @params {function (data)} [options.success] Success handler
                 * @params {function (error)} [options.error] Error handler
                 * @example
                 * // save configuration
                 * ox.api.config.save({
                 *   success: function () {
                 *      console.log("Configuration saved!");
                 *   }
                 * });
                 */
                save: function (options) {
                    // defaults
                    var opt = $.extend({
                        force: true,
                        silent: true,
                        success: $.noop,
                        error: $.noop
                    }, options || {});
                    // this is done via an event
                    ox.api.event.trigger(
                        "OX_Save_Configuration", opt.force, opt.silent, 
                        opt.success, opt.error
                    );
                }
            };
        }()),

        /**
         * @name ox.api.ui
         * @namespace
         */
        ui: (function () {
            
            // helper for setModule("mail")
            var setMail = function (cont) {
                // callback (step #4)
                var foundFolder = function (id) {
                    setActiveFolder(id, null, true);
                    triggerEvent("OX_Switch_Module", "mail", "newfolder");
                    cont();
                };
                // continuation (step #3)
                var processFolders = function () {
                    // cache is not cold
                    var cache = oMainFolderTree.cache.fast_access;
                    // get separator
                    var separator = configGetKey("modules.mail.defaultseparator") || "/";
                    // loop over available mail accounts
                    var i, acc;
                    for (i in mail_accounts.data.data) {
                        // get account
                        acc = mail_accounts.data.data[i];
                        // check
                        if (acc && acc.id < Infinity && (cache["default" + acc.id] || cache["default" + acc.id + separator + "INBOX"])) {
                            // found
                            foundFolder("default" + acc.id + separator + "INBOX");
                            return;
                        }
                    }
                    // if we come around here, we haven't found anything yet, so...
                    var rex = new RegExp("^default\\d+(" + escapeRegExp(separator) + "INBOX)?$"), match;
                    for (i in cache) {
                        if ((match = rex.exec(i))) {
                            foundFolder(match[1] ? i : i + separator + "INBOX");
                            return;
                        }
                    }
                    // still not found...
                    newAlert(
                        _("The E-Mail module is not available"), 
                        _("Unable to establish a connection to the E-Mail server. Possible reasons: the mail server is (temporarily) down or there are network connection problems. To prevent further errors the module has been disabled. Please contact your administrator."), /**i18n**/
                        null
                    );
                };
                // get folder (step #2)
                var getFolder = function () {
                    if (configGetKey("modules.folder.tree") === 1) {
                        processFolders();
                    } else {
                        // cache magic
                        oMainFolderTree.cache.do_load_subfolders("1", oMainFolderTree.cache.find_folder("1"), {}, processFolders);
                    }
                };
                
                // cache lookup (step #1)
                var lookup = function () {
                    // in cache?
                    if (isEmpty(oMainFolderTree.cache.fast_access)) {
                        // no, try again alter
                        setTimeout(lookup, 250);
                    } else {
                        getFolder();
                    }
                };
                // start with folder look up
                lookup();
            };
            
            // helper for public setModule
            var setModule = function (name, cont) {
                // continuation
                cont = cont || $.noop;
                // set global variable
                activemodule = name;
                // which module?
                switch (name) {
                case "mail":
                    setMail(cont);
                    break;
                case "configuration": 
                    activefolder = configGetKey("folder.configuration");
                    triggerEvent("OX_Switch_Module", name, "newfolder");
                    cont();
                    break;
                case "portal":
                    activefolder = undefined;
                    triggerEvent("OX_Switch_Module", name, "newfolder");
                    cont();
                    break;
                default:
                    setActiveFolder(configGetKey("folder")[name], null, true);
                    triggerEvent("OX_Switch_Module", name, "newfolder");
                    cont();
                    break;
                }
            };
            
            return {
                /** @lends ox.api.ui */
                
                /**
                 * Set active module
                 * @params {string} name Module name, e.g. mail, calendar etc.
                 * @params {function ()} cont Continuation
                 * @example
                 * ox.api.ui.setModule("calendar"); // switch to calendar
                 */
                setModule: function (name, cont) {
                    // configuration?
                    if (activemodule === "configuration") {
                        // ask first
                        configuration_askforSave(function () {
                            setModule(name, cont);
                        });
                    } else {
                        // set now
                        setModule(name, cont);
                    }
                },
                
                /**
                 * Get active module
                 * @returns {string} Active module
                 * @example
                 * // get current module, e.g. mail, calendar, etc.
                 * ox.api.ui.getModule();
                 */
                getModule: function () {
                    // quite simple
                    return activemodule;
                },
                
                /**
                 * Logout
                 * @params {Object} [options] Logout options
                 * @params {string} [options.location] Location to redirect to after logout
                 * @params {boolean} [options.force] If true no checks are done before, e.g. save configuration
                 * @example
                 * // logout
                 * ox.api.ui.logout({
                 *   location: "http://www.google.de", // landing page after logout
                 *   force: true // don't try to save configuration before logout
                 * });
                 */
                logout: function (options) {
                    // defaults
                    var opt = $.extend({
                        location: logout_location.format(),
                        force: false
                    }, options || {});
                    // api call
                    var processLogout = function () {
                        ox.api.http.GET({
                            module: "login",
                            params: {
                                action: "logout"
                            },
                            // on success or error
                            complete: function (data) {
                                // remove unload handlers
                                window.onbeforeunload = null;
                                $(window).unbind("beforeunload");
                                // redirect
                                window.setTimeout(function () { 
                                    window.location.replace(opt.location); 
                                }, 0);
                            }
                        });
                    };
                    // force logout?
                    if (opt.force === true) {
                        // go!
                        processLogout();
                    } else {
                        // check things first
                        var save = ox.api.config.get("gui.global.save", 1);
                        // save configuration before every logout?
                        if (save === 0) {
                            // no!
                            processLogout();
                        } else {
                            // yes!
                            // replace with 1 (might contain deprecated values)
                            ox.api.config.set("gui.global.save", 1);
                            // save configuration
                            ox.api.config.save({
                                success: function (data) {
                                    // logout
                                    processLogout();
                                },
                                error: function (error) {
                                    // could not save configuration?
                                    if (error !== null && error.code === "SES-0203") {
                                        // session expired, so...
                                        processLogout();
                                    } else {
                                        var msg = error ? "\n\n" + formatError(error) : "";
                                        newConfirm(
                                            _("Server Error:"),
                                            _("Your configuration settings could not be saved. All your changes will be lost. Do you really want to continue?") + msg, /*i18n*/ 
                                            AlertPopup.YESNO, null, null, 
                                            processLogout, // yes 
                                            $.noop // no
                                        );
                                    }
                                }
                            });
                        }
                    }
                }
            };
        }()),
        
        /**
         * @name ox.api.window
         * @namespace
         */
        window: (function () {
            
            // globally unique identifier
            var GUID = 1;
            
            var newGUID = function () {
                return "OX." + (GUID++);
            };
            
            var getGUID = function (obj) {
                if (typeof obj === "string") {
                    return obj === "OX.#" ? "OX." + (GUID - 1) : obj;
                } else {
                    return obj ? obj.guid : undefined;
                }
            };
            
            // window registry
            var windows = {};
            // window data
            var windowData = {};
            // is nested window?
            var isNested = function () {
                // already in core window? (we rely on this simple variable not to mess up SELENIUM tests)
                return window.oxCoreWindow === true ? false : true;
            };
            
            return {
                /** @lends ox.api.window */
                
                /**
                 * Returns true if current window is a nested window.
                 * @name ox.api.window.isNested
                 * @type Bool
                 */
                isNested: isNested(),
                
                /**
                 * Reference to topmost core window
                 * @name ox.api.window.core
                 * @type {Window}
                 */
                core: isNested() ? (window.opener || window.parent).ox.api.window.core : window,
                
                /**
                 * Sets a new window factory consisting of an opening and a closing function. See {@link ox.api-windowFactory}
                 * @param opener {function ()} The opening function
                 * @param closer {function ()} The closing function
                 * @example
                 * ox.api.setWindowFactory(
                 *   // opener
                 *   function (opt) {
                 *     // open
                 *     var win = window.open(opt, "", ox.api.serializeWindowOptions(opt.options));
                 *     // register window
                 *     ox.api.registerWindow(opt.guid, win);
                 *     // set guid
                 *     win.guid = opt.guid;
                 *     // continuation
                 *     opt.success(win, opt.guid);
                 *   },
                 *   // closer
                 *   function (obj) {
                 *     // call native function "close"
                 *     obj.window.close();
                 *   }
                 * );
                 */
                setFactory: function (opener, closer) {
                    if (opener !== undefined && closer !== undefined) {
                        windowFactory = opener;
                        windowCloser = closer;
                    } else {
                        console.error("setFactory(opener, closer): One factory is missing");
                    }
                },
                
                /**
                 * @description Set a window positioner
                 * @param {function ()} fn A function that returns the window bounds
                 * @see ox.api-windowPositioner
                 */
                setPositioner: function (fn) {
                    windowPositioner = fn;
                },
                
                /**
                 * Define new window defaults
                 * @param {Object} options Default options
                 * @example
                 * ox.api.setDefaultWindowOptions({
                 *   resizable: "yes",
                 *   menubar: "no",
                 *   toolbar: "no",
                 *   status: "no",
                 *   location: "yes",
                 *   scrollbars: "no"
                 * });
                 */
                setDefaultOptions: function (options) {
                    defaultWindowOptions = options;
                },
                
                /**
                 * Serialize window options
                 * @param {Object} o Window options (e.g. width, height, scrollbars etc.)
                 * @returns {string} Serialized string
                 */
                serializeOptions: function (o) {
                    // merge (take care not to extend the defaults permanently)
                    var opt = $.extend({}, defaultWindowOptions, o || {});
                    // copy bounds
                    opt.width = o.width;
                    opt.height = o.height;
                    opt.left = o.x;
                    opt.top = o.y;
                    // serialize
                    return ox.util.serialize(opt, ",");
                },
                
                /**
                 * Register a new window.
                 * @param {string} guid The GUID of the window
                 * @param {Window} window The new window object
                 * @param {Object} options Further information to be stored (extends internal registry object)
                 * @returns {Object} The internal registry object
                 */
                register: function (guid, window, options) {
                    windows[guid] = $.extend({ window: window }, options);
                    return windows[guid];
                },
                
                /**
                 * Check if a window is registered under the given GUID
                 * @param {string} guid Window GUID. "OX.#" refers to last window.
                 * @returns {Boolean}
                 */
                exists: function (guid) {
                    return windows[getGUID(guid)] !== undefined;
                },
                
                /**
                 * Get window from internal window registry
                 * @param {string} guid Window GUID. "OX.#" refers to last window.
                 * @returns {Object} Internal registry object
                 * @example
                 * ox.api.window.get("OX.1");
                 */
                get: function (guid) {
                    guid = getGUID(guid);
                    return windows[guid] !== undefined ? windows[guid] : null;
                },
                
                /**
                 * Get all windows from internal window registry
                 * @returns {Object} All open windows
                 * @example
                 * var windows = ox.api.window.getAll();
                 */
                getAll: function () {
                    return windows;
                },
                
                /**
                 * Passes data to a window
                 * @param {Object} data Data
                 * @param {string} [guid] Existing GUID (to update data)
                 * @returns {string} New/Existing GUID
                 * @example
                 * var guid = ox.api.window.setData({ hello: "world" });
                 */
                setData: function (data, guid) {
                    if (data !== undefined) {
                        guid = getGUID(guid);
                        // exists?
                        if (guid === undefined || windowData[guid] === undefined) {
                            // get new GUID
                            guid = newGUID();
                        }
                        debug("setData", guid, data);
                        windowData[guid] = { data: data || {} };
                    }
                    return guid;
                },
                
                /**
                 * Get window-specific data
                 * @param {string|Window} GUID
                 * @returns {Object} Data
                 * @example
                 * var data = ox.api.window.getData("OX.1");
                 * console.log(data);
                 */
                getData: function (obj) {
                    // get guid
                    var guid = getGUID(obj);
                    if (guid !== undefined) {
                        // get data
                        if (isNested()) {
                            return this.core.ox.api.window.getData(guid);
                        } else {
                            var d = windowData[guid];
                            debug("getData", guid, d.data, "identifiedBy", obj);
                            return clone(d.data);
                        }
                    } else {
                        console.error("getData: guid is missing in given window!");
                    }
                },
                
                /**
                 * Open a new window using the current window factory
                 * @param {string} path URL Path
                 * @param {Object} [options] Options
                 * @returns {string} New window GUID
                 * @example
                 * ox.api.window.open("test.html", { options: { locationbar: true, top: 100, left: 100, width: 400, height: 300 } });
                 */
                open: function (path, options) {
                    return constructWindow(path, processOptions({}, options));
                },
                
                /**
                 * Close a window
                 * @param {Window|string} obj window or GUID
                 * @example
                 * ox.api.window.close("OX.1");
                 * ox.api.window.close("OX.#"); // close last window
                 */
                close: function (obj) {
                    // get guid
                    var guid = getGUID(obj);
                    // defined?
                    if (windows[guid] !== undefined) {
                        windowCloser(windows[guid]);
                        delete windows[guid];
                        delete windowData[guid];
                    }
                },
                
                /**
                 * Select (i.e. focus) a window
                 * @param {Window|string} obj window or GUID
                 * @example
                 * ox.api.window.select("OX.1");
                 * ox.api.window.select("OX.#"); // select last window
                 */
                select: function (obj) {
                    // get guid
                    var guid = getGUID(obj);
                    // defined?
                    if (windows[guid] !== undefined) {
                        windows[guid].window.focus();
                    }
                },
                
                /**
                 * (Re-)select last window
                 * @example
                 * ox.api.window.selectLast();
                 * // same as
                 * ox.api.window.select("OX.#");
                 */
                selectLast: function () {
                    this.select("OX.#");
                },
                
                /**
                 * Returns last GUID
                 * @returns {string} GUID
                 * @example
                 * var guid = ox.api.window.getLastGUID();
                 */
                getLastGUID: function () {
                    return "OX." + (GUID - 1);
                },
                
                /**
                 * Returns next GUID
                 * @returns {string} GUID
                 */
                getNextGUID: function () {
                    return "OX." + (GUID);
                }
            };
        }()),
        
        /**
         * @param {string} [name] Name of the hash parameter
         * @returns {Object} Value or all values
         */
        getHash: function (name) {
            return name === undefined ? hashData : hashData[name];
        },

        /**
         * @param name {string} [Name] of the query parameter
         * @returns {Object} Value or all values
         */
        getParam: function (name) {
            return name === undefined ? queryData : queryData[name];
        },
        
        /**
         * Switch to modal mode
         * @param {Boolean} flag On/Off
         */
        setModal: function (flag) {
            // copy nodes from allnodes hash
            showNode("modal-dialog-decorator");
            showNode("modal-dialog");
            // show/hide?
            if (flag) {
                $("#modal-dialog-decorator").show();
                $("#modal-dialog").show();
            } else {
                $("#modal-dialog-decorator").hide();
                $("#modal-dialog").hide();
            }
            // remember
            modal = !!flag;
        },
        
        isModal: function () {
            return modal;
        },
        
        /**
         * @name ox.api.mail
         * @namespace
         */
        mail: (function () {
        
            var open = function (options) {
                // options
                var opt = processOptions({
                    params: {
                        folder: activefolder === undefined ? "default0/INBOX": activefolder,
                        id: undefined,
                        account: undefined,
                        noimg: !corewindow.loadblockedimages,
                        action: ""
                    },
                    message: undefined,
                    closer: function (opt) {
                        that.window.get(opt.guid).window.triggerEvent("OX_Cancel_Object");
                    }
                }, options);
                var p = opt.params;
                // message object?
                if (opt.message !== undefined) {
                    p.folder = opt.message.folder_id;
                    p.id = opt.message.id;
                }
                // fix account
                if (p.account === undefined) {
                    p.account = corewindow.getMailAccountIdByFolder(p.folder);
                }
                if ((p.id + "").match(/^default/)) {
                    p.account = corewindow.getMailAccountIdByFolder(p.id);
                }
                // construct
                return constructWindow("newMail.html", opt);
            };
            
            return {
                /** @lends ox.api.mail */
                
                /**
                 * Select an email (changes view & folder)
                 * @param {Object} options Options
                 * @param {string} options.id Id
                 * @param {string} [options.folder] Folder
                 * @param {function ()} [options.success] Callback
                 * @example
                 * ox.api.mail.select({ id: "4451" });
                 * ox.api.mail.select({ id: "4451", folder: "default0/INBOX" });
                 */
                select: function (options) {
                    processSelect("mail", options);
                },
                
                /**
                 * Generic open function
                 * @param {Object} [options] Options {@link ox.api-constructWindow}
                 * @returns {string} New window GUID
                 */
                open: function (options) {
                    return open(options);
                },

                /**
                 * Open compose dialog
                 * @param {Object} [options] Options {@link ox.api-constructWindow}
                 * @returns {string} New window GUID
                 * @example
                 * // open simple dialog 
                 * ox.api.mail.compose();
                 * 
                 * // open dialog with subject set
                 * ox.api.mail.compose({ data: { subject: "Hello World" } });
                 * 
                 * // open dialog with subject, recipients, and mail text set
                 * ox.api.mail.compose({
                 *   data: {
                 *     to: [["Max Mustermann", "max.mustermann@example.org"], ["John Doe", "john.doe@exmaple.org"]],
                 *     subject: "Meeting Minutes",
                 *     priority: 2,
                 *     mailtext: "Hi Max and John,&lt;br/>&lt;br/>enclosed you'll find the meeting minutes from this morning.&lt;br/>&lt;br/>Cheers,&lt;br/>Lila", 
                 *     disp_notification_to: true
                 *   }
                 * });
                 */
                compose: function (options) {
                    return open($.extend(true, options || {}, { params: { action: "" }}));
                },

                /**
                 * Open "forward" dialog
                 * @param {Object} [options] Options {@link ox.api-constructWindow}
                 * @returns {string} New window GUID
                 * @example
                 * ox.api.mail.forward({ params: { id: 4415 }});
                 */
                forward: function (options) {
                    return open($.extend(true, options || {}, { params: { action: "forward" }}));
                },

                /**
                 * Open "reply" dialog
                 * @param {Object} [options] Options {@link ox.api-constructWindow}
                 * @returns {string} New window GUID
                 * @example
                 * ox.api.mail.reply({ params: { id: 4415 }});
                 */
                reply: function (options) {
                    return open($.extend(true, options || {}, { params: { action: "reply" }}));
                },

                /**
                 * Open "replyAll" dialog
                 * @param {Object} [options] Options {@link ox.api-constructWindow}
                 * @returns {string} New window GUID
                 * @example
                 * ox.api.mail.replyAll({ params: { id: 4415 }});
                 */
                replyAll: function (options) {
                    return open($.extend(true, options || {}, { params: { action: "replyAll" }}));
                },

                /**
                 * Open "edit draft" dialog
                 * @param {Object} [options] Options {@link ox.api-constructWindow}
                 * @returns {string} New window GUID
                 * @example
                 * ox.api.mail.draft({ params: { id: 4415 }});
                 */
                draft: function (options) {
                    return open($.extend(true, options || {}, { params: { action: "draft" }}));
                },

                /**
                 * Open detail view
                 * @param {Object} [options] Options {@link ox.api-constructWindow}
                 * @returns {string} New window GUID
                 * @example
                 * ox.api.mail.view({params: { id: 4415 }});
                 * ox.api.mail.view({params: { id: 4415, folder: "default0/INBOX" }});
                 */
                view: function (options) {
                    // options
                    var opt = processOptions({
                        params: {
                            id: undefined,
                            folder: "default0/INBOX",
                            sid: undefined
                        },
                        closer: function (opt) {
                            that.window.get(opt.guid).window.triggerEvent("OX_Cancel_Object");
                        }
                    }, options);
                    // set type
                    opt.params.type = "isNestedMessage";
                    // construct
                    return constructWindow("detailMail.html", opt);
                },
                
                /**
                 * Open source view
                 * @param {Object} [options] Options {@link ox.api-constructWindow}
                 * @returns {string} New window GUID
                 * @example
                 * ox.api.mail.showSource({ params: { id: 4415 }});
                 */
                showSource: function (options) {
                    // options
                    var opt = processOptions({
                        params: {
                            triggerevent: "OX_Popup_Cb",
                            folder: "default0/INBOX"
                        },
                        mail: undefined,
                        options: { scrollbars: "yes" },
                        closer: function (opt) {
                            that.window.close(that.window.get(opt.guid).window);
                        }
                    }, options);
                    // handler
                    var handler = function (win) {
                        // get mail source
                        json.get(AjaxRoot + "/mail?" + 
                            ox.util.serialize({ action: "get", session: session, folder: opt.params.folder, id: opt.params.id, src: "true" }),
                            null,
                            function (response) {
                            if (opt.mail) {
                                OXMailMapping.editObjectsInternal([opt.mail], null, true, [{ flag: 32, bool: true }]);
                            }
                            var text = _("Unable to display E-Mail source."); /*i18n*/
                            if (response.data) {
                                text = response.data.replace(/\r/g, ""); // IE does not like CRs here
                            }
                            // show source
                            var doc = win.document, area;
                            $("body", doc).css({
                                margin: "0px",
                                padding: "10px",
                                backgroundColor: "lightyellow",
                                fontFamily: "Arial, Helvetica, sans-serif",
                                fontSize: "9pt",
                                borderTop: "5px solid #fc0"
                            }).append(
                                $("<div/>", doc).css({ 
                                    position: "absolute",
                                    top: "5px", 
                                    left: "0px", 
                                    right: "10px", 
                                    height: "25px", 
                                    lineHeight: "25px",
                                    textAlign: "right"
                                }).
                                append(
                                    $("<a/>", doc).attr({ href: "#" }).
                                    text(_("Close")).
                                    bind("click", function (e) {
                                        ox.api.window.close(win);
                                    })
                                )
                            ).append(
                                $("<div/>", doc).css({
                                    position: "absolute",
                                    top: "30px",
                                    left: "0px",
                                    right: "0px",
                                    bottom: "0px",
                                    padding: "10px"
                                }).append(
                                    area = $("<textarea/>", doc).css({
                                        width: "100%",
                                        height: "100%",
                                        backgroundColor: "lightyellow",
                                        border: "0px none",
                                        whiteSpace: "pre"
                                    }).val(text)
                                )
                            );
                            // focus textarea
                            area.focus();
                            // unregister
                            unregister('OX_Popup_Cb', handler);
                        });
                    };
                    // talk via event
                    register('OX_Popup_Cb', handler);
                    // construct
                    return constructWindow("popup_trigger_cb.html", opt);
                },
                
                /**
                 * Open print view
                 * @param {Object} [options] Options {@link ox.api-constructWindow}
                 * @example
                 * ox.api.mail.print({ id: 4415 });
                 */
                print: function (options) {
                    // options
                    var opt = processOptions({
                        id: undefined,
                        folder: "default0/INBOX",
                        closer: function (opt) {
                            ox.api.window.close(opt.guid);
                        }
                    }, options);
                    // get mail
                    this.httpGet({
                        params: {
                            id: opt.id,
                            folder: opt.folder,
                            view: "noimg"
                        },
                        success: function (data) {
                            // copy property
                            data.attachments_html_noimage = data.attachments;
                            // print (global function in js/mail.js)
                            mail_printSingleMail(data, opt.closer);
                        }
                    });
                },
                
                /**
                 * Get mail (HTTP API)
                 * @param {Object} [options] Options
                 */
                httpGet: function (options) {
                    // options
                    var opt = options || { params: {} };
                    // prepare options
                    ox.api.http.GET({
                        module: "mail",
                        params: {
                            action: "get",
                            id: opt.params.id,
                            folder: opt.params.folder || "default0/INBOX",
                            view: undefined
                        },
                        success: opt.success || $.noop,
                        error: opt.error || $.noop,
                        appendColumns: false
                    });
                }
            };
        }()),
        
        /**
         * @namespace
         * @name ox.api.calendar
         */
        calendar: (function () {
            
            var fix = function (o) {
                var p = o.params;
                p.folder_id = p.folder_id !== undefined ? o.folder_id : p.folder;
                delete p.folder;
                // recurrence position set?
                if (p.recurrence_position !== undefined && p.recurrence_position > 0) {
                    p.singleappointment = "yes";
                }
                return o;
            };
            
            var open = function (options) {
                // options
                var opt = processOptions({
                    params: {
                        modul: "new",
                        folder: config.folder.calendar,
                        view: currentCalendarView,
                        folderOwner: undefined,
                        start_date: new Date().getTime(),
                        end_date: "",
                        full_time: false,
                        id: undefined
                    },
                    closer: function (opt) {
                        that.window.get(opt.guid).window.triggerEvent("OX_Cancel_Object");
                    }
                }, options);
                // construct
                return constructWindow("newAppointment.html", fix(opt));
            };
            
            return {
                /** @lends ox.api.calendar */
                
                /**
                 * Select an appointment
                 * @param {Object} options Options
                 * @param {string} options.id Id
                 * @param {string} [options.folder] Folder
                 * @param {function ()} [options.success] Callback
                 * @example
                 * ox.api.calendar.select({ id: "158402" });
                 */
                select: function (options) {
                    processSelect("calendar", options);
                },
                
                /**
                 * Open compose dialog. Fields are described <a href="http://oxpedia.org/index.php?title=HTTP_API#DetailedTaskAndAppointmentData">here</a>.
                 * @param {Object} [options] Options {@link ox.api-constructWindow}
                 * @param {Object} [options.data] Data. Prefilled fields.
                 * @returns {string} New window GUID
                 * @example
                 * // open simple dialog
                 * ox.api.calendar.compose();
                 * 
                 * // open dialog with fields set
                 * ox.api.calendar.compose({
                 *   data: {
                 *     title: "Meeting",
                 *     location: "Berlin",
                 *     start_date: 1280919600000, // UTC
                 *     end_date: 1280937600000, // UTC
                 *     private_flag: false,
                 *     shown_as: 3,
                 *     full_time: false,
                 *     categories: "Meeting, Business",
                 *     note: "Please be prepared for the meeting.",
                 *     alarm: 300 // = 300 seconds = 5 minutes
                 *   }
                 * });
                 *
                 * // how to deal with timestamps
                 * ox.api.calendar.compose({
                 *   data: {
                 *     title: "Do not use local time!",
                 *     start_date: Date.UTC(2010, 0, 1, 12, 0, 0), // shows 12:00 PM
                 *     end_date: Date.UTC(2010, 0, 1, 14, 0, 0), // shows 02:00 PM (14:00)
                 *   }
                 * });
                 * 
                 * // open dialog with exemplary success handler
                 * ox.api.calendar.compose({
                 *   data: {
                 *     title: "Using the success handler",
                 *   },
                 *   success: function (win) {
                 *     // focus core window
                 *     window.focus()
                 *     // say hello
                 *     alert("Hello");
                 *     // focus new window via select
                 *     ox.api.window.select(ox.api.window.getLastGUID()); // e.g. "OX.1"
                 *   }
                 * });
                 */
                compose: function (options) {
                    // get next guid
                    var guid = ox.api.window.getNextGUID();
                    // get options
                    var opt = $.extend(true, options || {}, { params: { modul: "new" }});
                    // continuation
                    var cont = function (folder) {
                        // for private or shared folders add owner to participant list
                        if (folder.oxfolder.data.type === 1 || folder.oxfolder.data.type === 3) {
                            opt.params.folderOwner = folder.oxfolder.data.created_by;
                        } else {
                            opt.params.folderOwner = configGetKey('identifier');
                        }
                        // go!
                        open(opt);
                    };
                    // get folder data (default is active folder)
                    var id = opt.params.folder !== undefined ? opt.params.folder : (activefolder ? activefolder : config.folder.calendar);
                    // first try
                    oMainFolderTree.cache.get_folder(id, function (folder) {
                        // is calendar?
                        if (folder.oxfolder.data.module === "calendar") {
                            // ok
                            cont(folder);
                        } else {
                            // fail?
                            if (opt.params.folder !== undefined) {
                                // yes
                                triggerEvent("OX_New_Error", 4, _("Can not create appointment in a folder other than appointment folder")); /*i18n*/
                            } else {
                                // try default calendar folder
                                oMainFolderTree.cache.get_folder(config.folder.calendar, cont);
                            }
                        }
                    });
                    // return potential GUID
                    return guid;
                },
                
                /**
                 * Open edit dialog
                 * @param {Object} [options] Options {@link ox.api-constructWindow}
                 * @returns {string} New window GUID
                 * @example
                 * ox.api.calendar.edit({ params: { id: 158302 }}); // edit appointment
                 * ox.api.calendar.edit({ params: { id: 158374, recurrence_position: 0 }}); // edit series
                 * ox.api.calendar.edit({ params: { id: 158374, recurrence_position: 1 }}); // edit exception (1st appointment of series)
                 * ox.api.calendar.edit({ params: { id: 158374, recurrence_position: 5 }}); // edit exception (5th appointment of series)
                 */
                edit: function (options) {
                    return open($.extend(true, options || {}, { params: { modul: "edit" }}));
                },

                /**
                 * Open print dialog
                 * @param {Object} [options] Options {@link ox.api-constructWindow}
                 * @returns {string} New window GUID
                 * @example
                 * ox.api.calendar.print(); // prints day view (today)
                 * ox.api.calendar.print({ view: "month" }); // prints month view (current month)
                 * ox.api.calendar.print({ view: "week", year: 2010, month: 1 }); // prints month view (January 2010)
                 */
                print: function (options) {
                    
                    // options
                    var opt = processOptions({
                        view: "day",
                        day: activeDay,
                        month: activeMonth + 1,
                        year: activeYear,
                        options: { scrollbars: "yes" },
                        closer: function (opt) {
                            ox.api.window.close(opt.guid);
                        }
                    }, options);
                    // get start date
                    var date = new Date(Date.UTC(opt.year, opt.month - 1, opt.day)), start, days;
                    var self = this;
                    // printer
                    var print = function (template, start, end, numdays) {
                        // handler
                        var success = opt.success;
                        opt.success = function (win, guid) {
                            // get via HTTP API
                            self.httpPrint({
                                params: {
                                    start: start,
                                    end: end,
                                    template: template,
                                    folder: calendar_getAllFoldersAttribute() ? undefined : activefolder,
                                    work_week_day_amount: numdays ? numdays : undefined
                                },
                                success: function (response) {
                                    // still open?
                                    if (win && !window.closed) {
                                        // insert response
                                        win.document.open();
                                        // remove window.print from onload
                                        response = response.replace(/window\.print\(\)/, "");
                                        // firefox likes this
                                        if (ox.browser.Gecko) {
                                            response = response.replace(/<\/html>/, '<script type="text/javascript">window.print();</script>');
                                        }
                                        win.document.write(response);
                                        win.document.close();
                                        // for IE/Safari
                                        if (ox.browser.IE || ox.browser.WebKit) {
                                            setTimeout(function () {
                                                win.print();
                                            }, 0);
                                        }
                                        // continue
                                        success(win, guid);
                                    }
                                }
                            });
                        };
                        // open dummy window
                        return constructWindow("newInfoItemHidden.html", opt);
                    };
                    // process view
                    switch (opt.view) {
                    case "day":
                        start = date.getTime();
                        return print("cp_dayview_table.tmpl", start, start + 864e5, null);
                    case "workweek":
                        start = getDayInSameWeek(date, configGetKey("gui.calendar.workweek.startday")) * 864e5;
                        days = configGetKey("gui.calendar.workweek.countdays");
                        return print("cp_weekview_table.tmpl", start, start + days * 864e5, days);
                    case "week":
                        start = getDayInSameWeek(date, 1) * 864e5; // 1 = Monday
                        return print("cp_weekview_table.tmpl", start, start + 6048e5, null);
                    case "month":
                        return print("cp_monthview_table.tmpl", Date.UTC(opt.year, opt.month - 1, 1), Date.UTC(opt.year, opt.month, 1), null);
                    }
                },
                
                /**
                 * Get calendar object (HTTP API)
                 * @param {Object} options Request options
                 * @param {Object} options.params Request parameters
                 * @param {function (response)} options.success Success handler
                 * @param {function (response)} options.error Error handler
                 * @example
                 * ox.api.calendar.httpGet({ params: { id: 158302 } });
                 * ox.api.calendar.httpGet({ params: { id: 158302 }, success: ox.util.inspect });
                 */
                httpGet: function (options) {
                    ox.api.http.GET(processHTTPOptions("calendar", {
                        action: "get",
                        folder: config.folder.calendar,
                        id: undefined
                    }, options));
                },
                
                /**
                 * Get all calendar objects in a folder (HTTP API)
                 * @param {Object} options Request options#
                 * @example
                 * ox.api.calendar.httpGetAll();
                 * ox.api.calendar.httpGetAll({ folder: 4384 });
                 */
                httpGetAll: function (options) {
                    var now = (new Date()).getTime();
                    ox.api.http.GET(processHTTPOptions("calendar", {
                        action: "all",
                        folder: config.folder.calendar,
                        start: now,
                        end: now + 1000 * 3600 * 24 * 7
                    }, options));
                },
                
                /**
                 * Print calendar via template (HTTP API)
                 */
                httpPrint: function (options) {
                    var now = (new Date()).getTime();
                    ox.api.http.GET(processHTTPOptions("printCalendar", {
                        start: now,
                        end: now + 864e5, // day
                        template: "cp_dayview_table.tmpl", // day
                        folder: undefined, // = all folders
                        work_day_start_time: getWWStartTime(),
                        work_day_end_time: getWWEndTime(),
                        work_week_day_amount: undefined
                    }, options, { 
                        dataType: "text",
                        appendColumns: false
                    }));
                }
            };
            
        }()),
        
        /**
         * @namespace
         * @name ox.api.contact
         */
        contact: (function () {
            
            var open = function (file, options) {
                // options
                var opt = processOptions({
                    params: {
                        modul: "new",
                        currentView: undefined,
                        folder: config.folder.contacts, // = personal folder. 6 = global address book
                        session: session
                    },
                    closer: function (opt) {
                        that.window.get(opt.guid).window.triggerEvent("OX_Cancel_Object");
                    }
                }, options);
                // construct
                return constructWindow(file, opt);
            };
            
            return {
                /** @lends ox.api.contact */
                
                /**
                 * Select a contact
                 * @param {Object} options Options
                 * @param {string} options.id Id
                 * @param {string} [options.folder] Folder
                 * @param {function ()} [options.success] Callback
                 * @example
                 * ox.api.contact.select({ id: "93437" });
                 */
                select: function (options) {
                    processSelect("contacts", options);
                },
                
                /**
                 * Open "compose new contact" dialog. . Fields are described <a href="http://oxpedia.org/index.php?title=HTTP_API#DetailedContactData">here</a>.
                 * @param {Object} [options] Options {@link ox.api-constructWindow}
                 * @returns {string} New window GUID
                 * @example
                 * // open simple dialog
                 * ox.api.contact.compose();
                 * 
                 * // open dialog with predefined fields
                 * ox.api.contact.compose({
                 *   data: {
                 *     title: "Mr.", first_name: "Max", last_name: "Mustermann",
                 *     company: "Mustermann Inc.", street_business: "Musterstr. 11",
                 *     postal_code_business: "12345", city_business: "Berlin", state_business: "Berlin",
                 *     country_business: "DE", telephone_business1: "+49 1234 5678",
                 *     fax_business: "+49 1234 56990", email1: "max.mustermann@example.org",
                 *     url: "http://www.mustermann.inc", number_of_employees: "6", department: "IT",
                 *     note: "Business Contact, Mustermann Inc.", birthday: 147913200000
                 *   }
                 * });
                 */
                compose: function (options) {
                    return open("newContact.html", $.extend(true, options || {}, { params: { modul: "new" }}));
                },

                /**
                 * Open "compose distribution list" dialog
                 * @param {Object} [options] Options {@link ox.api-constructWindow}
                 * @returns {string} New window GUID
                 * @example
                 * // open simple dialog
                 * ox.api.contact.composeDistributionList();
                 * 
                 * // open dialog with prefilled fields
                 * ox.api.contact.composeDistributionList({
                 *   data: {
                 *     title: "My List", 
                 *     distribution_list: [
                 *       { display_name: "Max Mustermann", mail: "max.muster@example.org"}, 
                 *       { display_name: "John Doe", mail: "john.doe@example.org" }
                 *     ]
                 *   }
                 * });
                 */
                composeDistributionList: function (options) {
                    return open("newDistributionList.html", $.extend(true, options || {}, { params: { modul: "new" }}));
                },
                
                /**
                 * Open "edit contact" dialog
                 * @param {Object} [options] Options {@link ox.api-constructWindow}
                 * @returns {string} New window GUID
                 * @example
                 * ox.api.contact.edit({ params: { id: 73303 }});
                 * ox.api.contact.edit({ params: { id: 73303, folder: 4385 }}); // private contact in private contact folder (default)
                 * ox.api.contact.edit({ params: { id: 29, folder: 6 }}); // 6 = global address book
                 */
                edit: function (options) {
                    return open("newContact.html", $.extend(true, options || {}, { params: { modul: "edit" }}));
                },
                
                /**
                 * Open "edit distribution list" dialog
                 * @param {Object} [options] Options {@link ox.api-constructWindow}
                 * @returns {string} New window GUID
                 * @example
                 * ox.api.contact.editDistributionList({ params: { id: 115787 }});
                 * ox.api.contact.editDistributionList({ params: { id: 115787, folder: 4385 }});
                 */                
                editDistributionList: function (options) {
                    return open("newDistributionList.html", $.extend(true, options || {}, { params: { modul: "edit" }}));
                },

                /**
                 * Open dialog to create new contact based on existing contact
                 * @param {Object} [options] Options {@link ox.api-constructWindow}
                 * @returns {string} New window GUID
                 * @example
                 * ox.api.contact.duplicate({ params: { id: 29, folder: 6 }}); // 6 = global address book
                 */                
                duplicate: function (options) {
                    return open("newContact.html", $.extend(true, options || {}, { params: { modul: "duplicate" }}));
                },

                /**
                 * Open dialog to create new distribution list based on existing list
                 * @param {Object} [options] Options {@link ox.api-constructWindow}
                 * @returns {string} New window GUID
                 * @example
                 * ox.api.contact.duplicateDistributionList({ params: { id: 115787 }});
                 */
                duplicateDistributionList: function (options) {
                    return open("newDistributionList.html", $.extend(true, options || {}, { params: { modul: "duplicate" }}));
                }
            };
            
        }()),
        
        /**
         * @namespace
         * @name ox.api.task
         */
        task: (function () {
            
            var open = function (options) {
                // options
                var opt = processOptions({
                    params: {
                        modul: undefined,
                        folder: configGetKey("folder.tasks"),
                        session: session
                    },
                    closer: function (opt) {
                        that.window.get(opt.guid).window.triggerEvent("OX_Cancel_Object");
                    }
                }, options);
                // construct
                return constructWindow("newTask.html", opt);
            };
            
            return {
                /** @lends ox.api.task */
                
                /**
                 * Select a task
                 * @param {Object} options Options
                 * @param {string} options.id Id
                 * @param {string} [options.folder] Folder
                 * @param {function ()} [options.success] Callback
                 * @example
                 * ox.api.task.select({ id: "3810" });
                 */
                select: function (options) {
                    processSelect("tasks", options);
                },
                
                /**
                 * Open "compose new task" dialog
                 * @param {Object} [options] Options {@link ox.api-constructWindow}
                 * @returns {string} New window GUID
                 * @example
                 * // open simple dialog
                 * ox.api.task.compose();
                 * 
                 * // open dialog with predefined fields
                 * ox.api.task.compose({
                 *   data: {
                 *     title: "Setup new Hardware",
                 *     start_date: 1280919600000,
                 *     end_date: 1280937600000,
                 *     priority: 3,
                 *     note: "We need to setup the new hardware. Be prepared.",
                 *     alarm: 1280937600000
                 *   }
                 * });
                 */
                compose: function (options) {
                    return open($.extend(true, options || {}, { params: { modul: undefined }}));
                },
                
                /**
                 * Open "edit task" dialog
                 * @param {Object} [options] Options {@link ox.api-constructWindow}
                 * @returns {string} New window GUID
                 * @example
                 * ox.api.task.edit({ params: { id: 3616 }});
                 */
                edit: function (options) {
                    return open($.extend(true, options || {}, { params: { modul: "edit" }}));
                },
                
                /**
                 * Open dialog to create new task based on existing task
                 * @param {Object} [options] Options {@link ox.api-constructWindow}
                 * @returns {string} New window GUID
                 * @example
                 * ox.api.task.duplicate({ params: { id: 3616 }});
                 */
                duplicate: function (options) {
                    return open($.extend(true, options || {}, { params: { modul: "duplicate" }}));
                }
            };
        }()),
        
        /**
         * @namespace
         * @name ox.api.infostore
         */
        infostore: (function () {
            
            var fix = function (options) {
                options.params.edit_id = options.params.id;
                delete options.params.id;
                return options;
            };
            
            var open = function (options) {
                // options
                var opt = processOptions({
                    params: {
                        id: undefined,
                        folder: configGetKey("folder.infostore")
                    },
                    closer: function (opt) {
                        that.window.get(opt.guid).window.triggerEvent("OX_Cancel_Object");
                    }
                }, options);
                // construct
                return constructWindow("newInfoItemMain.html", fix(opt));
            };
            
            return {
                /** @lends ox.api.infostore */
                
                /**
                 * Select an infostore item
                 * @param {Object} options Options
                 * @param {string} options.id Id
                 * @param {string} [options.folder] Folder
                 * @param {function ()} [options.success] Callback
                 * @example
                 * ox.api.infostore.select({ id: "16560" });
                 */
                select: function (options) {
                    processSelect("infostore", options);
                },
                
                /**
                 * Open compose dialog
                 * @param {Object} [options] Options {@link ox.api-constructWindow}
                 * @returns {string} New window GUID
                 * @example
                 * // open simple dialog
                 * ox.api.infostore.compose();
                 * 
                 * // prefill some fields
                 * ox.api.infostore.compose({
                 *   data: {
                 *     title: "Exemplary document",
                 *     description: "Latest documentation", 
                 *     categories: ["Important", "Internal", "Business"]
                 *   }
                 * });
                 */
                compose: function (options) {
                    return open($.extend(true, options || {}, { params: { id: -1 }}));
                },
                
                /**
                 * Open "compose from attachment" dialog
                 * @param {Object} [options] Options {@link ox.api-constructWindow}
                 * @returns {string} New window GUID
                 * @example
                 * ox.api.infostore.composeFromAttachment({
                 *   params: {
                 *     attachmail: 1,
                 *     filename: "test.doc",
                 *     mailid: 4711,
                 *     mailfolder: "default0/INBOX"
                 *   },
                 *   success: function (win) {
                 *     // do something on success
                 *   }
                 * });
                 */
                composeFromAttachment: function (options) {
                    return open($.extend(true, options || {}, { params: { id: -1 }}));
                },
                
                /**
                 * Open edit dialog
                 * @param {Object} [options] Options {@link ox.api-constructWindow}
                 * @returns {string} New window GUID
                 * @example
                 * ox.api.infostore.edit({ params: { id: 16560 }});
                 */
                edit: function (options) {
                    return open(options);
                },
                
                /**
                 * Download file
                 * @param {string} id Infostore item id
                 * @param {string} [filename] Filename (helps getting a useful filename in IE)
                 * @example
                 * ox.api.infostore.download(16449);
                 */
                download: function (id, filename) {
                    // continuation
                    var cont = function (id, filename) {
                        // url parts
                        var parts = [AjaxRoot, "infostore", filename];
                        // create url
                        var url = parts.join("/") + "?" + ox.util.serialize({
                            action: "document",
                            id: id,
                            content_type: "application/octet-stream",
                            session: session
                        });
                        // create hidden frame
                        var iframe = $("<iframe/>", { src: url }).css({
                            display: "none",
                            width: "0px",
                            height: "0px"
                        }).one("load", function (e) {
                            // remove frame immediately
                            setTimeout(function () {
                                iframe.remove();
                            }, 10);
                        }).appendTo(document.body);
                    };
                    // missing filename? (just in IE)
                    if (ox.browser.IE && filename === undefined) {
                        // get object via HTTP API
                        ox.api.http.GET({
                            module: "infostore",
                            params: {
                                action: "get",
                                id: 16449,
                                columns: "702"
                            },
                            success: function (response) {
                                cont(id, response.filename);
                            }
                        });
                    } else {
                        cont(id, filename);
                    }
                }
            };
        }()),
        
        /**
         * @namespace
         * @name ox.api.http
         */
        http: (function () {
            
            // default columns for each module
            var idMapping = {
                "common" : {
                    "1" : "id",
                    "2" : "created_by",
                    "3" : "modified_by",
                    "4" : "creation_date",
                    "5" : "last_modified",
                    "100" : "categories",
                    "101" : "private_flag",
                    "102" : "color_label",
                    "104" : "number_of_attachments",
                    "20" : "folder_id"
                },
                "mail" : {
                    "102" : "color_label",
                    "600" : "id",
                    "601" : "folder_id",
                    "602" : "attachment",
                    "603" : "from",
                    "604" : "to",
                    "605" : "cc",
                    "606" : "bcc",
                    "607" : "subject",
                    "608" : "size",
                    "609" : "sent_date",
                    "610" : "received_date",
                    "611" : "flags",
                    "612" : "level",
                    "613" : "disp_notification_to",
                    "614" : "priority",
                    "615" : "msgref",
                    "651" : "flag_seen",
                    "652" : "account_name"
                },
                "contacts" : {
                    "500" : "display_name",
                    "501" : "first_name",
                    "502" : "last_name",
                    "503" : "second_name",
                    "504" : "suffix",
                    "505" : "title",
                    "506" : "street_home",
                    "507" : "postal_code_home",
                    "508" : "city_home",
                    "509" : "state_home",
                    "510" : "country_home",
                    "511" : "birthday",
                    "512" : "marital_status",
                    "513" : "number_of_children",
                    "514" : "profession",
                    "515" : "nickname",
                    "516" : "spouse_name",
                    "517" : "anniversary",
                    "518" : "note",
                    "519" : "department",
                    "520" : "position",
                    "521" : "employee_type",
                    "522" : "room_number",
                    "523" : "street_business",
                    "525" : "postal_code_business",
                    "526" : "city_business",
                    "527" : "state_business",
                    "528" : "country_business",
                    "529" : "number_of_employees",
                    "530" : "sales_volume",
                    "531" : "tax_id",
                    "532" : "commercial_register",
                    "533" : "branches",
                    "534" : "business_category",
                    "535" : "info",
                    "536" : "manager_name",
                    "537" : "assistant_name",
                    "538" : "street_other",
                    "539" : "city_other",
                    "540" : "postal_code_other",
                    "541" : "country_other",
                    "542" : "telephone_business1",
                    "543" : "telephone_business2",
                    "544" : "fax_business",
                    "545" : "telephone_callback",
                    "546" : "telephone_car",
                    "547" : "telephone_company",
                    "548" : "telephone_home1",
                    "549" : "telephone_home2",
                    "550" : "fax_home",
                    "551" : "cellular_telephone1",
                    "552" : "cellular_telephone2",
                    "553" : "telephone_other",
                    "554" : "fax_other",
                    "555" : "email1",
                    "556" : "email2",
                    "557" : "email3",
                    "558" : "url",
                    "559" : "telephone_isdn",
                    "560" : "telephone_pager",
                    "561" : "telephone_primary",
                    "562" : "telephone_radio",
                    "563" : "telephone_telex",
                    "564" : "telephone_ttytdd",
                    "565" : "instant_messenger1",
                    "566" : "instant_messenger2",
                    "567" : "telephone_ip",
                    "568" : "telephone_assistant",
                    "569" : "company",
                    "570" : "image1",
                    "571" : "userfield01",
                    "572" : "userfield02",
                    "573" : "userfield03",
                    "574" : "userfield04",
                    "575" : "userfield05",
                    "576" : "userfield06",
                    "577" : "userfield07",
                    "578" : "userfield08",
                    "579" : "userfield09",
                    "580" : "userfield10",
                    "581" : "userfield11",
                    "582" : "userfield12",
                    "583" : "userfield13",
                    "584" : "userfield14",
                    "585" : "userfield15",
                    "586" : "userfield16",
                    "587" : "userfield17",
                    "588" : "userfield18",
                    "589" : "userfield19",
                    "590" : "userfield20",
                    "592" : "distribution_list",
                    "594" : "number_of_distribution_list",
                    "596" : "contains_image1",
                    "597" : "image_last_modified",
                    "598" : "state_other",
                    "599" : "file_as",
                    "104" : "number_of_attachments",
                    "601" : "image1_content_type",
                    "602" : "mark_as_distributionlist",
                    "605" : "default_address",
                    "524" : "internal_userid",
                    "606" : "image1_url"
                },
                "calendar" : {
                    "200" : "title",
                    "201" : "start_date",
                    "202" : "end_date",
                    "203" : "note",
                    "204" : "alarm",
                    "207" : "recurrence_position",
                    "208" : "recurrence_date_position",
                    "209" : "recurrence_type",
                    "212" : "days",
                    "213" : "days_in_month",
                    "214" : "month",
                    "215" : "interval",
                    "216" : "until",
                    "220" : "participants",
                    "221" : "users",
                    "400" : "location",
                    "401" : "full_time",
                    "402" : "shown_as"
                },
                "infostore" : {
                    "700" : "title",
                    "701" : "url",
                    "702" : "filename",
                    "703" : "file_mimetype",
                    "704" : "file_size",
                    "705" : "version",
                    "706" : "description",
                    "707" : "locked_until",
                    "708" : "file_md5sum",
                    "709" : "version_comment",
                    "710" : "current_version",
                    "711" : "number_of_versions"
                },
                "task" : {
                    "200" : "title",
                    "201" : "start_date",
                    "202" : "end_date",
                    "203" : "note",
                    "204" : "alarm",
                    "209" : "recurrence_type",
                    "212" : "days",
                    "213" : "days_in_month",
                    "214" : "month",
                    "215" : "internal",
                    "216" : "until",
                    "220" : "participants",
                    "221" : "users",
                    "300" : "status",
                    "301" : "percent_completed",
                    "302" : "actual_costs",
                    "303" : "actual_duration",
                    "305" : "billing_information",
                    "307" : "target_costs",
                    "308" : "target_duration",
                    "309" : "priority",
                    "312" : "currency",
                    "313" : "trip_meter",
                    "314" : "companies",
                    "315" : "date_completed"
                },
                "folders" : {
                    "1" : "id",
                    "2" : "created_by",
                    "3" : "modified_by",
                    "4" : "creation_date",
                    "5" : "last_modified",
                    "6" : "last_modified_utc",
                    "20" : "folder_id",
                    "300" : "title",
                    "301" : "module",
                    "302" : "type",
                    "304" : "subfolders",
                    "305" : "own_rights",
                    "306" : "permissions",
                    "307" : "summary",
                    "308" : "standard_folder",
                    "309" : "total",
                    "310" : "new",
                    "311" : "unread",
                    "312" : "deleted",
                    "313" : "capabilities",
                    "314" : "subscribed",
                    "315" : "subscr_subflds",
                    "316" : "standard_folder_type"
                },
                "user" : {
                    "610" : "aliases",
                    "611" : "timezone",
                    "612" : "locale",
                    "613" : "groups",
                    "614" : "contact_id",
                    "615" : "login_info"
                }
            };
            
            // extend with commons (not all modules use common columns, e.g. folders)
            $.extend(idMapping.contacts, idMapping.common);
            $.extend(idMapping.calendar, idMapping.common);
            $.extend(idMapping.infostore, idMapping.common); 
            delete idMapping.infostore["101"]; // not "common" here (exception)
            delete idMapping.infostore["104"];
            $.extend(idMapping.task, idMapping.common);
            $.extend(idMapping.user, idMapping.contacts, idMapping.common);
            
            // get all columns of a module
            var getAllColumns = function (module) {
                // get ids
                var ids = idMapping[module];
                // flatten this array
                var tmp = [];
                for (var column in ids) {
                    tmp.push(column);
                }
                tmp.sort(function (a, b) {
                    return a - b;
                });
                return tmp;
            };
            
            // transform arrays to objects
            var makeObject = function (data, module, columns) {
                // get ids
                var ids = idMapping[module];
                var obj = {}, i = 0, $l = data.length;
                // loop through data
                for (; i < $l; i++) {
                    // get id
                    var id = ids[columns[i]];
                    // extend object
                    obj[id] = data[i];
                }
                return obj;
            };
            
            var processOptions = function (options, type) {
                // defaults
                var o = $.extend({
                    module: "",
                    params: {},
                    data: {},
                    dataType: "json",
                    success: $.noop,
                    error: $.noop,
                    complete: $.noop,
                    appendColumns: type === "GET" ? false : true
                }, options || {});
                // prepend root
                o.url = AjaxRoot + "/" + o.module;
                // add session
                o.params.session = session;
                // add columns
                if (o.appendColumns === true && o.params.columns === undefined) {
                    o.params.columns = getAllColumns(o.module).join(",");
                }
                // data & body
                if (type === "GET" || type === "POST") {
                    // GET & POST
                    o.data = o.params;
                } else {
                    // PUT & DELETE
                    o.url += "?" + ox.util.serialize(o.params);
                    o.data = JSON.serialize(o.data);
                    o.processData = false;
                    o.contentType = "text/javascript";
                }
                // done
                return o;
            };
            
            var processData = function (data, module, columns) {
                // not array or no columns given?
                if (!$.isArray(data) || !columns) {
                    // typically from "action=get" (already sanitized)
                    return data;
                } else {
                    // POST/PUT - sanitize data
                    var i = 0, $l = data.length, sanitized = [];
                    var columnList = columns.split(",");
                    for (; i < $l; i++) {
                        sanitized.push(makeObject(data[i], module, columnList));
                    }
                    return sanitized;
                }
            };
            
            var ajax = function (options, type) {
                // process options
                var o = processOptions(options, type);
                // ajax
                $.ajax({
                    // type (GET, POST, PUT, ...)
                    type: type,
                    // url
                    url: o.url,
                    // data
                    data: o.data,
                    dataType: o.dataType,
                    contentType: o.contentType || "application/x-www-form-urlencoded",
                    // handler
                    success: function (response) {
                        // process response
                        if (response && response.error !== undefined) {
                            // server error
                            o.error(response);
                        } else {
                            // success
                            if (o.dataType === "json") {
                                // response? (logout e.g. hasn't any)
                                if (response) {
                                    var data = processData(response.data, o.module, o.params.columns);
                                    o.success(data);
                                } else {
                                    o.success({});
                                }
                            } else {
                                // e.g. plain text
                                o.success(response || "");
                            }
                        }
                    },
                    error: function (xhr) {
                        o.error({}, xhr);
                    },
                    complete: function (xhr) {
                        o.complete({}, xhr);
                    }
                });
            };
            
            return {
                /** @lends ox.api.http */
                
                /**
                 * Send a GET request
                 * @param {Object} options Request options
                 * @param {string} options.module Module, e.g. folder, mail, calendar etc.
                 * @param {Object} options.params URL parameters
                 * @example
                 * ox.api.http.GET({ module: "mail", params: { action: "all", folder: "default0/INBOX" }});
                 */
                GET: function (options) {
                    ajax(options, "GET");
                },
                
                /**
                 * Send a POST request
                 * @param {Object} options Request options
                 * @param {string} options.module Module, e.g. folder, mail, calendar etc.
                 * @param {Object} options.params URL parameters
                 */
                POST: function (options) {
                    ajax(options, "POST");
                },

                /**
                 * Send a PUT request
                 * @param {Object} options Request options
                 * @param {string} options.module Module, e.g. folder, mail, calendar etc.
                 * @param {Object} options.params URL parameters
                 */
                PUT: function (options) {
                    ajax(options, "PUT");
                },
                
                /**
                 * Send a DELETE request
                 * @param {Object} options Request options
                 * @param {string} options.module Module, e.g. folder, mail, calendar etc.
                 * @param {Object} options.params URL parameters
                 */
                DELETE: function (options) {
                    ajax(options, "DELETE");
                }
            };
        }())
    };
    
    // local functions (no bubbling)
    var local = (function () {
        var localFn = {};
        return {
            add: function (fn) {
                localFn[fn] = true;
                return this;
            },
            has: function (fn) {
                return localFn[fn] !== undefined;
            }
        };
    }());
    
    local.add(that.window.getData).
        add(that.getHash).add(that.getParam).add(that.setModal).add(that.http);

    // wrap functions?
    if (that.window.isNested) {

        // wrap!
        var wrap = function (fn, scope, getArray) {
            return function () {
                // get arguments
                var tmp = $.makeArray(arguments), i = 0, $l = tmp.length;
                var args = getArray($l);
                // process arguments
                for (; i < $l; i++) {
                    // clone object except for DOM objects
                    if (!ox.util.isDOMObject(tmp[i])) {
                        args[i] = clone(tmp[i]);
                    }
                }
                // call
                return fn.apply(scope, args);
            };
        };
        var recurse = function (here, there, getArray) {
            // is object?
            if ($.isPlainObject(here) && there !== undefined) {
                // loop over properties
                for (var id in here) {
                    // cannot check there[id] via jQuery's isFunction()!
                    if (!local.has(here[id]) && $.isFunction(here[id]) && there[id] !== undefined) {
                        // replace function with wrapper
                        here[id] = wrap(there[id], there, getArray);
                    } else {
                        recurse(here[id], there[id], getArray);
                    }
                }
            }
        };
        var ref = that.window.core.ox;
        recurse(that, ref.api, ref.util.getArray);
    } 
    
    // done. return
    return that;
    
}());

// API test

/**
 * Exemplary replacement of window factory
 * @name ox.api.window.morph
 * @function
 * @example
 * ox.api.window.morph();
 */
ox.api.window.morph = function () {
    var $ = jQuery;
    var count = 0;
    var open = [];
    var addToList = function (guid) {
        var i = 0, $l = open.length;
        for (; i < $l; i++) {
            ox.api.window.get(open[i]).container.animate({ marginLeft: "-=100px" }, 0);
        }
        open.push(guid);
    };
    var removeFromList = function () {
        open.pop();
        var i = 0, $l = open.length;
        for (; i < $l; i++) {
            ox.api.window.get(open[i]).container.animate({ marginLeft: "+=100px" }, 0);
        }
    };
    ox.api.window.setFactory(
        function (opt) {
            // inc
            count += 2;
            // locals
            var iframe, lightbox, container;
            // create iframe
            iframe = $("<iframe/>", { src: urlify(opt.url), frameborder: "no" }).css({
                position: "absolute",
                border: "0px none",
                width: "100%",
                height: "100%"
            }).one("load", function (e) { // bind might fail, if success handler work with document.write (endless loop in IE)
                // set guid
                var win = this.contentWindow;
                win.guid = opt.guid;
                // register new window object
                ox.api.window.register(opt.guid, win, { lightbox: lightbox, container: container });
                // remove spinner
                lightbox.removeClass("busy-spinner-black");
                // show container
                container.css("visibility", "visible");
                // add to list
                addToList(opt.guid);
                // continue
                opt.success(win, opt.guid);
            });
            // create lightbox
            lightbox = $("<div/>").addClass("busy-spinner-black").css({
                position: "absolute", 
                top: "0px", 
                right: "0px",
                bottom: "0px",
                left: "0px",
                zIndex: 19 + count,
                backgroundColor: "black", 
                opacity: 0.4
            }).bind("click", function (e) {
                // close
                opt.closer(opt);
            }).appendTo(document.body);
            // create container
            var w = opt.options.width;
            container = $("<div/>").css({
                position: "absolute", 
                top: "0px", 
                bottom: "0px", 
                zIndex: 20 + count,
                left: "50%", 
                marginLeft: (-Math.floor(w / 2)) + "px", 
                width: w + "px",
                border: "1px solid #555", 
                borderWidth: "0px 1px 0px 1px", 
                backgroundColor: "white", 
                MozBoxShadow: "0px 5px 25px 5px #000",
                webkitBoxShadow: "0px 5px 25px 5px #000",
                visibility: "hidden"
            }).append(iframe).appendTo(document.body);
        },
        function (obj) {
            // close
            obj.container.remove();
            obj.container = null;
            obj.lightbox.remove();
            obj.lightbox = null;
            // dec
            count -= 2;
            // remove
            removeFromList();
        }
    );
};

