
var js = (function () {
	
	var pub = { };
	var priv = { };
	/** const */ var objectName = 'mayaFish';
	
	pub.js = pub[objectName] = pub;
	pub.conf = mayaFishConf;
	priv.conf = { '_documentLoaded': false };
	
	/** const */ pub.consts = mayaFishConf.consts;
	priv._translations = mayaFishConf.translations;
	
	pub.log = function () { };
	pub.debug = function (info) {
		try {
			if (typeof console != 'undefined') {
				if ('debug' in console) {
					return console.debug.apply(console, arguments);
				} else if ('log' in console) {
					return console.log.apply(console, arguments);
				}
			}
		} catch (e) { }
	};
	pub.domDir = function () { };
	pub.assert = function () { };
	
	String.prototype.binary = false;
	
	pub.$id = function (id, silent) {
		var el = document.getElementById(id);
		pub.assert(!!silent || el != null, "Undefined element; id:" + id, true);
		return el;
	};
	pub.$name = function (n, silent) {
		var el = document.getElementByName(n);
		pub.assert(!!silent || el != null, "Undefined element; name:" + id, true);
		return el;
	};
	pub.$tags = function (tn, silent) { return document.getElementsByTagName(tn); };
	pub.$tag = function (tn, pos, silent) {
		var position = typeof pos == "undefined" ? 0 : parseInt(pos);
		var el = document.getElementsByTagName(tn)[position];
		pub.assert(!!silent || el != null, "Undefined element; tag:" + tn + "; position:" + position, true);
		return el;
	};
	
	pub.$select = function (q, from) {
		return dojo.query(q, from);
	};
	
	pub.modules = { };
	
	priv._defaultParams = function (pars, defpars) {
		if (pars == null) return defpars;
		pub.forEach(defpars, function (key, p) {
			if (pars[key] == null) pars[key] = p;
		});
		return pars;
	};
	
	pub.addCSS = function (file) {
		if (document.createStyleSheet) {
			document.createStyleSheet(file);
		} else {
			if (priv.conf._documentLoaded) {
				var newCSS = document.createElement('link');
				newCSS.setAttribute('rel', 'stylesheet');
				newCSS.setAttribute('type', 'text/css');
				newCSS.setAttribute('href', file);
				pub.$tag('head').appendChild(newCSS);
			} else {
				document.write(unescape('%3Cstyle type="text/css"%3E@import url("' +
					file + '");%3C/style%3E'));
			}
		}
	};
	
	pub.addJS = function (file, defer) {
		var d = (typeof defer != 'undefined' && !!defer);
		if (priv.conf._documentLoaded) {
			var newJS = document.createElement('script');
			newJS.setAttribute('type', 'text/javascript');
			newJS.setAttribute('src', file);
			pub.$tag('head').appendChild(newJS);
		} else {
			document.write(unescape('%3Cscript type="text/javascript" src="' +
				file + (d ? ' defer="defer"' : '') + '"%3E%3C/script%3E'));
		}
	};
	
	pub.addSlashes = function (str, quoteStyle) {
		if (typeof quoteStyle == "undefined")
			quoteStyle = pub.slashes.QUOTE_BOTH;
		str = str.replace(/\\/g, '\\\\');
		if (quoteStyle & pub.slashes.QUOTE_SINGLE)
			str = str.replace(/\'/g, '\\\'');
		if (quoteStyle & pub.slashes.QUOTE_DOUBLE)
			str = str.replace(/\"/g, '\\"');
		str = str.replace(/\0/g, '\\0');
		str = str.replace(/\n/g, '\\n');
		return str;
	};
	
	pub.stripSlashes = function (str, quoteStyle) {
		if (typeof quoteStyle == "undefined")
			quoteStyle = pub.slashes.QUOTE_BOTH;
		if (quoteStyle & pub.slashes.QUOTE_SINGLE)
			str = str.replace(/\\'/g, '\'');
		if (quoteStyle & pub.slashes.QUOTE_DOUBLE)
			str = str.replace(/\\"/g, '"');
		str = str.replace(/\\0/g, '\0');
		str = str.replace(/\\n/g, "\n");
		str = str.replace(/\\\\/g, '\\');
		return str;
	};
	
	pub.slashes = {
		'add': pub.addSlashes,
		'escape': pub.addSlashes,
		'strip': pub.stripSlashes,
		'remove': pub.stripSlashes
	};
	
	pub.slashes.QUOTE_NONE = 1;
	pub.slashes.QUOTE_SINGLE = 2;
	pub.slashes.QUOTE_DOUBLE = 4;
	pub.slashes.QUOTE_BOTH =
		pub.slashes.QUOTE_SINGLE |
		pub.slashes.QUOTE_DOUBLE;
	
	pub.escape = {
		'chars': function (str, chrs) {
			if (!chrs) chrs = "'\"";
			return ("" + str).replace(/\\/g, "\\\\").replace(new RegExp("([" + chrs + "])", "g"), "\\$1");
		},
		'nonPrintable': function (str) {
			var u = function (code) {
				var r = code.toString(16);
				while (r.length < 4) r = '0' + r;
				return r;
			};
			var r = '';
			for (var i = 0; i < str.length; ++i) {
				if (str.charCodeAt(i) < 32 || str.charCodeAt(i) > 126) {
					r += '\\u' + u(str.charCodeAt(i));
				} else {
					r += str[i];
				}
			}
			return r;
		}
	};
	
	pub.tryUntil = function (func, expr, tryOnExcept, wait) {
		if (!wait) wait = 100;
		try {
			if (expr()) func();
			else setTimeout(function() {
				pub.tryUntil(func, expr, tryOnExcept, wait + 10);
			}, wait + 10);
		} catch (e) {
			pub.debug('#1: ' + e.message);
			pub.debug(e);
			if (tryOnExcept) setTimeout(function() {
				pub.tryUntil(func, expr, tryOnExcept, wait + 10);
			}, wait + 10);
		}
	};
	
	priv.windowSize = null;
	pub.windowSize = function (calculate) {
		if (!calculate) {
			var Width = 0, Height = 0, Top = 0, Left = 0;
			if (typeof window.innerWidth  == 'number') {
				Width = window.innerWidth;
				Height = window.innerHeight;
			} else if (document.documentElement && (document.documentElement.clientWidth ||
					document.documentElement.clientHeight)) {
				Width = document.documentElement.clientWidth;
				Height = document.documentElement.clientHeight;
			} else if (document.body && (document.body.clientWidth || document.body.clientHeight)) {
				Width = document.body.clientWidth;
				Height = document.body.clientHeight;
			}
			if (typeof window.pageYOffset  == 'number') {
				Top = window.pageYOffset;
				Left = window.pageXOffset;
			} else if (document.body && (document.body.scrollLeft || document.body.scrollTop)) {
				Top = document.body.scrollTop;
				Left = document.body.scrollLeft;
			} else if (document.documentElement && (document.documentElement.scrollLeft ||
					document.documentElement.scrollTop)) {
				Top = document.documentElement.scrollTop;
				Left = document.documentElement.scrollLeft;
			}
			priv.windowSize = {
				'top': Top,
				'left': Left,
				'width': Width,
				'height': Height
			};
		};
		return priv.windowSize;
	};
	pub.windowSize(false);
	
	pub.getAbsolutePosition = function (htmlo) {
		var top = 0, left = 0;
		if (htmlo.offsetParent) {
			var op = pub.getAbsolutePosition(htmlo.offsetParent);
			top = op.top;
			left = op.left;
		}
		return {
			'top': top + htmlo.offsetTop,
			'left': left + htmlo.offsetLeft,
			'width': htmlo.offsetWidth,
			'height': htmlo.offsetHeight
		};
	};
	
	pub.base64 = {
		'_keyStr': "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
		'encode': function (input) {
			var output = "";
			var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
			var i = 0;
			input = pub.base64._utf8_encode(input);
			while (i < input.length) {
				chr1 = input.charCodeAt(i++);
				chr2 = input.charCodeAt(i++);
				chr3 = input.charCodeAt(i++);
				enc1 = chr1 >> 2;
				enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
				enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
				enc4 = chr3 & 63;
				if (isNaN(chr2)) {
					enc3 = enc4 = 64;
				} else if (isNaN(chr3)) {
					enc4 = 64;
				}
				output = output +
					this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
					this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
			}
			return output;
		},
		'decode': function (input) {
			var output = "";
			var chr1, chr2, chr3;
			var enc1, enc2, enc3, enc4;
			var i = 0;
			input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
			while (i < input.length) {
				enc1 = this._keyStr.indexOf(input.charAt(i++));
				enc2 = this._keyStr.indexOf(input.charAt(i++));
				enc3 = this._keyStr.indexOf(input.charAt(i++));
				enc4 = this._keyStr.indexOf(input.charAt(i++));
				chr1 = (enc1 << 2) | (enc2 >> 4);
				chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
				chr3 = ((enc3 & 3) << 6) | enc4;
				output = output + String.fromCharCode(chr1);
				if (enc3 != 64) {
					output = output + String.fromCharCode(chr2);
				}
				if (enc4 != 64) {
					output = output + String.fromCharCode(chr3);
				}
			}
			output = pub.base64._utf8_decode(output);
			return output;
		},
		'_utf8_encode': function (string) {
			string = string.replace(/\r\n/g,"\n");
			var utftext = "";
			for (var n = 0; n < string.length; n++) {
				var c = string.charCodeAt(n);
				if (c < 128) {
					utftext += String.fromCharCode(c);
				} else if((c > 127) && (c < 2048)) {
					utftext += String.fromCharCode((c >> 6) | 192);
					utftext += String.fromCharCode((c & 63) | 128);
				} else {
					utftext += String.fromCharCode((c >> 12) | 224);
					utftext += String.fromCharCode(((c >> 6) & 63) | 128);
					utftext += String.fromCharCode((c & 63) | 128);
				}
			}
			return utftext;
		},
		'_utf8_decode': function (utftext) {
			var string = "";
			var i = 0;
			var c = c1 = c2 = 0;
			while ( i < utftext.length ) {
				c = utftext.charCodeAt(i);
				if (c < 128) {
					string += String.fromCharCode(c);
					i++;
				} else if((c > 191) && (c < 224)) {
					c2 = utftext.charCodeAt(i+1);
					string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
					i += 2;
				} else {
					c2 = utftext.charCodeAt(i+1);
					c3 = utftext.charCodeAt(i+2);
					string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
					i += 3;
				}
			}
			return string;
		}
	};
	
	pub.php = {
		'serialize': function (mixed_value) {
			var _getType = function (inp) {
				var type = typeof inp, match;
				var key;
				if (type == 'object' && !inp) return 'null';
				if (type == "object") {
					if (!inp.constructor) return 'object';
					var cons = inp.constructor.toString();
					match = cons.match(/(\w+)\(/);
					if (match) cons = match[1].toLowerCase();
					var types = ["boolean", "number", "string", "array"];
					for (key in types) {
						if (cons == types[key]) {
							type = types[key];
							break;
						}
					}
				}
				return type;
			};
			var type = _getType(mixed_value);
			var val = '';
			
		    switch (type) {
		        case "function":
		            val = ""; 
		            break;
		        case "boolean":
		            val = "b:" + (mixed_value ? "1" : "0");
		            break;
		        case "number":
		            val = (Math.round(mixed_value) == mixed_value ? "i" : "d") + ":" + mixed_value;
		            break;
		        case "string":
		            val = "s:" + encodeURIComponent(mixed_value).replace(/%../g, 'x').length + ":\"" + mixed_value + "\"";
		            break;
		        case "array":
		        case "object":
		            val = "a";
		            var count = 0;
		            var vals = "";
		            for (var key in mixed_value) {
		                var ktype = _getType(mixed_value[key]);
		                if (ktype == "function") continue;
		                var okey = (key.match(/^[0-9]+$/) ? parseInt(key) : key);
		                vals += this.serialize(okey) +
		                        this.serialize(mixed_value[key]);
		                count++;
		            }
		            val += ":" + count + ":{" + vals + "}";
		            break;
		        case "undefined":
		        default:
		            val = "N";
		            break;
		    }
		    if (type != "object" && type != "array") {
		        val += ";";
		    }
		    return val;
		},
		'unserialize': function (data) {
		    var error = function (type, msg, filename, line) {
				throw new this.window[type](msg, filename, line);
			};
		    var read_until = function (data, offset, stopchr) {
		        var buf = [];
		        var chr = data.slice(offset, offset + 1);
		        var i = 2;
		        while (chr != stopchr) {
		            if ((i+offset) > data.length) {
		                error('Error', 'Invalid');
		            }
		            buf.push(chr);
		            chr = data.slice(offset + (i - 1),offset + i);
		            i += 1;
		        }
		        return [buf.length, buf.join('')];
		    };
		    var read_chrs = function (data, offset, length) {
		        var buf;
		
		        buf = [];
		        for (var i = 0;i < length;i++){
		            var chr = data.slice(offset + (i - 1),offset + i);
		            buf.push(chr);
		        }
		        return [buf.length, buf.join('')];
		    };
		    var _unserialize = function (data, offset) {
		        var readdata;
		        var readData;
		        var chrs = 0;
		        var ccount;
		        var stringlength;
		        var keyandchrs;
		        var keys;
		
		        if (!offset) {offset = 0;}
		        var dtype = (data.slice(offset, offset + 1)).toLowerCase();
		
		        var dataoffset = offset + 2;
		        var typeconvert = new Function('x', 'return x');
		
		        switch (dtype) {
		            case 'i':
		                typeconvert = function (x) {return parseInt(x, 10);};
		                readData = read_until(data, dataoffset, ';');
		                chrs = readData[0];
		                readdata = readData[1];
		                dataoffset += chrs + 1;
		            break;
		            case 'b':
		                typeconvert = function (x) {return parseInt(x, 10) !== 0;};
		                readData = read_until(data, dataoffset, ';');
		                chrs = readData[0];
		                readdata = readData[1];
		                dataoffset += chrs + 1;
		            break;
		            case 'd':
		                typeconvert = function (x) {return parseFloat(x);};
		                readData = read_until(data, dataoffset, ';');
		                chrs = readData[0];
		                readdata = readData[1];
		                dataoffset += chrs + 1;
		            break;
		            case 'n':
		                readdata = null;
		            break;
		            case 's':
		                ccount = read_until(data, dataoffset, ':');
		                chrs = ccount[0];
		                stringlength = ccount[1];
		                dataoffset += chrs + 2;
		
		                readData = read_chrs(data, dataoffset+1, parseInt(stringlength, 10));
		                chrs = readData[0];
		                readdata = readData[1];
		                dataoffset += chrs + 2;
		                if (chrs != parseInt(stringlength, 10) && chrs != readdata.length){
		                    error('SyntaxError', 'String length mismatch');
		                }
		            break;
	            case 'a':
		                readdata = {};
		
		                keyandchrs = read_until(data, dataoffset, ':');
		                chrs = keyandchrs[0];
		                keys = keyandchrs[1];
		                dataoffset += chrs + 2;
		
		                for (var i = 0; i < parseInt(keys, 10); i++){
		                    var kprops = _unserialize(data, dataoffset);
		                    var kchrs = kprops[1];
		                    var key = kprops[2];
		                    dataoffset += kchrs;
		
		                    var vprops = _unserialize(data, dataoffset);
		                    var vchrs = vprops[1];
		                    var value = vprops[2];
		                    dataoffset += vchrs;
		
		                    readdata[key] = value;
		                }
		
		                dataoffset += 1;
		            break;
		            default:
		                error('SyntaxError', 'Unknown / Unhandled data type(s): ' + dtype);
		            break;
		        }
		        return [dtype, dataoffset - offset, typeconvert(readdata)];
		    };
		    
		    return _unserialize((data+''), 0)[2];
		}
	};
	
	pub.toJson = function (par) {
		var ret = '';
		if ((typeof par == 'undefined') || (par == null)) {
			ret += 'null';
		} else if (par instanceof Array) {
			
			ret += '[';
			var first = true;
			for (var i = 0; i < par.length; i++) {
				if (typeof par[i] != 'undefined') {
					if (first) first = false;
					else ret += ',';
					ret += pub.toJson(par[i]);
				}
			}
			ret += ']';
			
		} else if (typeof par == 'object') {
			
			if (!par.____jsonEncoded) {
				par.____jsonEncoded = true;
				ret += '{';
				var first = true;
				for (var i in par) {
					if (i != '____jsonEncoded') {
						if (first) first = false;
						else ret += ',';
						ret += '"' + pub.slashes.add(i, pub.slashes.QUOTE_DOUBLE) + '":';
						ret += pub.toJson(par[i]);
					}
				}
				ret += '}';
				delete par.____jsonEncoded;
			} else {
				ret += 'null';
			}
			
		} else if (typeof par == 'number') {
			if (isNaN(par)) ret += 'null';
			else ret += par;
		} else if (typeof par == 'boolean') {
			ret += par ? 'true' : 'false';
		} else {
			if (typeof par == 'string' && par.binary)
				ret += '"' + pub.slashes.add('' + pub.escape.nonPrintable(par), pub.slashes.QUOTE_DOUBLE) + '"';
			else
				ret += '"' + pub.slashes.add('' + par, pub.slashes.QUOTE_DOUBLE) + '"';
		}
		return ret;
	};
	
	pub.defineGetter = function (obj, k, getter) {
		if (!!obj.__defineGetter__) {
			obj.__defineGetter__(k, getter);
		} else if (!!Object.defineProperty) {
			Object.defineProperty(obj, k, { 'getter': getter });
		} else {
			throw new Exception('Getters / setters not implemented!');
		}
	};
	
	pub.defineSetter = function (obj, k, setter) {
		if (!!obj.__defineSetter__) {
			obj.__defineSetter__(k, setter);
		} else if (!!Object.defineProperty) {
			Object.defineProperty(obj, k, { 'setter': setter });
		} else {
			throw new Exception('Getters / setters not implemented!');
		}
	};
	
	priv.safeGround = {
		'$': null, 'all': null, 'top': null, 'self': null, 'event': null,
		'parent': null, 'window': null, 'frames': null, 'opener': null,
		'content': null, 'children': null, 'document': null, 'location': null,
		'navigator': null, 'firstChild': null, 'frameElement': null,
		'localStorage': null, 'globalStorage': null, 'sessionStorage': null
	};
	priv.safeGround[objectName] = null;
	
	if (typeof JSON == 'undefined') {
		window.JSON = {
			'parse': function (text) {
				if (/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
					replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
					replace(/(?:^|:|,)(?:\s*\[)+/g, ''))
				) try {
					return (new Function('with(this) return (' + text + ')')).call(priv.safeGround);
				} catch (e) {
					pub.debug('#2: ' + e.message);
					pub.debug(e);
				}
				return null;
			},
			'stringify': pub.toJson
		};
	}
	
	pub.clone = function (par) {
		var ret;
		if ((typeof par == 'undefined') || (par == null)) {
			ret = null;
		} else if (par instanceof Array) {
			
			ret = new Array();
			for (var i = 0; i < par.length; i++) {
				ret.push(pub.clone(par[i]));
			}
			
		} else if (typeof par == 'object') {
			
			if (!par.____cloned) {
				par.____cloned = true;
				ret = new Object();
				for (var i in par) {
					if (i != '____cloned') {
						ret[i] = pub.clone(par[i]);
					}
				}
				delete par.____cloned;
			} else {
				ret = null;
			}
			
		} else {
			ret = par;
		}
		return ret;
	};

	priv._breakForEach = false;
	pub.breakForEach = function () {
		priv._breakForEach = true;
	};
	/** const */ pub.BREAK = false;
	pub.forEach = function (arr, func/*(key, item)*/, scope) {
		var s = scope || this;
		if (arr instanceof Array) {
			for (var i = 0; i < arr.length; ++i) {
				if ((func.call(s, i, arr[i]) === pub.BREAK) || priv._breakForEach) {
					priv._breakForEach = false;
					return false;
				}
			}
		} else if (!!arr.reset && !!arr.next && 'index' in arr) {
			var idx, get;
			if (typeof arr.index == 'function') idx = function () { return arr.index(); };
			else idx = function () { return arr.index; };
			if (typeof arr.get == 'function') get = function (i) { return arr.get(i); };
			else get = function (i) { return arr[i]; };
			for (var i = arr.reset(); i = idx(); arr.next()) {
				if ((func.call(s, i, get(i)) === pub.BREAK) || priv._breakForEach) {
					priv._breakForEach = false;
					return false;
				}
			}
		} else if (typeof arr == 'object') {
			for (var i in arr) {
				if ((func.call(s, i, arr[i]) === pub.BREAK) || priv._breakForEach) {
					priv._breakForEach = false;
					return false;
				}
			}
		} else {
			pub.assert(false, objectName + '.forEach: first parameter not array, nor object');
			return false;
		}
		return true;
	};
	
	try {
		var got = function () {
			pub.debug('#3: Access error');
			pub.debug(this);
			return null;
		};
		if (!!priv.safeGround.__defineGetter__) {
			pub.forEach(priv.safeGround, function (k, v) {
				if (!!priv.safeGround.__lookupGetter__(k))
					priv.safeGround.__defineGetter__(k, got);
			});
		}/* else if (!!Object.defineProperty) {
			pub.forEach(priv.safeGround, function (k, v) {
				Object.defineProperty(priv.safeGround, k, {
					'getter': got
				});
			});
		}*/
	} catch(e) {
		pub.debug('#4: ' + e.message);
		pub.debug(e);
	}
	
	pub.inArray = function (arr, elem) {
		var found = false;
		pub.forEach(arr, function (key, item) {
			if (item == elem) {
				found = true;
				return pub.BREAK;
			}
		});
		return found;
	};
	
	pub.arrayInsert = function (arr, elem, beta /** beta(elem) = false|"after"|"before" */) {
		var ret = new Array();
		if (arr instanceof Array) {
			for (var i = 0; i < arr.length; i++) {
				var b = beta(arr[i]);
				if (b == "before") ret.push(elem);
				ret.push(pub.clone(arr[i]));
				if (b == "after") ret.push(elem);
			}
		}
		return ret;
	};
	
	pub.arrayRemove = function (arr, beta /** beta(elem) = true|false */) {
		var ret = new Array();
		var el = null;
		if (arr instanceof Array) {
			for (var i = 0; i < arr.length; i++) {
				if (beta(arr[i]) && (el == null))
					el = pub.clone(arr[i]);
				else
					ret.push(pub.clone(arr[i]));
			}
		}
		return {
			'array': ret,
			'elem': el
		};
	};
	
	pub.min = function (m1, m2) { return (m1 < m2) ? m1 : m2; };
	pub.max = function (m1, m2) { return (m1 > m2) ? m1 : m2; };
	
	pub.color = {
		/** r, g, b in [0.0, 1.0] */
		'Rgb': function (r, g, b) { this.r = r; this.g = g; this.b = b; },
		/** c, m, y in [0.0, 1.0] */
		'Cmy': function (c, m, y) { this.c = c; this.m = m; this.y = y; },
		/** h in [0.0, 360.0]; s, v in [0.0, 1.0] */
		'Hsv': function (h, s, v) { this.h = h; this.s = s; this.v = v; },
		/** h in [0.0, 360.0]; s, l in [0.0, 1.0] */
		'Hsl': function (h, s, l) { this.h = h; this.s = s; this.l = l; },
		'rgb2cmy': function (c1 /** r, g, b */) {
			return new pub.color.Cmy(1 - c1.r, 1 - c1.g, 1 - c1.b);
		},
		'cmy2rgb': function (c1 /** c, m, y */) {
			return new pub.color.Rgb(1 - c1.c, 1 - c1.m, 1 - c1.y);
		},
		'rgb2hsv': function (c1 /** r, g, b */) {
			var themin = pub.min(c1.r, pub.min(c1.g, c1.b));
			var themax = pub.max(c1.r, pub.max(c1.g, c1.b));
			var delta = themax - themin;
			var c2 = new pub.color.Hsv(0, 0, themax);
			if (themax > 0) c2.s = delta / themax;
			if (delta > 0) {
				if (themax == c1.r && themax != c1.g)
					c2.h += (c1.g - c1.b) / delta;
				if (themax == c1.g && themax != c1.b)
					c2.h += (2 + (c1.b - c1.r) / delta);
				if (themax == c1.b && themax != c1.r)
					c2.h += (4 + (c1.r - c1.g) / delta);
				c2.h *= 60;
			}
			return c2;
		},
		'hsv2rgb': function (c1 /** h, s, v */){
			var c2 = new pub.color.Rgb(0, 0, 0);
			var sat = new pub.color.Rgb(0, 0, 0);
			
			while (c1.h < 0) c1.h += 360;
			while (c1.h > 360) c1.h -= 360;
			
			if (c1.h < 120) {
				sat.r = (120 - c1.h) / 60.0;
				sat.g = c1.h / 60.0;
				sat.b = 0;
			} else if (c1.h < 240) {
				sat.r = 0;
				sat.g = (240 - c1.h) / 60.0;
				sat.b = (c1.h - 120) / 60.0;
			} else {
				sat.r = (c1.h - 240) / 60.0;
				sat.g = 0;
				sat.b = (360 - c1.h) / 60.0;
			}
			sat.r = pub.min(sat.r, 1);
			sat.g = pub.min(sat.g, 1);
			sat.b = pub.min(sat.b, 1);
			
			c2.r = (1 - c1.s + c1.s * sat.r) * c1.v;
			c2.g = (1 - c1.s + c1.s * sat.g) * c1.v;
			c2.b = (1 - c1.s + c1.s * sat.b) * c1.v;
			
			return c2;
		},
		'rgb2hsl': function (c1 /** r, g, b */) {
			var themin = pub.min(c1.r, pub.min(c1.g, c1.b));
			var themax = pub.max(c1.r, pub.max(c1.g, c1.b));
			var delta = themax - themin;
			var c2 = new pub.color.Hsl(0, 0, (themin + themax) / 2);
			if (c2.l > 0 && c2.l < 1)
				c2.s = delta / (c2.l < 0.5 ? (2*c2.l) : (2-2*c2.l));
			if (delta > 0) {
				if (themax == c1.r && themax != c1.g)
					c2.h += (c1.g - c1.b) / delta;
				if (themax == c1.g && themax != c1.b)
					c2.h += (2 + (c1.b - c1.r) / delta);
				if (themax == c1.b && themax != c1.r)
					c2.h += (4 + (c1.r - c1.g) / delta);
				c2.h *= 60;
			}
			return c2;
		},
		'hsl2rgb': function (c1 /** h, s, l */) {
			var c2 = new pub.color.Rgb(0, 0, 0);
			var sat = new pub.color.Rgb(0, 0, 0);
			var ctmp = new pub.color.Rgb(0, 0, 0);
			
			while (c1.h < 0) c1.h += 360;
			while (c1.h > 360) c1.h -= 360;
			
			if (c1.h < 120) {
				sat.r = (120 - c1.h) / 60.0;
				sat.g = c1.h / 60.0;
				sat.b = 0;
			} else if (c1.h < 240) {
				sat.r = 0;
				sat.g = (240 - c1.h) / 60.0;
				sat.b = (c1.h - 120) / 60.0;
			} else {
				sat.r = (c1.h - 240) / 60.0;
				sat.g = 0;
				sat.b = (360 - c1.h) / 60.0;
			}
			sat.r = pub.min(sat.r, 1);
			sat.g = pub.min(sat.g, 1);
			sat.b = pub.min(sat.b, 1);
			
			ctmp.r = 2 * c1.s * sat.r + (1 - c1.s);
			ctmp.g = 2 * c1.s * sat.g + (1 - c1.s);
			ctmp.b = 2 * c1.s * sat.b + (1 - c1.s);
			
			if (c1.l < 0.5) {
				c2.r = c1.l * ctmp.r;
				c2.g = c1.l * ctmp.g;
				c2.b = c1.l * ctmp.b;
			} else {
				c2.r = (1 - c1.l) * ctmp.r + 2 * c1.l - 1;
				c2.g = (1 - c1.l) * ctmp.g + 2 * c1.l - 1;
				c2.b = (1 - c1.l) * ctmp.b + 2 * c1.l - 1;
			}
			
			return c2;
		},
		'parse': function (rgb, str) {
			var m;
			if (m = str.match(/#([0-9a-fA-F]{1,2})([0-9a-fA-F]{1,2})([0-9a-fA-F]{1,2})/)) {
				rgb.r = parseInt(m[1], 16) / 255;
				rgb.g = parseInt(m[2], 16) / 255;
				rgb.b = parseInt(m[3], 16) / 255;
			} else if (m = str.match(/rgba?\(\s*([0-9]{1,3})%\s*,\s*([0-9]{1,3})%\s*,\s*?([0-9]{1,3})%/)) {
				rgb.r = parseInt(m[1], 10) / 100;
				rgb.g = parseInt(m[2], 10) / 100;
				rgb.b = parseInt(m[3], 10) / 100;
			} else if (m = str.match(/rgba?\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*?([0-9]{1,3})/)) {
				rgb.r = parseInt(m[1], 10) / 255;
				rgb.g = parseInt(m[2], 10) / 255;
				rgb.b = parseInt(m[3], 10) / 255;
			} else if (m = str.match(/rgba?\(\s*([0-9\.]+)\s*,\s*([0-9\.]+)\s*,\s*?([0-9\.]+)/)) {
				rgb.r = parseFloat(m[1]);
				rgb.g = parseFloat(m[2]);
				rgb.b = parseFloat(m[3]);
			}
		}
	};
	
	pub.color.Rgb.prototype.parse = function (str) { return pub.color.parse(this, str); };
	pub.color.Rgb.prototype.toCmy = function () { return pub.color.rgb2cmy(this); };
	pub.color.Rgb.prototype.toHsv = function () { return pub.color.rgb2hsv(this); };
	pub.color.Rgb.prototype.toHsl = function () { return pub.color.rgb2hsl(this); };
	pub.color.Cmy.prototype.toRgb = function () { return pub.color.cmy2rgb(this); };
	pub.color.Hsv.prototype.toRgb = function () { return pub.color.hsv2rgb(this); };
	pub.color.Hsl.prototype.toRgb = function () { return pub.color.hsl2rgb(this); };
	pub.color.Rgb.prototype.toString = function () {
		var rs = parseInt(this.r * 255).toString(16);
		var gs = parseInt(this.g * 255).toString(16);
		var bs = parseInt(this.b * 255).toString(16);
		if (rs.length == 1) rs = '0' + rs;
		if (gs.length == 1) gs = '0' + gs;
		if (bs.length == 1) bs = '0' + bs;
		return '#' + rs + gs + bs;
	};
	pub.color.Cmy.prototype.toString =
	pub.color.Hsv.prototype.toString =
	pub.color.Hsl.prototype.toString = function () {
		return this.toRgb().toString();
	};
	
	priv._makeParams = function (params) {
		if (typeof params == 'object') {
			var paramStr = '';
			for (par in params) {
				if (paramStr != '') paramStr += '&';
				if (params[par] instanceof Array) {
					for (var p2 = 0; p2 < params[par].length; ++p2) {
						if (p2 > 0) paramStr += '&';
						paramStr += '' + par + '[' + p2 + ']=' + params[par];
					}
				} else if (typeof params[par] == 'object') {
					var first = true;
					for (p2 in params[par]) {
						if (first) first = false;
						else paramStr += '&';
						paramStr += '' + par + '[' + p2 + ']=' + params[par];
					}
				} else {
					paramStr += '' + par + '=' + params[par];
				}
			}
			return paramStr;
		} else {
			return '' + params;
		}
	};
	
	priv.loadState = function (loaded, total) {
		var f = function (p, f) { return p ? '' + p + f : '??'; };
		this.total = total ? total : null;
		this.loaded = loaded ? loaded : null;
		this.ratio = total ? (loaded / total) : null;
		this.toString = function () {
			if (this.total == null && loaded == null) return '???';
			return '' + (f(Math.round(this.ratio * 1000) / 10, '%')) +
				' (' + f(loaded, ' B') + ' / ' + f(total, ' B') + ')';
		};
	};
	priv.httpRequest = function (params, xhr) {
		var p = params;
		var x = xhr;
		
		this.contentType = null;
		this.toString = function () {
			return 'HTTP request to url("' + p.url + '")';
		};
		this.getXhr = function () { return x; };
		this.cancel = function () {
			if (!!x.abort) x.abort();
			x.onreadystatechange = function () {};
			delete x;
			if (!!p.onFinish) try {
				p.onFinish.call(this);
			} catch (e) {
				pub.debug('#5: ' + e.message);
				pub.debug(e);
			}
		};
	};
	pub.httpRequest = function (params /** url, [method], [params], [randomize], [parser], [async],
		[contentType], [charset], [headers], [onSuccess], [onFail], [onFinish], [onProgress] */) {
		if (params.url) {
			var _xhr = new pub.types.Xhr();
			if (!_xhr.valid) throw new Exception('xmlHttpRequest not implemented');
			var returnObject = new priv.httpRequest(params, _xhr.xhr);
			if (params.method) {
				params.method = params.method.toUpperCase();
				if (params.method != 'GET' && params.method != 'POST')
					params.method = 'GET';
			} else
				params.method = 'GET';
			
			params.params = ('params' in params) ? priv._makeParams(params.params) : '';
			
			if (typeof params.randomize == 'undefined' || params.randomize) {
				var randStr = '__randomize=' + parseInt(Math.random() * 100000000000);
				if (params.method == 'GET') {
					if (params.params) params.params += '&' + randStr;
					else params.params = randStr;
				} else {
					if (params.url.indexOf('?') >= 0)
						params.url += '&' + randStr;
					else
						params.url += '?' + randStr;
				}
			}
			
			if (params.method == 'GET' && params.params) {
				if (params.url.indexOf('?') >= 0)
					params.url += '&' + params.params;
				else
					params.url += '?' + params.params;
			}
			
			if (!pub.httpRequestParser[params.parser])
				params.parser = 'auto';
			
			if (!params.onProgress && !!params.onFinish && !!params.onFinish.setProgressState)
				params.onProgress = params.onFinish.setProgressState;
			
			if (!!params.onProgress) {
				_xhr.xhr.onprogress = function (evt) {
					if (evt.lengthComputable) {
						params.onProgress.call(returnObject,
							new priv.loadState(evt.loaded, evt.total));
					} else {
						var total, loaded;
						try {
							total = (typeof _xhr.xhr.getResponseHeader != 'undefined') ?
								_xhr.xhr.getResponseHeader('Content-Length') ||
								_xhr.xhr.getResponseHeader('Content-length') : null;
						} catch (e) {
							pub.debug('#6: ' + e.message);
							pub.debug(e);
							total = null;
						}
						try {
							loaded = ('' + _xhr.xhr.responseText).length;
						} catch (e) {
							pub.debug('#7: ' + e.message);
							pub.debug(e);
							loaded = null;
						}
						params.onProgress.call(returnObject,
							new priv.loadState(loaded, total));
					}
				};
			}
			
			_xhr.xhr.onreadystatechange = function () {
				if ((_xhr.xhr.readyState == 2 || _xhr.xhr.readyState == 3) && !!params.onProgress) {
					var total, loaded;
					try {
						total = (typeof _xhr.xhr.getResponseHeader != 'undefined') ?
							_xhr.xhr.getResponseHeader('Content-Length') ||
							_xhr.xhr.getResponseHeader('Content-length') : null;
					} catch (e) {
						pub.debug('#8: ' + e.message);
						pub.debug(e);
						total = null;
					}
					try {
						loaded = ('' + _xhr.xhr.responseText).length;
					} catch (e) {
						pub.debug('#9: ' + e.message);
						pub.debug(e);
						loaded = null;
					}
					params.onProgress.call(returnObject, new priv.loadState(loaded, total));
				} else if (_xhr.xhr.readyState == 4) {
					if (_xhr.xhr.getResponseHeader('X-mayafish-maintenance')) {
						pub.ui.alert('Site is under maintenance. We are sorry for the inconvenience.',
							null, null, null, pub.ui.icons.INFO);
					} else if (_xhr.xhr.status == 200) {
						if (!!params.onSuccess) {
							try {
								returnObject.contentType = null;
								returnObject.contentType = _xhr.xhr.getResponseHeader('Content-type') ||
									_xhr.xhr.getResponseHeader('Content-Type');
								params.onSuccess.call(returnObject,
									pub.httpRequestParser[params.parser](_xhr.xhr));
							} catch (e) {
								pub.debug('#10: ' + e.message);
								pub.debug(e);
							}
						}
					} else if (_xhr.xhr.status != 200 && !!params.onFail) {
						try {
							params.onFail.call(returnObject, _xhr.xhr.status);
						} catch (e) {
							pub.debug('#11: ' + e.message);
							pub.debug(e);
						}
					}
					if (!!params.onFinish)
						try {
							params.onFinish.call(returnObject);
						} catch (e) {
							pub.debug('#12: ' + e.message);
							pub.debug(e);
						}
				}
			};
			
			if (typeof params.async == 'undefined')
				params.async = true;
			else
				params.async = !!params.async;
			
			if (!params.charset) params.charset = 'UTF-8';
			_xhr.xhr.open(params.method, params.url, params.async);
			if (params.contentType) {
				if (params.contentType.search(/[;,] ?charset=[a-zA-Z0-8_-]+/i))
					_xhr.xhr.setRequestHeader('Content-Type', params.contentType);
				else if (0 > params.contentType.indexOf(";"))
					_xhr.xhr.setRequestHeader('Content-Type',
						params.contentType + '; charset=' + params.charset);
				else
					_xhr.xhr.setRequestHeader('Content-Type',
						params.contentType + ', charset=' + params.charset);
			} else if (params.method == 'POST')
				_xhr.xhr.setRequestHeader('Content-Type',
					'application/x-www-form-urlencoded; charset=' + params.charset);
			
			if ((params.headers instanceof Array) && (params.headers.length > 0))
				for (var i = 0; i < params.headers.length; ++i) {
					var h = ('' + params.headers[i]).split(':', 2);
					if (h[1]) h[1] = h[1].replace(/^ +/, '');
					_xhr.xhr.setRequestHeader(h[0], h[1]);
				}
			
			if (params.method == 'POST') {
				if (params.binary && _xhr.xhr.sendAsBinary)
					_xhr.xhr.sendAsBinary(params.params);
				else
					_xhr.xhr.send(params.params);
			} else 
				_xhr.xhr.send(null);
			
			return returnObject;
		} else {
			pub.assert(false, objectName + '.httpRequest: Missing url.');
		}
	};
	priv.parseAsXml = function (text) {
		var xml = new pub.types.XmlParser();
		if (xml.valid) {
			try {
				xml.xml.async = false;
				xml.xml.loadXML(text);
				return xml.xml;
			} catch (e) {
				pub.debug('#13: ' + e.message);
				pub.debug(e);
				return text;
			}
		} else {
			return text;
		}
	};
	pub.httpRequestParser = {
		'text': function (x) { return x.responseText; },
		'object': function (x) { return x; },
		'json': function (x) {
			try {
				if ((typeof JSON != 'undefined') && (!!JSON.parse))
					return JSON.parse(x.responseText);
				else
					return (new Function('with(this) return (' + x.responseText + ')')).call(priv.safeGround);
			} catch(e) {
				pub.debug('#14: ' + e.message);
				pub.debug(e);
				return x.responseText;
			}
		},
		'eval': function (x) {
			try {
				return eval('(' + x.responseText + ')');
			} catch(e) {
				pub.debug('#15: ' + e.message);
				pub.debug(e);
				return e;
			}
		},
		'func': function (x) {
			try {
				return (new Function(x.responseText)).call(window);
			} catch(e) {
				pub.debug('#16: ' + e.message);
				pub.debug(e);
				return e;
			}
		},
		'xml': function (x) {
			if (x.responseXML)
				return x.responseXML;
			else
				return priv.parseAsXml(x.responseText);
		},
		'none': function (x) {
			return null;
		},
		'auto': function (x) {
			var contentType = x.getResponseHeader('Content-Type') ||
				x.getResponseHeader('Content-type');
			contentType = contentType.replace(/;.*$/, '');
			contentType = contentType.replace(/\/.*\+/, '/');
			contentType = contentType.replace('/x-', '/');
			switch (contentType) {
				case 'text/xml':
				case 'application/xml':
					return pub.httpRequestParser.xml(x);
				break;
				case 'text/json':
				case 'application/json':
					return pub.httpRequestParser.json(x);
				break;
				case 'text/javascript':
				case 'application/javascript':
					return pub.httpRequestParser.func(x);
				break;
				case 'text/plain':
				default:
					return x.responseText;
				break;
			}
		}
	};
	
	priv.jsonRpc = { '_lastId': 0 };
	pub.jsonRpc = function (rpcParams /** [url|site], method, [params],
		[onSuccess], [onError], [onFail], [notify], [onFinish] */) {
		rpcParams = rpcParams || this;
		var success = null, rpcId = null;
		if (!!rpcParams.notify) {
			success = function () {}
		} else {
			rpcId = ++priv.jsonRpc._lastId;
			success = function (res) {
				if (res && res.error) {
					if (res.error.code > 0) {
						try {
							rpcParams.onError(res.error);
						} catch (e) {
							pub.ui.alert('Error: <b>' + res.error.code +
								'</b><br />Message: <b>' + res.error.message + '</b>',
								pub.translations('common', 'Error'), null, null, pub.ui.icons.ERROR);
							pub.debug('#17: ' + e.message);
							pub.debug(e);
						}
					} else
						pub.ui.alert('Error: <b>' + res.error.code +
							'</b><br />Message: <b>' + res.error.message + '</b>',
							pub.translations('common', 'Error'), null, null, pub.ui.icons.ERROR);
				} else if (res && res.id && (!rpcParams.notify) && (res.id != rpcId)) {
					pub.ui.alert('Id mismatch error while JSON-RPC #' + rpcId,
						pub.translations('common', 'Error'), null, null, pub.ui.icons.ERROR);
				} else if (res && ('result' in res)) {
					try {
						rpcParams.onSuccess(res.result);
					} catch (e) {
						if (!rpcParams.notify) {
							pub.ui.alert('Runtime error while JSON-RPC #' + rpcId + ':<br />' + e.message,
								pub.translations('common', 'Error'), null, null, pub.ui.icons.ERROR);
							pub.debug('#18: ' + e.message);
							pub.debug(e);
						}
					}
				} else {
					pub.ui.alert('Parse error while JSON-RPC #' + rpcId,
						pub.translations('common', 'Error'), null, null, pub.ui.icons.ERROR);
					pub.debug(res);
				}
			};
		}
		var request = pub.httpRequest({
			'url': rpcParams.url ?
				rpcParams.url : (
				rpcParams.site ?
				(rpcParams.site + '/jsonRpc') :
				pub.consts.SITE_URL + '/jsonRpc'),
			'method': 'post',
			'params': pub.toJson({
				'jsonrpc': '2.0',
				'method': rpcParams.method,
				'params': rpcParams.params ? rpcParams.params : null,
				'id': !!rpcParams.notify ? null : rpcId
			}),
			'async': rpcParams.async,
			'contentType': 'application/json; charset=UTF-8',
			'parser': !!rpcParams.notify ? 'none' : 'json',
			'onSuccess': success,
			'onFail': function (err) {
				if (!!rpcParams.onFail) try {
					rpcParams.onFail(err);
				} catch (e) {
					pub.debug('#19: ' + e.message); 
					pub.debug(e);
				} else {
					pub.ui.alert('Http error (' + err + ') while JSON-RPC #' + rpcId,
							pub.translations('common', 'Error'), null, null, pub.ui.icons.ERROR);
				}
			},
			'onFinish': ((rpcParams && !!rpcParams.onFinish) ? rpcParams.onFinish : null)
		});
		if (!!rpcParams.notify) {
			setTimeout(function () {
				request.cancel();
			}, 667);
		}
		return request;
	};
	
	priv.xmlRpc = {
		'_lastId': 0,
		'xmlEscape': function (str) {
			return str.replace('&', '&amp;').replace('"', '&quot;').
				replace("'", '&#039;').replace('<', '&lt;').replace('>', '&gt;');
		},
		'makeValue': function (par) {
			var ret = '';
			if ((typeof par == 'undefined') || (par == null)) {
				ret += '<null />';
			} else if (par instanceof Array) {
				
				ret += '<array><data>';
				for (var i = 0; i < par.length; i++) {
					if (typeof par[i] != 'undefined') {
						ret += '<value>' + priv.xmlRpc.makeValue(par[i]) + '</value>';
					}
				}
				ret += '</data></array>';
				
			} else if (typeof par == 'object') {
				
				if (!par.____xmlRpcEncoded) {
					par.____xmlRpcEncoded = true;
					ret += '<struct>';
					for (var i in par) {
						if (i != '____xmlRpcEncoded') {
							ret += '<member>';
							ret += '<name>' + priv.xmlRpc.xmlEscape(i) + '</name>';
							ret += '<value>' + priv.xmlRpc.makeValue(par[i]) + '</value>';
							ret += '</member>';
						}
					}
					ret += '</struct>';
					delete par.____xmlRpcEncoded;
				} else {
					ret += '<null />';
				}
				
			} else if (typeof par == 'number') {
				if (isNaN(par)) ret += '<null />';
				else if (parseInt(par) == par) ret += '<int>' + par + '</int>';
				else ret += '<double>' + par + '</double>';
			} else if (typeof par == 'boolean') {
				ret += '<boolean>' + (par ? '1' : '0') + '</boolean>';
			} else {
				if (typeof par == 'string' && par.binary)
					ret += '<base64>' + pub.base64.encode(par) + '</base64>';
				else ret += '<string>' + priv.xmlRpc.xmlEscape('' + par) + '</string>';
			}
			return ret;
		},
		'make': function (method, params) {
			var result = "<" + '?xml version="1.0" encoding="UTF-8"?' + ">\n";
			result += "<methodCall>";
			result += "<methodName>" + method + "</methodName>";
			result += "<params>";
			if (params == null) {
			} else if (params instanceof Array) {
				for (var i = 0; i < params.length; ++i) {
					result += "<param><value>";
					result += priv.xmlRpc.makeValue(params[i]);
					result += "</value></param>";
				}
			} else if (params instanceof Object) {
				for (var i in params) {
					result += "<param><name>" + i + "</name><value>";
					result += priv.xmlRpc.makeValue(params[i]);
					result += "</value></param>";
				}
			} else {
				result += "<param><value>" + method + "</value></param>";
			}
			result += "</params>";
			result += "</methodCall>";
			return result;
		},
		'getText': function (node) {
			if (node.innerText) return node.innerText;
			if (node.textContent) return node.textContent;
			return node.innerHTML;
		},
		'convertValue': function (domValue) {
			switch (domValue.firstChild.tagName) {
				case 'null':
					return null;
				case 'i4':
				case 'int':
				case 'integer':
					return parseInt(priv.xmlRpc.getText(domValue.firstChild));
				case 'bool':
				case 'boolean':
					return parseInt(priv.xmlRpc.getText(domValue.firstChild)) ? true : false;
				case 'string':
				case 'dateTime.iso8601':
					return '' + priv.xmlRpc.getText(domValue.firstChild);
				case 'float':
				case 'double':
					return parseFloat(priv.xmlRpc.getText(domValue.firstChild));
				case 'base64':
					return pub.base64.decode('' + priv.xmlRpc.getText(domValue.firstChild));
				case 'struct':
					var ret = {};
					for (var node = domValue.firstChild.firstChild; node != null; node = node.nextSibling) {
						if (node.tagName == 'member') {
							var name = '' + priv.xmlRpc.getText(node.getElementsByTagName('name')[0]);
							ret[name] = priv.xmlRpc.convertValue(node.getElementsByTagName('value')[0]);
						}
					}
					return ret;
				case 'array':
					var ret = [];
					var data = domValue.getElementsByTagName('data');
					for (var node = data.firstChild; node != null; node = node.nextSibling) {
						if (node.tagName == 'value') {
							ret.push(priv.xmlRpc.convertValue(node));
						}
					}
					return ret;
			}
		}
	};
	pub.xmlRpc = function (rpcParams /** [url|site], method, [params],
		[onSuccess], [onError], [notify], [onFinish] */) {
		rpcParams = rpcParams || this;
		if (!rpcParams.notify) priv.xmlRpc._lastId++;
		var rpcId = parseInt(priv.xmlRpc._lastId);
		pub.httpRequest({
			'url': rpcParams.url ?
				rpcParams.url : (
				rpcParams.site ?
				(rpcParams.site + '/xmlRpc') :
				pub.consts.SITE_URL + '/xmlRpc'),
			'method': 'post',
			'params': priv.xmlRpc.make(
				rpcParams.method,
				rpcParams.params ? rpcParams.params : null
			),
			'async': rpcParams.async,
			'contentType': 'text/xml; charset=UTF-8',
			'parser': 'xml',
			'onSuccess': function (res) {
				var result = null;
				if (res && (res.getElementsByTagName('fault').length > 0)) {
					var errorData = priv.xmlRpc.convertValue(res.
						getElementsByTagName('fault')[0].getElementsByTagName('value')[0]);
					if (errorData.faultCode > 0) {
						try {
							rpcParams.onError({
								'code': errorData.faultCode,
								'message': errorData.faultString
							});
						} catch (e) {
							pub.ui.alert('Error: <b>' + errorData.faultCode +
								'</b><br />Message: <b>' + errorData.faultString + '</b>',
								pub.translations('common', 'Error'), null, null, pub.ui.icons.ERROR);
							pub.debug('#20: ' + e.message);
							pub.debug(e);
						}
					} else
						pub.ui.alert('Error: <b>' + errorData.faultCode +
							'</b><br />Message: <b>' + errorData.faultString + '</b>',
							pub.translations('common', 'Error'), null, null, pub.ui.icons.ERROR);
				} else if (res && (result = res.getElementsByTagName('params')[0])) {
					var params = result.getElementsByTagName('param');
					var convParams = [];
					for (var i = 0; i < params.length; ++i) {
						var name = params[i].getElementsByTagName('name');
						var value = priv.xmlRpc.convertValue(params[i].getElementsByTagName('value')[0]);
						if (name.length > 0) convParams[priv.xmlRpc.getText(name[0])] = value;
						else convParams.push(value);
					}
					try {
						rpcParams.onSuccess(convParams);
					} catch (e) {
						pub.ui.alert('Runtime error while XML-RPC: <br />' + e.message,
							pub.translations('common', 'Error'), null, null, pub.ui.icons.ERROR);
						pub.debug('#21: ' + e.message);
						pub.debug(e);
					}
				} else {
					pub.ui.alert('Parse error while XML-RPC',
						pub.translations('common', 'Error'), null, null, pub.ui.icons.ERROR);
					pub.debug(res);
				}
			},
			'onFail': function (err) {
				pub.ui.alert('Http error (' + err + ') while XML-RPC',
					pub.translations('common', 'Error'), null, null, pub.ui.icons.ERROR);
			},
			'onFinish': ((rpcParams && !!rpcParams.onFinish) ? rpcParams.onFinish : null)
		});
	};
	
	pub.translations = function (section, msg) {
		section = section.toLowerCase();
		if (!priv._translations[section]) {
			pub.assert(false, 'Undefined translation; section: ' + section + '; text: ' + msg);
			return msg;
		} else {
			var origMsg = '' + msg;
			msg = msg.toLowerCase();
			msg = msg.replace(/[^a-zA-Z0-9]/g, '_');
			msg = msg.replace(/_+/g, '_');
			msg = msg.replace(/^_/g, '');
			msg = msg.replace(/_$/g, '');
			if (msg in priv._translations[section])
				return priv._translations[section][msg];
			else return origMsg;
		}
	};
	
	pub.makeSymbol = function (str) {
		str = str.toLowerCase();
		str = str.replace(/[^a-zA-Z0-9]/g, '_');
		str = str.replace(/_+/g, '_');
		str = str.replace(/^_/g, '');
		str = str.replace(/_$/g, '');
		return str;
	};
	
	pub.makeId = function (str) {
		str = pub.makeSymbol(str);
		if (pub.$id(str, true)) {
			var i = 1;
			var str2;
			do {
				str2 = str + '_' + (i++);
			} while (pub.$id(str2, true));
			return str2;
		} else
			return str;
	};
	
	priv._events = [ ];
	pub.event = function (eventName, obj, eventObj) {
		for (i in priv._events) {
			if (
					(i == parseInt(i)) &&
					(priv._events[i].name == eventName) &&
					(!obj || (priv._events[i].obj == obj)) &&
					(typeof priv._events[i].func == 'function')
				) try {
					if (!priv._events[i].func(eventObj) && obj)
						return false;
				} catch(e) {
					pub.debug('#22: Event: ' + eventName);
					pub.debug(priv._events[i].func);
					pub.debug(e.message);
					pub.debug(e);
				}
		}
		return true;
	};
	pub.addEvent = function (name, func, obj) {
		var max = priv._events.length;
		var _name = func ? name : name.name;
		var _obj = func ? obj : name.obj;
		var _func = func ? func : (name.func ? name.func : name['function']);
		priv._events[max] = {
			'name': _name,
			'func': _func,
			'obj': _obj
		};
		if (_obj) {
			try {
				if (!_obj[_name])
					_obj[_name] = function (event) {
						return pub.event(_name, _obj, event);
					};
			} catch(e) {
				pub.debug('#23: Add event: ' + name);
				pub.debug(func);
				pub.debug(e.message);
				pub.debug(e);
			}
		}
	};
	
	(function () {
		if (!/*@cc_on!@*/0) return;
		var e = ['abbr', 'article', 'aside', 'audio', 'bb', 'canvas', 'datagrid', 'datalist',
			'details', 'dialog', 'eventsource', 'figure', 'footer', 'header', 'hgroup', 'mark',
			'menu', 'meter', 'nav', 'output', 'progress', 'section', 'time', 'video'],
		i = e.length;
		while (i--) { document.createElement(e[i]); }
	})();
	
	pub.addEventListener =
	pub.attachEvent = (function () {
		if (document.addEventListener) {
			return function (el, type, fn) {
				el.addEventListener(type, fn, false);
			};
		} else {
			return function (el, type, fn) {
				el.attachEvent('on' + type, function () { return fn.call(el, window.event); });
			};
		}
	})();
	
	pub.consts.CANCEL_EVENT = function (event) {
		if (typeof event.preventDefault == 'function')
			event.preventDefault();
		if (typeof event.stopPropagation == 'function')
			event.stopPropagation();
		event.returnValue = false;
		event.cancelBubble = true;
		return false;
	};
	
	priv.shiftArgs = function (args) {
		var result = [];
		for (var i = 1; i < args.length; ++i)
			result.push(args[i]);
		return result;
	}
	
	if (!window.console) {
		function Console (log) { this.log = log; }
		window.console = new Console(
			(typeof opera != 'undefined' && !!opera.postError) ?
					opera.postError : function () { }
		);
	}
	
	if (!console.info) console.info = console.log;
	if (!console.warn) console.warn = console.log;
	if (!console.debug) console.debug = console.log;
	if (!console.error) console.error = console.log;
	if (!console.dir) console.dir = function (d) {
		console.log(pub.toJson(d));
	};
	if (!console.dirxml) console.dirxml = function (d) {
		if ('innerHTML' in d) console.log(d.innerHTML);
		else lonsole.log(d);
	};
	if (!console.group) console.group = function (grp) {
		console.log('-- Group: ' + grp + ' start');
		for (var i = 1; i < arguments.length; ++i)
			console.log(arguments[i]);
	};
	if (!console.groupCollapsed) console.groupCollapsed = console.group;
	if (!console.groupEnd) console.groupEnd = function () {
		console.log('-- Group end');
	};
	if (!console.assert) console.assert = function (test, msg) {
		if (test) console.log(msg);
	};
	if (!console.time) {
		console._timers = {};
		console.time = function (name) {
			var st = new Date();
			console.log('-- Timer (' + name + ') starts at: ' + st);
			console._timers[name] = st;
		};
		console.timeEnd = function (name) {
			if (console._timers[name]) {
				var en = new Date();
				console.log('-- Timer (' + name + ') end at: ' + en); 
				console.log('-- Timer (' + name + ') elapsed: ' + (en - console._timers[name]));
				delete console._timers[name];
			} else {
				console.log('-- Timer (' + name + ') does not exists.');
			}
		};
	}
	if (!console.profile) {
		console.profiles = [];
		console.profile = function (title) {
			console.time('profile');
		};
		console.profileEnd = function () {
			console.timeEnd('profile');
		};
	}
	if (!console.count) {
		console._count = { '_': 0 };
		console.count = function (title) {
			console.log('-- ' + (title ? title : '') + ' count: ' + (++console._count[(title ? title : '_')]));
		};
	}
	if (!console.trace) console.trace = function () {
		if (!!arguments.callee) {
			var func =  arguments.callee;
			console.log('-- Trace:' + (!!func && !!func.name ? ' ' + func.name : ''));
			while (!!func) {
				if (!!func.name) {
					console.group(func.name);
					console.log(func);
					console.groupEnd();
				} else
					console.log(func);
				func = func.caller;
			}
		} else
			console.log('-- Trace not supported.');
	};
	
	pub.log = function () { console.log.apply(console, arguments); };
	pub.domDir = function () { !!console.dir.apply ? console.dir.apply(console, arguments) : console.dir(arguments) };
	pub.debug = function () { !!console.debug.apply ? console.debug.apply(console, arguments) : console.debug(arguments); };
	pub.assert = function (exp, msg, warnOnly) {
		if (warnOnly && !exp) console.warn(msg);
		else console.assert(exp, msg);
	};
	
	djConfig = { 'parseOnLoad': false, 'isDebug': !!pub.conf.debug };
	pub.addJS(pub.consts.DOJO_PATH + '/dojo/dojo.js');
	
	pub.addEvent('onload', function () {
		dojo.require('dojo.parser');
		pub.attr = function () { return dojo.attr.apply(dojo, arguments); };
		pub.style = function () { return dojo.style.apply(dojo, arguments); };
		if (!!dojo.isIE && (dojo.isIE < 8)) {
			var bs = document.getElementsByTagName('button');
			if (!!bs && (bs.length > 0)) {
				for (var i = 0; i < bs.length; ++i) {
					if (!!bs[i].attributes && (('value' in bs[i].attributes) || bs[i].getAttribute('value', 2))) {
						bs[i].attachEvent('onclick', function () {
							try {
								if ('value' in this.attributes) this.value = this.attributes.value.value;
								else this.value = this.getAttribute('value', 2);
							} catch (e) {
								pub.debug('#24: ' + e.message);
								pub.debug(e);
							}
						});
					}
				}
			}
		}
	});
	
	pub.tryUntil(function () {
		dojo.addOnLoad(function () {
			if (!priv.conf._documentLoaded) {
				priv.conf._documentLoaded = true;
				pub.event('onload');
			}
	    });
	}, function () {
		return (typeof dojo != 'undefined') && !!dojo.addOnLoad;
	});
	
	priv._toIsoDate = function (dateObj) {
		return (dateObj instanceof Date ?
			'' + dateObj.getFullYear() +
			'-' + ((dateObj.getMonth() + 1) < 10 ? '0' : '') + (dateObj.getMonth() + 1) +
			'-' + (dateObj.getDate() < 10 ? '0' : '') + dateObj.getDate()
			: '');
	};
	
	priv._toIsoTime = function (dateObj) {
		return (dateObj instanceof Date ?
			'' + (dateObj.getHours() < 10 ? '0' : '') + dateObj.getHours() +
			':' + (dateObj.getMinutes() < 10 ? '0' : '') + dateObj.getMinutes() +
			':' + (dateObj.getSeconds() < 10 ? '0' : '') + dateObj.getSeconds()
			: '');
	};
	
	pub.toIsoDateTime = function (dateObj, separator) {
		if (!separator) separator = 'T';
		return (dateObj instanceof Date ?
			priv._toIsoDate(dateObj) + separator + priv._toIsoTime(dateObj)
		: '');
	};
	
	priv._parseIsoDate = function (dateStr) {
		var dates = dateStr.split('-');
		if (dates.length) {
			var dateObj = new Date();
			dateObj.setFullYear(parseInt(dates[0], 10), parseInt(dates[1], 10) - 1, parseInt(dates[2], 10));
			return dateObj;
		} else
			return '';
	};
	
	priv._parseIsoTime = function (dateStr) {
		var dates = dateStr.split(':');
		if (dates.length) {
			var dateObj = new Date();
			dateObj.setHours(dates[0]);
			dateObj.setMinutes(dates[1]);
			dateObj.setSeconds(dates[2]);
			return dateObj;
		} else
			return '';
	};
	
	pub.parseIsoDateTime = function (dateStr) {
		var dates = dateStr.split(/[Tt ]+/);
		if (dates.length) {
			return priv._parseIsoDate(dates[0]) + priv._parseIsoTime(dates[1]);
		} else
			return '';
	};
	
	pub.addClass = function (htmlo, classn) {
		if (!!htmlo.className) {
			var classes = htmlo.className.split(' ');
			for (var i = 0; i < classes.length; i++) {
				if (classes[i] == classn) return;
			}
			htmlo.className = (htmlo.className + ' ' + classn).replace(/^ +/, '').replace(/ +$/, '');
		} else {
			htmlo.className = classn;
		}
	};
	
	pub.removeClass = function (htmlo, classn) {
		if (htmlo.className) {
			var classes = htmlo.className.split(' ');
			var ncn = '';
			for (var i = 0; i < classes.length; i++) {
				if (classes[i] != classn) ncn = ncn + ' ' + classes[i];
			}
			htmlo.className = ncn.replace(/^ +/, '').replace(/ +$/, '');
		}
	};
	
	pub.redraw = function (onElement) {
		if (dojo.isIE) {
			onElement = onElement || document.body;
			var saved = '' + onElement.style.position;
			onElement.style.position = 'relative';
			setTimeout(function() {
				onElement.style.position = saved;
			}, 333);
		}
	};
	
	priv._lastGenerated = 0;
	pub.generateId = function () {
		priv._lastGenerated++;
		return objectName + "_generated_" + priv._lastGenerated;
	};
	
	priv._preload = { 'preloaded': [] };
	
	pub.ui = {
		/** const */ 'buttons': { 'OK': 1, 'CANCEL': 2, 'YES': 4, 'NO': 8 },
		/** const */ 'icons': {
			'INFO': '/images/common/info.19x19.gif',
			'WARN': '/images/common/warn.19x19.gif',
			'ERR':  '/images/common/error.19x19.gif',
			'WARNING': '/images/common/warn.19x19.gif',
			'ERROR':  '/images/common/error.19x19.gif',
			'INFORMATION': '/images/common/info.19x19.gif',
			'LOAD': '/images/common/icon_loading_16x16.gif',
			'LOADING': '/images/common/icon_loading_16x16.gif'
		},
		'preload': function (url) {
			var preloadBox = pub.$id(objectName + '-hidden-preload', true);
			if (preloadBox == null) {
				preloadBox = document.createElement('div');
				preloadBox.setAttribute('id', preloadBox.id = objectName + '-hidden-preload');
				pub.$tag('head').appendChild(preloadBox);
				preloadBox.style.display = 'none';
			}
			if (!pub.inArray(priv._preload.preloaded, url)) {
				preloadBox.innerHTML += '<img src="' + url + '" alt="preview" />';
				priv._preload.preloaded.push(url);
			}
		},
		'preview': function (code, closeClick, color) {
			var pc = document.createElement('table'),
				pr = document.createElement('tr'),
				pd = document.createElement('td'),
				body = pub.$tag('body'),
				closeFunc = null;
			
			pd.innerHTML = code;
			
			if (!color) {
				if (!!body.currentStyle)
					color = body.currentStyle.backgroundColor;
				if (!color && document.defaultView.getComputedStyle)
					color = document.defaultView.getComputedStyle(body, null).
						getPropertyValue('background-color');
			}
			try {
				if (!!color) {
					var c = new pub.color.Rgb(0, 0, 0);
					c.parse(color);
					pc.style.backgroundColor = 'rgba(' + parseInt(c.r * 255) + ', ' + 
						parseInt(c.g * 255) + ', ' + parseInt(c.b * 255) + ', 0.7)';
				} else
					pc.style.backgroundColor = 'rgba(128, 128, 128, 0.7)';
			} catch (e) {
				pub.debug('#25: ' + e.message);
				pub.debug(e);
				if (!!dojo.isIE) {
					var p = function (n) {
						var s = '' + parseInt(n * 255).toString(16);
						pub.debug(s);
						if (n < 16) s = '0' + s;
						return s;
					}, iecol = '#C0' + p(c.r) + p(c.g) + p(c.b);
					pc.style.filter = "progid:DXImageTransform.Microsoft.Gradient(StartColorStr=" +
						iecol + ",EndColorStr=" + iecol + ")";
				} else {
					pc.style.backgroundImage = 'url(' + pub.consts.ACTUAL_TEMPLATE_URL +
						'/images/common/half.png)';
				}
			}
			pc.style.width = '100%';
			pc.style.height = '100%';
			pc.style.top = '0px';
			pc.style.left = '0px';
			pc.style.zIndex = '99999';
			pd.style.textAlign = 'center';
			pd.style.verticalAlign = 'middle';
			pd.style.boxShadow = '2px 2px 2px #000';
			pc.style.position = 'absolute';
			if (!!dojo.isIE && dojo.isIE < 8) {
				pub.$tag('html').style.overflow = 'hidden';
				window.scrollTo(0, 0);
				pc.style.backgroundImage = 'none';
			} else {
				pc.style.position = 'fixed';
				if (!closeClick) pc.onclick = closeFunc;
			}
			pr.appendChild(pd);
			pc.appendChild(pr);
			if (!!dojo.isIE && dojo.isIE < 8) {
				var container = document.createElement('div');
				container.innerHTML = '<img src="' + pub.consts.ACTUAL_TEMPLATE_URL +
					'/images/common/blank.gif" style="filter: progid:' +
					'DXImageTransform.Microsoft.AlphaImageLoader(src=\'' + pub.consts.ACTUAL_TEMPLATE_URL +
					'/images/common/half.png\', sizingMethod=scale);" width="100%" height="100%" ' +
					'alt="" /> ' + pc.outerHTML;
				container.style.width = '100%';
				container.style.height = '100%';
				container.style.top = '0px';
				container.style.left = '0px';
				container.style.zIndex = '99998';
				container.style.position = 'absolute';
				body.insertBefore(container, body.firstChild);
				if (!closeClick) container.onclick = function () {
					pub.$tag('html').style.overflow = '';
					this.parentNode.removeChild(this);
				};
			} else {
				body.insertBefore(pc, body.firstChild);
			}
			return closeFunc = function () {
				if (!!dojo.isIE && dojo.isIE < 8)
					pub.$tag('html').style.overflow = '';
				pc.parentNode.removeChild(pc);
			};
		},
		'lightbox': function (params) {
			if (typeof dojox.image == 'undefined' || typeof dojox.image.Lightbox == 'undefined') {
				pub.addCSS(pub.consts.DOJO_PATH + '/dojox/image/resources/Lightbox.css');
				dojo.require('dojox.image.Lightbox');
			}
			if (params instanceof Array) {
				var lboxes = [], grp = pub.generateId();
				for (var i = 0; i in params; ++i) {
					var url, lb, p, img;
					if (typeof params[i] != 'string') p = params[i];
					else p = pub.$id(params[i]);
					if (!!p.href) url = p.href;
					else if (!!p.src) url = p.src;
					else if (!!p.url) url = p.url;
					pub.ui.preload(url);
					lb = new dojox.image.Lightbox({
						'title': '' + params[i].title,
						'group': grp,
						'href': '' + url
					});
					lb.startup();
					lboxes.push(lb);
				}
				return {
					'show': function (index) {
						if (index in lboxes) {
							lboxes[index].show();
							return true;
						} else 
							return false;
					}
				};
			} else {
				var url;
				if (!!params.href) url = params.href;
				else if (!!params.src) url = params.src;
				else if (!!params.url) url = params.url;
				pub.ui.preload(url);
				var dialog = new dojox.image.LightboxDialog({});
				dialog.startup();
				dialog.show({ 'title': '' + params.title, 'href': '' + url });
			}
		},
		'progress': function (msg, title, progress, onElement) {
			if (typeof onElement == "undefined") {
				dojo.require('dijit.Dialog');
				dojo.require('dijit.form.Button');
				
				if (!title) title = pub.consts.WINDOW_TITLE;
				if (!msg) msg = pub.translations('common', 'Please wait');
				var dial = new dijit.Dialog({ 'title': title, 'onCancel': function () { return false; } });
				var msgDiv = document.createElement('div');
				var stateDiv = document.createElement('div');
				var setProgressState = function (state) {
					stateDiv.innerHTML = '' + state;
				};
				
				//msgDiv.appendChild(document.createTextNode(msg));
				msgDiv.innerHTML = '<img src="' + pub.consts.ACTUAL_TEMPLATE_URL +
					pub.ui.icons.LOADING + '" alt="..." /> ' + msg + ' ...';
				
				msgDiv.appendChild(stateDiv);
				dial.attr('content', msgDiv);
				dojo.body().appendChild(dial.domNode);
				dial.show();
				dial.containerNode.style.height = 'auto';
				dial.closeButtonNode.style.display = 'none';
				
				if (typeof progress == "function") {
					try {
						progress.call({ 'setProgressState': setProgressState });
					} catch (e) {
						pub.debug('#26: ' + e.message);
						pub.debug(e);
					}
					dial.destroy();
					return false;
				} else {
					var f = function () { dial.destroy(); };
					f.setProgressState = setProgressState;
					return f;
				}
			} else {
				dojo.require('dijit.Tooltip');
				dijit.showTooltip('<img src="' + pub.consts.ACTUAL_TEMPLATE_URL +
					pub.ui.icons.LOADING + '" alt="..." /> ' + msg + ' ...', onElement);
				if (typeof progress == "function") {
					try {
						progress();
					} catch (e) {
						pub.debug('#27: ' + e.message);
						pub.debug(e);
					}
					dijit.hideTooltip(onElement);
					return false;
				} else
					return function () { dijit.hideTooltip(onElement); };
			}
		},
		'working': function (title, msg) {
			if (!msg) msg = null;
			return pub.ui.progress(msg, title);
		},
		'alert': function (msg, title, onClick, buttons, icon) {
			dojo.require('dijit.Dialog');
			dojo.require('dijit.form.Button');
			
			if (!title) title = pub.consts.WINDOW_TITLE;
			var dial = new dijit.Dialog({ 'title': title });
			var msgDiv = document.createElement('div');
			var buttDiv = document.createElement('div');
			var divs = document.createElement('div');
			
			var btLabel = pub.translations('common', (parseInt(buttons) & pub.ui.buttons.YES) ? 'yes' : 'ok');
			
			msgDiv.innerHTML = msg;
			divs.appendChild(msgDiv);
			
			(new dijit.form.Button({
				'label': btLabel,
				'onCancel': function () { return false; },
				'onClick': function () {
					dial.destroy();
					if (onClick)
						try {
							onClick();
						} catch(e) {
							pub.debug('#28: ' + e.message);
							pub.debug(e);
						};
				} 
			})).placeAt(buttDiv);
			buttDiv.style.textAlign = 'center';
			divs.appendChild(buttDiv);
			
			dial.attr('content', divs);
			dojo.body().appendChild(dial.domNode);
			dial.show();
			dial.containerNode.style.height = 'auto';
			if (typeof icon != 'undefined') {
				msgDiv.style.paddingLeft = '26px';
				if (dojo.isIE && (dojo.isIE < 7))
					msgDiv.style.height = '20px';
				msgDiv.style.minHeight = '20px';
				msgDiv.style.background = 'url(' + pub.consts.ACTUAL_TEMPLATE_URL +
					icon + ') 0% 50% no-repeat';
			}
			buttDiv.style.textAlign = 'center';
		},
		'popup': function (content, title) {
			dojo.require('dijit.Dialog');
			
			if (!title) title = pub.consts.WINDOW_TITLE;
			var dial = new dijit.Dialog({ 'title': title });
			var divs = document.createElement('div');
			
			//msgDiv.appendChild(document.createTextNode(msg));
			divs.innerHTML = content;
			
			dial.attr('content', divs);
			dojo.body().appendChild(dial.domNode);
			dial.show();
			dial.containerNode.style.height = 'auto';
			
			return function () { dial.destroy(); };
		},
		'load': function (htmla, title, params) {
			dojo.require('dijit.Dialog');
			
			var t = htmla || this;
			var src = (typeof t.href != 'undefined') ? t.href : ('' + htmla);
			var p = params || {};
			if (!('width' in p)) p.width = 600;
			if (!('height' in p)) p.height = 400;
			
			pub.httpRequest({
				'url': src,
				'method': 'get',
				'params': '',
				'randomize': false,
				'parser': 'text',
				'onSuccess': function (result) {
					var res = '';
					var m = [];
					result = ('' + result).replace(/[\n\r\t ]+/g, " ");
					
					if (!title) {
						if (m = result.match(/<head>.*<title>(.*)<\/title>.*<\/head>/i)) title = m[1];
						else title = pub.consts.WINDOW_TITLE;
					}
					
					if (m = result.match(/<body[^>]*>(.*)<\/body>/i)) res = m[1];
					else if (m = result.match(/<html[^>]*>(.*)<\/html>/i)) res = m[1];
					else res = result;
					
					var dial = new dijit.Dialog({ 'title': title });
					var div = document.createElement('div');
					div.setAttribute('style', 'position:relative;width:' + p.width +
						'px;height:' + p.height + 'px;overflow:auto;');
					
					dial.attr('content', div);
					dojo.body().appendChild(dial.domNode);
					dial.show();
					
					div.innerHTML = res;
					div.style.width = p.width + 'px';
					div.style.height = p.height + 'px';
					div.style.overflow = 'auto';
				},
				'onFail': function (err) {
					pub.ui.alert('HTTP error :' + err + '<br />Could not load: <i>' + src + '</i>',
						pub.translations('common', 'Error'), null, null, pub.ui.icons.ERROR);
				}
			});
			
			return false;
		},
		'confirm': function (msg, title, onYes, onNo, buttons) {
			dojo.require('dijit.Dialog');
			dojo.require('dijit.form.Button');
			
			if (typeof buttons == "undefined")
				buttons = (pub.ui.buttons.YES | pub.ui.buttons.NO);
			
			if (!title) title = pub.consts.WINDOW_TITLE;
			
			var msgDiv = document.createElement('div');
			var buttDiv = document.createElement('div');
			var divs = document.createElement('div');
			
			var okLabel = pub.translations('common', (parseInt(buttons) &
				pub.ui.buttons.YES) ? 'yes' : 'ok');
			var cancelLabel = ((parseInt(buttons) & pub.ui.buttons.CANCEL) ||
				(parseInt(buttons) & pub.ui.buttons.NO)) ?
				pub.translations('common', (parseInt(buttons) &
				pub.ui.buttons.NO) ? 'no' : 'cancel') : false;
			
			var dial = new dijit.Dialog({ 'title': title, 'onCancel': function () { return !!cancelLabel; } });
			
			msgDiv.appendChild(document.createTextNode(msg));
			divs.appendChild(msgDiv);
			
			(new dijit.form.Button({ 'label': okLabel, 'onClick': function () {
				dial.destroy(); try { onYes(); } catch(e) { pub.debug('#29: ' + e.message); pub.debug(e); };
			} })).placeAt(buttDiv);
			if (cancelLabel)
				(new dijit.form.Button({ 'label': cancelLabel, 'onClick': function () {
					dial.destroy(); try { onNo(); } catch(e) { pub.debug('#30: ' + e.message); pub.debug(e); };
				} })).placeAt(buttDiv);
			buttDiv.style.textAlign = 'center';
			divs.appendChild(buttDiv);
			
			dial.attr('content', divs);
			dojo.body().appendChild(dial.domNode);
			dial.show();
			dial.containerNode.style.height = 'auto';
			buttDiv.style.textAlign = 'center';
			if (!cancelLabel) dial.closeButtonNode.style.display = 'none';
		},
		'prompt': function (msg, title, defaultVal, onEnter, buttons) {
			dojo.require('dijit.Dialog');
			dojo.require('dijit.form.Button');
			dojo.require('dijit.form.TextBox');
			
			if (typeof buttons == "undefined")
				buttons = (pub.ui.buttons.OK | pub.ui.buttons.CANCEL);
			
			if (!title) title = pub.consts.WINDOW_TITLE;
			
			var msgDiv = document.createElement('div');
			var buttDiv = document.createElement('div');
			var inpDiv = document.createElement('div');
			var divs = document.createElement('div');
			
			var okLabel = pub.translations('common', (parseInt(buttons) &
				pub.ui.buttons.YES) ? 'yes' : 'ok');
			var cancelLabel = ((parseInt(buttons) & pub.ui.buttons.CANCEL) ||
				(parseInt(buttons) & pub.ui.buttons.NO)) ?
				pub.translations('common', (parseInt(buttons) &
				pub.ui.buttons.NO) ? 'no' : 'cancel') : false;
			
			var dial = new dijit.Dialog({
				'title': title,
				'onCancel': function () { return !!cancelLabel; }
			});
			
			msgDiv.appendChild(document.createTextNode(msg));
			divs.appendChild(msgDiv);
			
			var inp = new dijit.form.TextBox({
				'onKeyUp': function (key) {
					if ((key.keyCode || key.which) == 13) {
						var val = inp.attr('value');
						dial.destroy();
						try { onEnter(val); } catch(e) { pub.debug('#31: ' + e.message); pub.debug(e); };
					}
				}
			});
			inp.attr('value', (!!defaultVal) ? ('' + defaultVal) : '');
			inp.placeAt(inpDiv);
			divs.appendChild(inpDiv);
			
			(new dijit.form.Button({ 'label': okLabel, 'onClick': function () {
				var val = inp.attr('value');
				dial.destroy();
				try { onEnter(val); } catch(e) { pub.debug('#32: ' + e.message); pub.debug(e); };
			} })).placeAt(buttDiv);
			if (cancelLabel)
				(new dijit.form.Button({ 'label': cancelLabel, 'onClick': function () {
					dial.destroy();
				} })).placeAt(buttDiv);
			buttDiv.style.textAlign = 'center';
			divs.appendChild(buttDiv);
			
			dial.attr('content', divs);
			dojo.body().appendChild(dial.domNode);
			dial.show();
			dial.containerNode.style.height = 'auto';
			buttDiv.style.textAlign = 'center';
			if (!cancelLabel) dial.closeButtonNode.style.display = 'none';
		},
		'promptForm': function (title, onOk, onCancel, buttons) {
			dojo.require('dijit.Dialog');
			dojo.require('dijit.form.Button');
			
			if (typeof buttons == "undefined")
				buttons = (pub.ui.buttons.OK | pub.ui.buttons.CANCEL);
			
			if (!title) title = pub.consts.WINDOW_TITLE;
			
			var buttDiv = document.createElement('div');
			var container = document.createElement('div');
			var divs = document.createElement('div');
			divs.appendChild(container);
			
			var okLabel = pub.translations('common', (parseInt(buttons) &
				pub.ui.buttons.YES) ? 'yes' : 'ok');
			var cancelLabel = ((parseInt(buttons) & pub.ui.buttons.CANCEL) ||
				(parseInt(buttons) & pub.ui.buttons.NO)) ?
				pub.translations('common', (parseInt(buttons) &
				pub.ui.buttons.NO) ? 'no' : 'cancel') : false;
			
			var dial = new dijit.Dialog({
				'title': title,
				'onCancel': function () { return !!cancelLabel; }
			});
			
			(new dijit.form.Button({ 'label': okLabel, 'onClick': function () {
				var r = onOk();
				if (r !== false)
					dial.destroy();
			} })).placeAt(buttDiv);
			if (!!cancelLabel) {
				(new dijit.form.Button({ 'label': cancelLabel, 'onClick': function () {
					if (!!onCancel) try { onCancel(); } catch(e) { pub.debug('#33: ' + e.message); pub.debug(e); };
					dial.destroy();
				} })).placeAt(buttDiv);
			}
			buttDiv.style.textAlign = 'center';
			divs.appendChild(buttDiv);
			
			dial.attr('content', divs);
			dojo.body().appendChild(dial.domNode);
			setTimeout(function () {
				dial.show();
				if (!!cancelLabel) {
					dial.closeButtonNode.onclick = function () {
						try { onCancel(); } catch(e) { pub.debug('#34: ' + e.message); pub.debug(e); };
					};
				} else {
					dial.closeButtonNode.style.display = 'none';
				}
				
			}, 1000);
			dial.containerNode.style.height = 'auto';
			buttDiv.style.textAlign = 'center';
			
			return container;
		},
		'promptUrl': function (title, defaultVal, onEnter, buttons) {
			dojo.require('dijit.Dialog');
			dojo.require('dijit.form.Button');
			dojo.require('dijit.form.TextBox');
			
			if (typeof buttons == "undefined")
				buttons = (pub.ui.buttons.OK | pub.ui.buttons.CANCEL);
			
			if (!title) title = pub.consts.WINDOW_TITLE;
			
			var preIfr = document.createElement('iframe');
			var buttDiv = document.createElement('div');
			var inpDiv = document.createElement('div');
			var divs = document.createElement('div');
			
			var okLabel = pub.translations('common', (parseInt(buttons)
				& pub.ui.buttons.YES) ? 'yes' : 'ok');
			var cancelLabel = ((parseInt(buttons) & pub.ui.buttons.CANCEL) ||
				(parseInt(buttons) & pub.ui.buttons.NO)) ?
				pub.translations('common', (parseInt(buttons) &
				pub.ui.buttons.NO) ? 'no' : 'cancel') : false;
			
			var dial = new dijit.Dialog({
				'title': title,
				'onCancel': function () { return !!cancelLabel; }
			});
			
			var value = (!!defaultVal) ? ('' + defaultVal) : pub.consts.SITE_URL;
			
			var inp = new dijit.form.TextBox({
				'value': value,
				'style': 'width: 860px;',
				'onChange': function () { preIfr.setAttribute('src', this.attr('value')); }
			});
			
			var go = new dijit.form.Button({
				'label': '&raquo;',
				'onClick': function () { preIfr.setAttribute('src', inp.attr('value')); }
			});
			
			inp.placeAt(inpDiv);
			go.placeAt(inpDiv);
			divs.appendChild(inpDiv);
			
			preIfr.setAttribute('id', pub.generateId());
			preIfr.setAttribute('src', value);
			preIfr.setAttribute('style', 'width: 900px; height: 400px; border: 1px solid #888888;');
			preIfr.onload = function () { 
				var loc = { 'href': preIfr.getAttribute('src') };
				if (preIfr.contentDocument && preIfr.contentDocument.location)
					loc = preIfr.contentDocument.location;
				if (preIfr.contentWindow && preIfr.contentWindow.location)
					loc = preIfr.contentWindow.location;
				if (preIfr.document && preIfr.document.location)
					loc = preIfr.document.location;
				inp.attr('value', loc.href);
			};
			
			divs.appendChild(preIfr);
			
			(new dijit.form.Button({ 'label': okLabel, 'onClick': function () {
				var val = inp.attr('value');
				dial.destroy();
				try { onEnter(val); } catch(e) { pub.debug('#35: ' + e.message); pub.debug(e); };
			} })).placeAt(buttDiv);
			if (cancelLabel)
				(new dijit.form.Button({ 'label': cancelLabel, 'onClick': function () {
					dial.destroy();
				} })).placeAt(buttDiv);
			buttDiv.style.textAlign = 'center';
			divs.appendChild(buttDiv);
			
			dial.attr('content', divs);
			dojo.body().appendChild(dial.domNode);
			dial.show();
			dial.containerNode.style.height = 'auto';
			buttDiv.style.textAlign = 'center';
			if (!!cancelLabel) dial.closeButtonNode.style.display = 'none';
		},
		'ask': function (form, title, onAnswer, buttons, scope) {
			dojo.require('dijit.Dialog');
			
			if (typeof buttons == "undefined")
				buttons = (pub.ui.buttons.OK | pub.ui.buttons.CANCEL);
			
			if (typeof scope == "undefined")
				scope = this;
			
			if ((form.type == 'xhr') &&
				(typeof File != 'undefined') &&
				('getAsBinary' in File.prototype) &&
				!(pub.forEach(form.inputs, function (key, input) {
					return input.type != 'file';
				}))) { form.type = 'form'; }
			
			if (!title) title = pub.consts.WINDOW_TITLE;
			var dial = new dijit.Dialog({ 'title': title, 'onCancel': function () {
				return !!(parseInt(buttons) & pub.ui.buttons.CANCEL);
			} });
			var div = document.createElement('div');
			var f = document.createElement('form');
			if (!form.id) form.id = pub.generateId();
			f.setAttribute('id', form.id);
			if (form.type == 'form' || form.type == 'iframe') {
				f.setAttribute('action', !!form.action ? form.action : '?');
				f.setAttribute('method', !!form.method ? form.method : 'get');
				if (!!form.enctype) {
					f.setAttribute('enctype', form.enctype);
					if (!!dojo.isIE) f.setAttribute('encoding', form.enctype);
				}
			}
			if (!!form.label) {
				var l = document.createElement('div');
				l.style.margin = '0 auto';
				l.style.textAlign = 'center';
				f.appendChild(l);
				l.innerHTML = '' + form.label;
				l.style.margin = '0 auto';
				l.style.textAlign = 'center';
			}
			var t = document.createElement('div');
			if (!dojo.isIE || dojo.isIE >= 8) {
				t.setAttribute('style', 'display: table; border: none;');
			}
			var radios = { };
			var arrays = { };
			pub.forEach(form.inputs, function (key, input) {
				if (input.type == 'hidden') {
					var hi = document.createElement('input');
					hi.setAttribute('id', form.id + '_' + input.name);
					hi.setAttribute('name', '' + input.name);
					hi.setAttribute('type', 'hidden');
					hi.setAttribute('value', '' + input.value);
					f.appendChild(hi);
				} else {
					var inputId = form.id + '_' + input.name;
					if (inputId.match(/\[.*\]$/)) {
						var k = inputId.match(/(.*)\[(.*)\]$/);
						if (k[2]) inputId = k[1] + '_' + k[2];
						else {
							if (!(k[1] in arrays)) arrays[k[1]] = 0;
							inputId = k[1] + '_' + arrays[k[1]];
							arrays[k[1]] = arrays[k[1]] + 1;
						}
					}
					if (input.type == 'radio') {
						if (!(input.name in radios)) radios[input.name] = 0;
						inputId = inputId + '_' + radios[input.name];
						radios[input.name] = radios[input.name] + 1;
					}
					var tr = document.createElement('div');
					if (!dojo.isIE || dojo.isIE >= 8) {
						tr.setAttribute('style', 'display: table-row;');
					}
					var td1 = document.createElement('span');
					var lab = document.createElement('label');
					lab.setAttribute('for', inputId);
					td1.appendChild(lab);
					lab.innerHTML = '' + input.label;
					var td2 = document.createElement('span');
					var inp = null;
					if (input.type == 'select') {
						inp = document.createElement('select');
					} else {
						inp = document.createElement('input');
					}
					inp.setAttribute('id', inputId);
					inp.setAttribute('name', '' + input.name);
					if (!!input.multiple) inp.setAttribute('multiple', 'multiple');
					if ('min' in input) inp.setAttribute('min', input.min);
					if ('max' in input) inp.setAttribute('max', input.max);
					if (input.type == 'select') {
						if (input.options instanceof Array)
							for (var i = 0; i < input.options.length; ++i) {
								var inpo = document.createElement('option');
								inpo.setAttribute('value', '' + input.options[i].value);
								if (input.value == input.options[i].value) 
									inpo.setAttribute('selected', 'selected');
								inpo.innerHTML = '' +
									(input.options[i].text ? input.options[i].text : input.options[i].value);
								inp.appendChild(inpo);
							}
					} else {
						inp.setAttribute('type', '' + input.type);
						if ('value' in input) inp.setAttribute('value', '' + input.value);
						if (!!input.accept) inp.setAttribute('accept', '' + input.accept);
					}
					td2.appendChild(inp);
					
					if (input.type == 'checkbox' || input.type == 'radio') {
						if (!!input.checked) inp.setAttribute('checked', 'checked');
						if (!dojo.isIE || dojo.isIE >= 8) {
							td2.setAttribute('style', 'display: table-cell; text-align: right;' +
								' padding-right: 10px;');
							td1.setAttribute('style', 'display: table-cell; text-align: left;');
						} else {
							td2.setAttribute('style', 'padding-right: 10px;');
						}
						tr.appendChild(td2);
						tr.appendChild(td1);
					} else {
						if (!dojo.isIE || dojo.isIE >= 8) {
							td1.setAttribute('style', 'display: table-cell; text-align: right;' +
								' padding-right: 10px;');
							td2.setAttribute('style', 'display: table-cell; text-align: left;');
						} else {
							td1.setAttribute('style', 'padding-right: 10px;');
						}
						tr.appendChild(td1);
						tr.appendChild(td2);
					}
					
					t.appendChild(tr);
				}
			});
			
			var extractResult = function () {
				var result = { };
				var radios = { };
				var arrays = { };
				pub.forEach(form.inputs, function (key, input) {
					var inputId = form.id + '_' + input.name;
					var inputKey = input.name;
					var inputKey2 = false;
					if (inputId.match(/\[.*\]$/)) {
						var k = inputId.match(/(.*)\[(.*)\]$/);
						if (k[2]) inputId = k[1] + '_' + k[2];
						else {
							if (!(k[1] in arrays)) arrays[k[1]] = 0;
							inputId = k[1] + (k[2] = '_' + arrays[k[1]]);
							arrays[k[1]] = arrays[k[1]] + 1;
						}
						inputKey = input.name.match(/(.*)\[.*\]$/)[1];
						inputKey2 = k[2];
						if (!(inputKey in result)) result[inputKey] = { };
						if (input.type == 'file' && form.enctype == 'multipart/form-data') {
							result[inputKey][inputKey2] = {
								'fileName': pub.$id(inputId).value,
								'binaryData': (0 in pub.$id(inputId).files &&
									typeof pub.$id(inputId).files[0].getAsBinary == 'function') ?
										pub.$id(inputId).files[0].getAsBinary() : null
							};
						} else if (input.type == 'checkbox') {
							result[inputKey][inputKey2] = !!pub.$id(inputId).checked ?
								(!!pub.$id(inputId).value ? pub.$id(inputId).value : true) : false;
						} else if (input.type == 'radio') {
							var radioKey = inputKey + '_' + inputKey2;
							if (!(radioKey in radios)) radios[radioKey] = 0;
							inputId = inputId + '_' + radios[radioKey];
							radios[radioKey] = radios[radioKey] + 1;
							if (!!pub.$id(inputId).checked)
								result[inputKey][inputKey2] = pub.$id(inputId).value;
						} else {
							result[inputKey][inputKey2] = pub.$id(inputId).value;
						}
					} else {
						if (input.type == 'file' && form.enctype == 'multipart/form-data') {
							result[inputKey] = {
								'fileName': pub.$id(inputId).value,
								'binaryData': pub.$id(inputId).files[0].getAsBinary()
							};
						} else if (input.type == 'checkbox') {
							result[inputKey] = !!pub.$id(inputId).checked ?
								(!!pub.$id(inputId).value ? pub.$id(inputId).value : true) : false;
						} else if (input.type == 'radio') {
							if (!(inputKey in radios)) radios[inputKey] = 0;
							inputId = inputId + '_' + radios[inputKey];
							radios[inputKey] = radios[inputKey] + 1;
							if (!!pub.$id(inputId).checked)
								result[inputKey] = pub.$id(inputId).value;
						} else {
							result[inputKey] = pub.$id(inputId).value;
						}
					}
				});
				return result;
			};
			
			var okClick = null;
			if (form.type == 'script') {
				okClick = function () {
					var result = extractResult();
					dial.destroy();
					onAnswer(result, scope);
				};
			} else if (form.type == 'xhr') {
				okClick = function () {
					if (onAnswer(f, scope)) {
						var url = '' + form.action;
						var req = '';
						var wrp = null;
						var res = extractResult();
						if (('' + form.method).toLowerCase() == 'post') {
							if (form.enctype == 'multipart/form-data') {
								var boundaryString = 'AJAX---BOUNDARY----' + (new Date).getTime();
								var boundary = '--' + boundaryString;
								var CRLF  = "\r\n";
								wrp = function (key, val) {
									if ('fileName' in val) {
										req += CRLF + boundary + CRLF +
											'Content-Disposition: form-data; name="' + key +
											'"; filename="' + val.fileName + '"' + CRLF +
											'Content-Type: application/octet-stream' +
											CRLF + CRLF + val.binaryData;
									} else {
										req += CRLF + boundary + CRLF +
											'Content-Disposition: form-data; name="' +
											key + '"' + CRLF + CRLF + val;
									}
								};
							} else {
								wrp = function (key, val) { req += key + '=' + val + '&'; };
							}
						} else {
							if (url == '') url = '?';
							else if (url.indexOf('?') < 0) url = url + '?';
							else url = url + '&';
							wrp = function (key, val) { url += key + '=' + val + '&'; };
						}
						pub.forEach(form.inputs, wrp);
						pub.httpRequest({
							'url': url,
							'method': form.method,
							'binary': true,
							'params': req + (form.enctype == 'multipart/form-data' ?
								CRLF + boundary + '--' : ''),
							'contentType': (!!form.enctype ? form.enctype +
								(form.enctype == 'multipart/form-data' ? ';boundary=' +
								boundaryString : '') : 'application/x-www-form-urlencoded'),
							'parser': (!!form.resultParser ? form.resultParser : 'text'),
							'onSuccess': form.onSuccess,
							'onFail': form.onFail,
							'onFinish': form.onFinish,
							'onProgress': form.onProgress
						});
					} else return false;
				};
			} else if (form.type == 'iframe') {
				okClick = function () {
					if (onAnswer(f, scope)) {
						var ifr = document.createElement('iframe');
						var ifrId = form.id + '_iframe';
						ifr.setAttribute('id', ifrId);
						ifr.setAttribute('name', ifrId);
						ifr.setAttribute('submitName', ifrId);
						ifr.setAttribute('src', 'about:blank');
						pub.$tag('body').appendChild(ifr);
						ifr.style.display = 'none';
						ifr.onload = function () {};
						pub.addEventListener(ifr, 'load', function () {
							form.onLoad((ifr.contentDocument || ifr.contentWindow.document ||
								ifr.document).documentElement);
						});
						f.setAttribute('target', ifrId);
						f.submit();
					}
					return false;
				};
			} else {
				okClick = function () {
					if (onAnswer(f, scope)) f.submit();
					return true;
				};
			}
			
			f.appendChild(t);
			
			var buttDiv = document.createElement('div');
			var okbut = new dijit.form.Button({
				'label': pub.translations('common', 'ok'),
				'onClick': okClick
			});
			if (!!form.submitName) okbut.attr('name', '' + form.submitName);
			okbut.placeAt(buttDiv);
			
			if ((parseInt(buttons) & pub.ui.buttons.CANCEL)) {
				var ccbut = new dijit.form.Button({
					'label': pub.translations('common', 'cancel'),
					'onClick': function () { dial.destroy(); }
				});
				ccbut.placeAt(buttDiv);
			}
			buttDiv.style.textAlign = 'center';
			
			f.appendChild(buttDiv);
			div.appendChild(f);
			dial.attr('content', div);
			dojo.body().appendChild(dial.domNode);
			dial.show();
			dial.containerNode.style.height = 'auto';
			buttDiv.style.textAlign = 'center';
			if (!(parseInt(buttons) & pub.ui.buttons.CANCEL))
				dial.closeButtonNode.style.display = 'none';
		},
		'toolTip': function (htmlo, label) {
			dojo.require('dijit.Tooltip');
			dijit.hideTooltip(htmlo);
			if (!!label) dijit.showTooltip('' + label, htmlo);
		},
		'createToolTip': function (htmlo, label) {
			dojo.require('dijit.Tooltip');
			if (!htmlo.id) htmlo.id = pub.generateId();
			if (label) {
				var tool = new dijit.Tooltip({
					'connectId': [htmlo.id],
					'label': label,
					'showDelay': 0
				});
				htmlo.parentNode.insertBefore(tool.domNode, htmlo.nextSibling);
			}
		},
		'dynamicToolTip': function (htmlo, label) {
			dojo.require('dijit.Tooltip');
			if (!htmlo.id) htmlo.id = pub.generateId();
			dojo.connect(htmlo, 'onmouseover', htmlo, function () {
				var labelStr = label.call(htmlo);
				if (!!labelStr) dijit.showTooltip('' + labelStr, htmlo);
			});
			dojo.connect(htmlo, 'onmouseout', htmlo, function () {
				dijit.hideTooltip(htmlo);
			});
		},
		'LayoutEditor': function (params) {
			dojo.require('dijit.layout.SplitContainer');
			dojo.require('dijit.layout.ContentPane');
			dojo.require('dijit.form.Button');
			
			var maxChild = 0;
			var htmlObject = null;
			var layoutRoot = null;
			var t = this;
			var positions = [{
				'id': 0,
				'parent': null,
				'share': 1,
				'split': params.splitRoot || 'none'
			}];
			
			this.addPosition = function (posParams) {
				posParams = posParams || {};
				var id = posParams.id ? (0 + parseInt(posParams.id)) : (maxChild + 1);
				if (maxChild < id) maxChild = id;
				if (isNaN(parseInt(posParams.parent))) id = 0;
				positions[id] = {
					'id': id,
					'parent': (id == 0) ? null : (parseInt(posParams.parent) || 0),
					'share': ((0 + parseInt(posParams.share)) || 50),
					'split': posParams.split || 'none'
				};
				return id;
			};
			
			var getChildren = function (posId) {
				var ret = [];
				for (var i in positions) {
					if (
						(i in positions) &&
						(typeof positions[i] != 'undefined') &&
						(positions[i].parent == posId)
					) {
						ret.push(positions[i]);
					}
				}
				return ret;
			};
			
			var usedEvent = null;
			var centerize = function (posId, evt) {
				if ((usedEvent != evt) && (posId in positions)) {
					for (var i in positions) {
						if ((i in positions) && (typeof positions[i] != 'undefined')) {
							if (posId == positions[i].parent)
								positions[i].share = 50;
							else
								positions[i].share = positions[i].object.widget.attr('sizeShare');
						}
					}
					t.remove();
					t.draw(htmlObject);
					return pub.consts.CANCEL_EVENT(usedEvent = evt);
				}
			};
			var removePosition = function (posId, evt) {
				if ((usedEvent != evt) && (posId in positions) && posId) {
					var parId = positions[posId].parent;
					delete positions[posId];
					if (posId == activePosition) activePosition = null;
					var children = 0;
					var child = null;
					for (var i in positions) {
						if ((i in positions) && (typeof positions[i] != 'undefined')) {
							if (parId == positions[i].parent) {
								children++;
								child = i;
							}
							positions[i].share = positions[i].object.widget.attr('sizeShare');
						}
					}
					if (children == 1) {
						delete positions[child];
						if (child == activePosition) activePosition = null;
						--children;
					}
					if (children == 0) {
						positions[parId].order = false;
						positions[parId].split = 'none';
					}
					
					t.remove();
					t.draw(htmlObject);
					return pub.consts.CANCEL_EVENT(usedEvent = evt);
				}
			};
			
			var order = 0;
			var activePosition = null;
			var makePosition = function (posId, ord) {
				var share = 50;
				var parent = positions[positions[posId].parent];
				if (!!parent && positions[posId].share) {
					share = positions[posId].share;
				}
				var content = null;
				var insertable = false;
				var onClick = function () {};
				var onDblClick = function (e) {};
				if (positions[posId].split == 'horizontal' || positions[posId].split == 'vertical') {
					content = new dijit.layout.SplitContainer({
						'orientation': positions[posId].split,
						'sizerWidth': 7,
						'activeSizing': true,
						'persist': true,
						'onDblClick': function (e) { return centerize(posId, e); }
					});
					insertable = true;
				} else {
					if (!ord) ord = ++order;
					content = document.createTextNode('#' + ord);
					onClick = function () {
						if (activePosition != null) {
							pub.removeClass(positions[activePosition].object.widget.domNode, 'selected');
							if (activePosition == posId) {
								activePosition = null;
								return;
							}
						}
						pub.addClass(this.domNode, 'selected');
						activePosition = posId;
					};
					onDblClick = function (e) { removePosition(posId, e); };
				}
				positions[posId].object = {
					'order': insertable ? false : ord,
					'addChild': insertable ? function (pos, after) {
						content.addChild(pos.object.widget, after);
					} : false,
					'getIndex': !!parent ? function () {
						var index = 0;
						pub.forEach(parent.object.widget.getChildren()[0].getChildren(), function (k, v) {
							if (v == positions[posId].object.widget) return pub.BREAK;
							index++;
						});
						return index;
					} : function () { return 0; },
					'widget': new dijit.layout.ContentPane({
						'sizeMin': 10,
						'sizeShare': share,
						'positionId': posId,
						'content': content,
						'onClick': onClick,
						'onDblClick': onDblClick
					})
				};
				return positions[posId];
			};
			
			var makeDivide = function (posId) {
				var content = new dijit.layout.SplitContainer({
					'orientation': positions[posId].split,
					'sizerWidth': 7,
					'activeSizing': true,
					'persist': true,
					'onDblClick': function (e) { return centerize(posId, e); }
				});
				positions[posId].object.addChild = function (pos, after) {
					content.addChild(pos.object.widget, after);
				};
				positions[posId].object.addChild(makePosition.call(this, this.addPosition({
					'parent': posId
				}), positions[posId].object.order));
				positions[posId].object.addChild(makePosition.call(this, this.addPosition({
					'parent': posId
				})));
				positions[posId].object.widget.attr('content', content);
				positions[posId].object.order = false;
			};
			
			var drawId = function (parentId, pw) {
				pub.forEach(getChildren(parentId), function (k, c) {
					var p = makePosition(c.id);
					pw.object.addChild(p);
					if (p.object.addChild)
						drawId(c.id, p);
				});
			};
			
			var splitPos = function (splitType) {
				if (activePosition != null) {
					var posId = activePosition;
					var parent = positions[parseInt(positions[posId].parent)];
					positions[posId].object.widget.onClick();
					if (!!parent && (parent.split == splitType) && parent.object.addChild) {
						parent.object.addChild(makePosition.call(this, newPos = this.addPosition({
							'parent': (0 + parent.id)
						})), positions[posId].object.getIndex() + 1);
					} else {
						positions[posId].split = splitType;
						makeDivide.call(this, posId);
						positions[posId].object.widget.onClick = function () {};
						positions[posId].object.widget.onDblClick = function () {};
					}
					activePosition = null;
				}
			};
			
			this.draw = function (htmlo) {
				order = 0;
				htmlObject = htmlo;
				htmlObject.innerHTML = '';
				var container = document.createElement('div');
				htmlObject.appendChild(container);
				container.style.margin = '0px auto';
				container.style.width = (params.width || 500) + 'px';
				container.className = 'LayoutEditorContainer';
				layoutRoot = makePosition(0);
				drawId(0, layoutRoot);
				layoutRoot.object.widget.placeAt(container);
				layoutRoot.object.widget.domNode.style.width = (params.width || 500) + 'px';
				layoutRoot.object.widget.domNode.style.height = (params.height || 400) + 'px';
				layoutRoot.object.widget.startup();
				var t = this;
				(new dijit.form.Button({
					'label': pub.translations('common', 'Split vertically'),
					'onClick': function () { splitPos.call(t, 'horizontal'); }
				})).placeAt(container);
				(new dijit.form.Button({
					'label': pub.translations('common', 'Split horizontally'),
					'onClick': function () { splitPos.call(t, 'vertical'); }
				})).placeAt(container);
				(new dijit.form.Button({
					'label': pub.translations('common', 'Reorder'),
					'onClick': function () { t.reOrder(); }
				})).placeAt(container);
				(new dijit.form.Button({
					'label': pub.translations('common', 'Save'),
					'onClick': function () { params.onSave(t.getPositions()); }
				})).placeAt(container);
				if (!!params.onCancel)
					(new dijit.form.Button({
						'label': pub.translations('common', 'Cancel'),
						'onClick': function () { params.onCancel(); }
					})).placeAt(container);
			};
			
			this.remove = function () {
				if (htmlObject) {
					layoutRoot.object.widget.destroy();
					htmlObject.innerHTML = '';
				}
			};
			
			this.reOrder = function () {
				for (var i in positions) {
					if ((i in positions) && (typeof positions[i] != 'undefined')) {
						positions[i].share = positions[i].object.widget.attr('sizeShare');
					}
				}
				this.remove();
				this.draw(htmlObject);
			};
			
			this.getPositions = function () {
				var r = [];
				var f = function (p) {
					var sumShare = 0;
					if (p) {
						pub.forEach(p.getChildren(), function (k, c) {
							var pos = positions[c.positionId];
							sumShare += (pos.share = pos.object.widget.attr('sizeShare'));
							var p = {
								'id': pos.id,
								'parent': pos.parent,
								'share': pos.share,
								'split': pos.split,
								'order': pos.object.order
							};
							var ind = r.length;
							r.push(p);
							r[ind].childrenShares = f(c.getChildren()[0]);
						});
					}
					return sumShare;
				};
				var pos = positions[0];
				pos.share = pos.object.widget.attr('sizeShare');
				r.push({
					'id': pos.id,
					'parent': pos.parent,
					'share': pos.share,
					'split': pos.split,
					'order': pos.object.order,
					'top': 0,
					'left': 0,
					'width': 1,
					'height': 1
				});
				r[0].childrenShares = f(layoutRoot.object.widget.getChildren()[0]);
				var pa = [];
				var p = function (pid) {
					if (pid in pa) return pa[pid];
					pub.forEach(r, function (k, v) {
						if (v.id == pid) {
							pa[pid] = v;
							return pub.BREAK;
						}
					});
					return pa[pid];
				};
				pub.forEach(r, function (k, v) {
					if (v.parent != null) {
						v.percent = v.share / p(v.parent).childrenShares;
						if (p(v.parent).split == 'vertical') {
							if (!('childTop' in p(v.parent)))
								p(v.parent).childTop = 0;
							v.height = p(v.parent).height * v.percent;
							v.width = p(v.parent).width;
							v.top = p(v.parent).top + p(v.parent).childTop;
							p(v.parent).childTop += v.height;
							v.left = p(v.parent).left;
						}
						if (p(v.parent).split == 'horizontal') {
							if (!('childLeft' in p(v.parent)))
								p(v.parent).childLeft = 0;
							v.width = p(v.parent).width * v.percent;
							v.height = p(v.parent).height;
							v.left = p(v.parent).left + p(v.parent).childLeft;
							p(v.parent).childLeft += v.width;
							v.top = p(v.parent).top;
						}
					}
				});
				return r;
			};
			
		},
		'input': {
			'validation': function (htmlo, params) {
				dojo.require('dijit.form.ValidationTextBox');
				htmlo.style.display = 'none';
				var ins = new dijit.form.ValidationTextBox({
					'value': htmlo.value ? htmlo.value : '',
					'intermediateChanges': true,
					'required': (params && typeof params.required != 'undefined') ? !!params.required : false,
					'regExp': (params && typeof params.regExp != 'undefined') ?
						params.regExp : ((params && params.isEmail) ?
						'[a-zA-Z0-9_.-]+@[a-zA-Z0-9_.-]+\\.[a-zA-Z]+' : '.*'),
					'invalidMessage': (params && typeof params.invalidMessage != 'undefined') ?
						params.invalidMessage :	(
						(params && params.isEmail) ?
						pub.translations('data', 'Invalid email format') :
						pub.translations('data', 'Invalid data format'))
				});
				if (params) {
					if (typeof params.maxlength != 'undefined')
						ins.maxlength = parseInt(params.maxlength);
				}
				ins.onChange = ins.onBlur = function () { htmlo.value = ins.attr('value'); };
				htmlo.parentNode.insertBefore(ins.domNode, htmlo.nextSibling);
				dojo.connect(ins.domNode, 'onclick', function () { if (htmlo.onclick) htmlo.onclick(); });
				htmlo.onchange = function () { ins.attr('value', htmlo.value); };
			},
			'number': function (htmlo, params) {
				dojo.require('dijit.form.NumberSpinner');
				htmlo.style.display = 'none';
				var ins = new dijit.form.NumberSpinner({
					'value': htmlo.value ? parseFloat(htmlo.value) : '',
					'intermediateChanges': true,
					'required': (params && typeof params.required != 'undefined') ? !!params.required : false,
					'constraints': {}
				});
				if (params) {
					if (typeof params.min != 'undefined')
						ins.constraints.min = parseFloat(params.min);
					if (typeof params.max != 'undefined')
						ins.constraints.max = parseFloat(params.max);
					if (typeof params.delta != 'undefined')
						ins.smallDelta = parseFloat(params.delta);
					if (typeof params.places != 'undefined')
						ins.constraints.places = parseFloat(params.places);
				}
				ins.onChange = ins.onBlur = function () {
					htmlo.value = isNaN(ins.attr('value')) ? '' : ins.attr('value');
				};
				ins.onClick = function () { if (htmlo.onclick) htmlo.onclick(); };
				htmlo.parentNode.insertBefore(ins.domNode, htmlo.nextSibling);
				htmlo.onchange = function () { ins.attr('value', htmlo.value); };
			},
			'date': function (htmlo, params) {
				dojo.require('dijit.form.DateTextBox');
				htmlo.style.display = 'none';
				var ins = new dijit.form.DateTextBox({
					'value': priv._parseIsoDate(htmlo.value),
					'intermediateChanges': true,
					'required': (params && typeof params.required != 'undefined') ? !!params.required : false,
					'constraints': {}
				});
				if (params) {
					if (typeof params.min != 'undefined')
						ins.constraints.min = priv._parseIsoDate(params.min);
					if (typeof params.max != 'undefined')
						ins.constraints.max = priv._parseIsoDate(params.max);
				}
				ins.onChange = ins.onBlur = function () { htmlo.value = priv._toIsoDate(ins.attr('value')); };
				ins.onClick = function () { if (htmlo.onclick) htmlo.onclick(); };
				htmlo.parentNode.insertBefore(ins.domNode, htmlo.nextSibling);
				htmlo.onchange = function () { ins.attr('value', priv._parseIsoDate(htmlo.value)); };
			},
			'time': function (htmlo, params) {
				dojo.require('dijit.form.TimeTextBox');
				htmlo.style.display = 'none';
				var ins = new dijit.form.TimeTextBox({
					'value': priv._parseIsoTime(htmlo.value),
					'intermediateChanges': true,
					'required': (params && typeof params.required != 'undefined') ? !!params.required : false,
					'constraints': {}
				});
				if (params) {
					if (typeof params.min != 'undefined')
						ins.constraints.min = priv._parseIsoTime(params.min);
					if (typeof params.max != 'undefined')
						ins.constraints.max = priv._parseIsoTime(params.max);
				}
				ins.onChange = ins.onBlur = function () { htmlo.value = priv._toIsoTime(ins.attr('value')); };
				ins.onClick = function () { if (htmlo.onclick) htmlo.onclick(); };
				htmlo.parentNode.insertBefore(ins.domNode, htmlo.nextSibling);
				htmlo.onchange = function () { ins.attr('value', priv._parseIsoTime(htmlo.value)); };
			},
			'datetime': function (htmlo, params) {
				dojo.require('dijit.form.DateTextBox');
				dojo.require('dijit.form.TimeTextBox');
				htmlo.style.display = 'none';
				var val = htmlo.value.split(' ');
				var constr = false;
				if (params) {
					constr = {
						'required': (typeof params.required != 'undefined') ? !!params.required : false
					};
					if (params.min) {
						constr.minDate = priv._parseIsoDate(params.min);
					}
					if (params.max) {
						constr.maxDate = priv._parseIsoDate(params.max);
					}
				}
				var insDate = new dijit.form.DateTextBox({
					'value': val.length > 1 ? priv._parseIsoDate(val[0]) : '',
					'intermediateChanges': true,
					'required': constr ? constr.required : false,
					'constraints': {}
				});
				if (constr && constr.minDate)
					insDate.constraints.min = constr.minDate;
				if (constr && constr.maxDate)
					insDate.constraints.max = constr.maxDate;
				
				var insTime = new dijit.form.TimeTextBox({
					'value': val.length > 1 ? priv._parseIsoTime(val[1]) : '',
					'intermediateChanges': true,
					'required': constr ? constr.required : false,
					'constraints': {}
				});
				
				insDate.onChange = insDate.onBlur = insTime.onChange = insTime.onBlur =	function () {
					if ((insDate.attr('value') instanceof Date) || (insTime.attr('value') instanceof Date))
						htmlo.value = '' +
							(	priv._toIsoDate(insDate.attr('value')) ?
								priv._toIsoDate(insDate.attr('value')) :
								'0000-00-00') + ' ' +
							(	priv._toIsoTime(insTime.attr('value')) ?
								priv._toIsoTime(insTime.attr('value')) :
								'00:00:00');
					else
						htmlo.value = '';
				};
				insDate.onClick = insTime.onClick = function () { if (htmlo.onclick) htmlo.onclick(); };
				htmlo.parentNode.insertBefore(insTime.domNode, htmlo.nextSibling);
				htmlo.parentNode.insertBefore(insDate.domNode, htmlo.nextSibling);
				htmlo.onchange = function () {
					var valu = htmlo.value.split(' ');
					insDate.attr('value', valu.length > 1 ? priv._parseIsoDate(valu[0]) : '');
					insTime.attr('value', valu.length > 1 ? priv._parseIsoTime(valu[1]) : '');
				};
			},
			'url': function (htmlo, params) {
				dojo.require("dijit.form.Button");
				dojo.require('dijit.form.ValidationTextBox');
				htmlo.style.display = 'none';
				var ins = new dijit.form.ValidationTextBox({
					'value': htmlo.value ? htmlo.value : '',
					'intermediateChanges': true,
					'required': (params && typeof params.required != 'undefined') ? !!params.required : false,
					'regExp': (params && !!params.relativeOnly) ? '^/?[a-zA-Z]+(?!://).*' : ((params &&
						!!params.absoluteOnly) ? '^[a-zA-Z]+://[a-zA-Z0-9]+\\.[a-zA-Z0-9\\.]+/?.*' : '.*'),
					'invalidMessage': (params && typeof params.invalidMessage != 'undefined') ?
						params.invalidMessage :	pub.translations('data', 'Invalid url format')
				});
				ins.onChange = ins.onBlur = function () { htmlo.value = ins.attr('value'); };
				htmlo.parentNode.insertBefore(ins.domNode, htmlo.nextSibling);
				dojo.connect(ins.domNode, 'onclick', function () { if (htmlo.onclick) htmlo.onclick(); });
				htmlo.onchange = function () { ins.attr('value', htmlo.value); };
				if (params && !!params.selectFrame) {
					var sf = new dijit.form.Button({
						'label': '&raquo;',
						'onClick': function () {
							pub.ui.promptUrl(null, ins.attr('value'),
								function (url) { ins.attr('value', url); });
						}
					});
					htmlo.parentNode.insertBefore(sf.domNode, ins.domNode.nextSibling);
				}
			},
			'colorPalette': function (htmlo, params) {
				dojo.require('dijit.form.Button');
				dojo.require('dijit.ColorPalette');
				htmlo.style.display = 'none';
				params = params || {};
				if (!('haveHashMark' in params))
					params.haveHashMark = ('' + htmlo.value).substr(0, 1) == '#';
				else params.haveHashMark = !!params.haveHashMark;
				if (htmlo.value == '#transparent')
					htmlo.value = 'transparent';
				var cparams = {};
				if (params && params.palette)
					cparams.palette = params.palette;
				var colPal = new dijit.ColorPalette(cparams);
				var ins = new dijit.form.DropDownButton({
					'iconClass': 'dijitEditorIcon',
					'label': (params && params.label) ? params.label : pub.translations('data', 'Color'),
					'dropDown': colPal
				});
				var look = ins.iconNode;
				if (!htmlo.value || (htmlo.value == 'transparent')) look.style.background = 'transparent';
				else look.style.background = (params.haveHashMark ? '' : '#') + htmlo.value;
				colPal.onChange = colPal.onBlur = function () {
					look.style.background = htmlo.value = ('' + colPal.attr('value'));
					if (!params.haveHashMark && ('' + htmlo.value).substr(0, 1) == '#')
						htmlo.value = ('' + htmlo.value).substr(1);
					if (typeof htmlo.onchange == 'function')
						htmlo.onchange();
				};
				ins.onClick = function () { htmlo.click(); };
				htmlo.parentNode.insertBefore(ins.domNode, htmlo.nextSibling);
			},
			'_customColorRGB': function (params) {
				dojo.require('dijit.form.Slider');
				params = params || {};
				var fromCode = function (txt, c) {
					return parseInt(('' + txt).substr(c * 2, 2), 16);
				};
				var toCode = function (num) {
					var c = parseInt(num).toString(16);
					if (c.length == 1) c = '0' + c;
					return c;
				};
				if (params.code) {
					if (('' + params.code).substr(0, 1) == '#')
						params.code = ('' + params.code).substr(1);
					params.r = fromCode(params.code, 0);
					params.g = fromCode(params.code, 1);
					params.b = fromCode(params.code, 2);
				} else {
					params.code = toCode(params.r) + toCode(params.g) + toCode(params.b);
				}
				if (!!params.id) params.id = pub.generateId();
				if (!!params.width) params.width = 400;
				var outer = document.createElement('div');
				var sliders = document.createElement('div');
				sliders.setAttribute('style', 'width: ' + (params.width + 20) + 'px;');
				var input = document.createElement('div');
				var sliderChange = function () {
					inputCode.value = toCode(rColor.attr('value')) +
						toCode(gColor.attr('value')) + toCode(bColor.attr('value'));
					sample.style.background = inputHidden.value =
						'#' + inputCode.value;
				};
				
				var sample = document.createElement('div');
				sample.setAttribute('style', 'background: #' + params.code + '; max-width: ' + (params.width + 20) + 'px;');
				sample.innerHTML = '&nbsp;';
				outer.appendChild(sample);
				
				var srrLabel = document.createElement('div');
				srrLabel.setAttribute('style', 'float: left; clear: left; margin: 2px;');
				srrLabel.appendChild(document.createTextNode('R'));
				sliders.appendChild(srrLabel);
				var rColor = new dijit.form.HorizontalSlider({
					'value': params.r,
					'minimum': 0,
					'maximum': 255,
					'discreteValues': 256,
					'intermediateChanges': true,
					'style': 'width: ' + params.width + 'px;',
					'onChange': sliderChange
				});
				sliders.appendChild(rColor.domNode);
				
				var srgLabel = document.createElement('div');
				srgLabel.setAttribute('style', 'float: left; clear: left; margin: 2px;');
				srgLabel.appendChild(document.createTextNode('G'));
				sliders.appendChild(srgLabel);
				var gColor = new dijit.form.HorizontalSlider({
					'value': params.g,
					'minimum': 0,
					'maximum': 255,
					'discreteValues': 256,
					'intermediateChanges': true,
					'style': 'width: ' + params.width + 'px;',
					'onChange': sliderChange
				});
				sliders.appendChild(gColor.domNode);
				
				var srbLabel = document.createElement('div');
				srbLabel.setAttribute('style', 'float: left; clear: left; margin: 2px;');
				srbLabel.appendChild(document.createTextNode('B'));
				sliders.appendChild(srbLabel);
				var bColor = new dijit.form.HorizontalSlider({
					'value': params.b,
					'minimum': 0,
					'maximum': 255,
					'discreteValues': 256,
					'intermediateChanges': true,
					'style': 'width: ' + params.width + 'px;',
					'onChange': sliderChange
				});
				sliders.appendChild(bColor.domNode);
				
				outer.appendChild(sliders);
				
				var label = document.createElement('label');
				var inputCode = document.createElement('input');
				inputCode.setAttribute('type', 'text');
				inputCode.setAttribute('value', params.code);
				inputCode.onchange = function () {
					rColor.attr('value', fromCode(inputCode.value, 0));
					gColor.attr('value', fromCode(inputCode.value, 1));
					bColor.attr('value', fromCode(inputCode.value, 2));
					sample.style.background = inputHidden.value =
						'#' + ('' + inputCode.value).substr(0, 6);
				};
				
				var inputHidden = document.createElement('input');
				inputHidden.setAttribute('type', 'hidden');
				inputHidden.setAttribute('value', '#' + params.code);
				
				label.appendChild(document.createTextNode('#'));
				label.appendChild(inputCode);
				input.appendChild(inputHidden);
				input.appendChild(label);
				outer.appendChild(input);
				
				return {
					'id': params.id,
					'dom': outer,
					'input': inputHidden
				};
			},
			'_customColorHSV': function (params) {
				dojo.require('dijit.form.Slider');
				params = params || {};
				var fromCode = function (txt, c) {
					return parseInt(('' + txt).substr(c * 2, 2), 16);
				};
				var toCode = function (num) {
					var c = parseInt(num).toString(16);
					if (c.length == 1) c = '0' + c;
					return c;
				};
				if (params.code) {
					if (('' + params.code).substr(0, 1) == '#')
						params.code = ('' + params.code).substr(1);
					params.colorRgb = new pub.color.Rgb(
						fromCode(params.code, 0) / 255,
						fromCode(params.code, 1) / 255,
						fromCode(params.code, 2) / 255
					);
					params.colorHsv = params.colorRgb.toHsv();
				} else {
					params.colorHsv = new pub.color.Hsv(params.h, params.s, params.v);
					params.colorRgb = params.colorHsv.toRgb();
					params.code = '' + params.colorRgb;
				}
				if (!!params.id) params.id = pub.generateId();
				if (!!params.width) params.width = 400;
				var outer = document.createElement('div');
				var sliders = document.createElement('div');
				sliders.setAttribute('style', 'width: ' + (params.width + 20) + 'px;');
				var input = document.createElement('div');
				var sliderChange = function () {
					params.colorHsv = new pub.color.Hsv(
						hColor.attr('value'),
						sColor.attr('value'),
						vColor.attr('value')
					);
					params.colorRgb = params.colorHsv.toRgb();
					sample.style.background = inputHidden.value = '' + params.colorRgb;
					inputCode.value = ('' + inputHidden.value).substr(1);
				};
				
				var sample = document.createElement('div');
				sample.setAttribute('style', 'background: #' + params.code + '; max-width: ' + (params.width + 20) + 'px;');
				sample.innerHTML = '&nbsp;';
				outer.appendChild(sample);
				
				var srhLabel = document.createElement('div');
				srhLabel.setAttribute('style', 'float: left; clear: left; margin: 2px;');
				srhLabel.appendChild(document.createTextNode('H'));
				sliders.appendChild(srhLabel);
				var hColor = new dijit.form.HorizontalSlider({
					'value': params.colorHsv.h,
					'minimum': 0,
					'maximum': 360,
					'discreteValues': 361,
					'intermediateChanges': true,
					'style': 'width: ' + params.width + 'px;',
					'onChange': sliderChange
				});
				sliders.appendChild(hColor.domNode);
				
				var srsLabel = document.createElement('div');
				srsLabel.setAttribute('style', 'float: left; clear: left; margin: 2px;');
				srsLabel.appendChild(document.createTextNode('S'));
				sliders.appendChild(srsLabel);
				var sColor = new dijit.form.HorizontalSlider({
					'value': params.colorHsv.s,
					'minimum': 0,
					'maximum': 1,
					// 'discreteValues': 0,
					'intermediateChanges': true,
					'style': 'width: ' + params.width + 'px;',
					'onChange': sliderChange
				});
				sliders.appendChild(sColor.domNode);
				
				var srvLabel = document.createElement('div');
				srvLabel.setAttribute('style', 'float: left; clear: left; margin: 2px;');
				srvLabel.appendChild(document.createTextNode('V'));
				sliders.appendChild(srvLabel);
				var vColor = new dijit.form.HorizontalSlider({
					'value': params.colorHsv.v,
					'minimum': 0,
					'maximum': 1,
					// 'discreteValues': 0,
					'intermediateChanges': true,
					'style': 'width: ' + params.width + 'px;',
					'onChange': sliderChange
				});
				sliders.appendChild(vColor.domNode);
				
				outer.appendChild(sliders);
				
				var label = document.createElement('label');
				var inputCode = document.createElement('input');
				inputCode.setAttribute('type', 'text');
				inputCode.setAttribute('value', params.code);
				inputCode.onchange = function () {
					params.colorRgb = new pub.color.Rgb(
						fromCode(inputCode.value, 0) / 255,
						fromCode(inputCode.value, 1) / 255,
						fromCode(inputCode.value, 2) / 255
					);
					params.colorHsv = params.colorRgb.toHsv();
					hColor.attr('value', params.colorHsv.h);
					sColor.attr('value', params.colorHsv.s);
					vColor.attr('value', params.colorHsv.v);
					sample.style.background = inputHidden.value =
						'#' + ('' + inputCode.value).substr(0, 6);
				};
				
				var inputHidden = document.createElement('input');
				inputHidden.setAttribute('type', 'hidden');
				inputHidden.setAttribute('value', '#' + params.code);
				
				label.appendChild(document.createTextNode('#'));
				label.appendChild(inputCode);
				input.appendChild(inputHidden);
				input.appendChild(label);
				outer.appendChild(input);
				
				return {
					'id': params.id,
					'dom': outer,
					'input': inputHidden
				};
			},
			'color': function (htmlo, params) {
				dojo.require('dijit.Dialog');
				dojo.require('dijit.form.Button');
				dojo.require('dijit.form.CheckBox');
				dojo.require('dijit.ColorPalette');
				htmlo.style.display = 'none';
				params = params || {};
				if (!('haveHashMark' in params))
					params.haveHashMark = ('' + htmlo.value).substr(0, 1) == '#';
				else params.haveHashMark = !!params.haveHashMark;
				if ((/#+transparent/).test(htmlo.value))
					htmlo.value = 'transparent';
				var cparams = { };
				if (params && params.palette)
					cparams.palette = params.palette;
				var colPal = new dijit.ColorPalette(cparams);
				var realOnChange = (typeof htmlo.onchange == 'function') ? htmlo.onchange : null;
				colPal.domNode.appendChild((new dijit.form.Button({
					'label': 'RGB',
					'onClick': function() {
						ins.closeDropDown();
						
						var dial = new dijit.Dialog({ 'title': pub.translations('data', 'Pick') + ': RGB' });
						var buttDiv = document.createElement('div');
						var divs = document.createElement('div');
						
						var cpo = pub.ui.input._customColorRGB({
							'width': 400,
							'code': (!!htmlo.value && htmlo.value != 'transparent' ? htmlo.value : 'ffffff')
						});
						
						divs.appendChild(cpo.dom);
						
						(new dijit.form.Button({
							'label': pub.translations('common', 'ok'),
							'onClick': function () {
								dial.destroy();
								look.style.background = htmlo.value = ('' + cpo.input.value);
								if (!params.haveHashMark && ('' + htmlo.value).substr(0, 1) == '#')
									htmlo.value = ('' + htmlo.value).substr(1);
								if (typeof htmlo.onchange == 'function')
									htmlo.onchange();
							}
						})).placeAt(buttDiv);
						
						(new dijit.form.Button({
							'label': pub.translations('common', 'cancel'),
							'onClick': function () { dial.destroy(); }
						})).placeAt(buttDiv);
						
						divs.appendChild(buttDiv);
						
						dial.attr('content', divs);
						dojo.body().appendChild(dial.domNode);
						dial.show();
						dial.containerNode.style.height = 'auto';
						dial.containerNode.style.maxWidth = '420px';
					}
				})).domNode);
				colPal.domNode.appendChild((new dijit.form.Button({
					'label': 'HSV',
					'onClick': function() {
						ins.closeDropDown();
						
						var dial = new dijit.Dialog({ 'title': pub.translations('data', 'Pick') + ': HSV' });
						var buttDiv = document.createElement('div');
						var divs = document.createElement('div');
						
						var cpo = pub.ui.input._customColorHSV({
							'width': 400,
							'code': (!!htmlo.value && htmlo.value != 'transparent' ? htmlo.value : 'ffffff')
						});
						
						divs.appendChild(cpo.dom);
						
						(new dijit.form.Button({
							'label': pub.translations('common', 'ok'),
							'onClick': function () {
								dial.destroy();
								look.style.background = htmlo.value = ('' + cpo.input.value);
								if (!params.haveHashMark && ('' + htmlo.value).substr(0, 1) == '#')
									htmlo.value = ('' + htmlo.value).substr(1);
								if (typeof htmlo.onchange == 'function')
									htmlo.onchange();
							}
						})).placeAt(buttDiv);
						
						(new dijit.form.Button({
							'label': pub.translations('common', 'cancel'),
							'onClick': function () { dial.destroy(); }
						})).placeAt(buttDiv);
						
						divs.appendChild(buttDiv);
						
						dial.attr('content', divs);
						dojo.body().appendChild(dial.domNode);
						dial.show();
						dial.containerNode.style.height = 'auto';
						dial.containerNode.style.maxWidth = '420px';
					}
				})).domNode);
				colPal.domNode.appendChild((new dijit.form.Button({
					'label': pub.translations('data', 'Transparent'),
					'onClick': function() {
						ins.closeDropDown();
						
						if (!htmlo.value || htmlo.value == 'transparent')
							return false;
						look.style.background = 'transparent';
						if (params.haveHashMark) htmlo.value = 'transparent';
						else htmlo.value = '';
						if (realOnChange) realOnChange();
					}
				})).domNode);
				var ins = new dijit.form.DropDownButton({
					'iconClass': 'dijitEditorIcon',
					'label': (params && params.label) ? params.label : pub.translations('data', 'Color'),
					'dropDown': colPal
				});
				var look = ins.iconNode;
				if (!htmlo.value || (htmlo.value == 'transparent')) look.style.background = 'transparent';
				else look.style.background = '#' + ('' + htmlo.value).replace(/^#+/, '');
				colPal.onChange = function () {
					look.style.background = htmlo.value = ('' + colPal.attr('value'));
					if (!params.haveHashMark && ('' + htmlo.value).substr(0, 1) == '#')
						htmlo.value = ('' + htmlo.value).substr(1);
					if (realOnChange) realOnChange();
				};
				htmlo.parentNode.insertBefore(ins.domNode, htmlo.nextSibling);
				htmlo.onchange = function() {
					if (realOnChange) realOnChange();
					look.style.background = (!!this.value ? (params.haveHashMark ? '' : '#') +
					this.value : 'transparent');
				};
			},
			'_htmlEditorInit': function (id, params, wait) {
				if (typeof tinyMCE == "object") {
					params.mode = 'exact';
					params.elements = id;
					if (!params.theme) params.theme = 'advanced';
					if (!params.plugins) params.plugins = 'paste,noneditable,contextmenu';
					if (params.emotions) params.plugins += ',emotions';
					if (!params.theme_advanced_buttons1)
						params.theme_advanced_buttons1 =
							'bold,italic,underline,strikethrough,|,link,unlink,|,undo,redo';
					if (params.emotions) params.theme_advanced_buttons1 += ',|,emotions';
					if (!params.theme_advanced_buttons2)
						params.theme_advanced_buttons2 = '';
					if (!params.theme_advanced_buttons3)
						params.theme_advanced_buttons3 = '';
					if (!params.theme_advanced_toolbar_location)
						params.theme_advanced_toolbar_location = 'top';
					if (!params.theme_advanced_toolbar_align)
						params.theme_advanced_toolbar_align = 'left';
					if (!params.extended_valid_elements)
						params.extended_valid_elements = 'hr[class|width|size|noshade],style[type],link[rel|href|type],iframe[src|name|height|width|frameborder]';
					//if (!params.file_browser_callback)
					//	params.file_browser_callback = 'ajaxfilemanager';
					if (!params.document_base_url)
						params.document_base_url = pub.consts.SITE_URL;
					if (typeof params.paste_use_dialog == 'undefined')
						params.paste_use_dialog = false;
					if (typeof params.theme_advanced_resizing == 'undefined')
						params.theme_advanced_resizing = true;
					if (typeof params.theme_advanced_resize_horizontal == 'undefined')
						params.theme_advanced_resize_horizontal = true;
					if (typeof params.apply_source_formatting == 'undefined')
						params.apply_source_formatting = true;
					if (typeof params.force_br_newlines == 'undefined')
						params.force_br_newlines = true;
					if (typeof params.force_p_newlines == 'undefined')
						params.force_p_newlines = false;
					if (typeof params.relative_urls == 'undefined')
						params.relative_urls = true;
					if (typeof params.remove_script_host == 'undefined')
						params.remove_script_host = true;
					if (typeof params.remember_last_path == 'undefined')
						params.remember_last_path = false;
					if (!params.width) params.width = 350;
					if (!params.height) params.height = 150;
					tinyMCE.init(params);
				} else {
					setTimeout(function () {
						pub.ui.input._htmlEditorInit(id, params, wait + 125);
					}, wait + 125);
				}
			},
			'htmlEditor': function (htmlo, params) {
				if (!htmlo.id) htmlo.id = pub.generateId();
				pub.ui.input._htmlEditorInit(htmlo.id, params ? params : { }, 100);
			},
			'richEditor': function (htmlo, params) {
				dojo.require("dijit.Editor");
				htmlo.style.display = "none";
				if (!params) params = {};
				var cparams = {
					'styleSheets': pub.consts.DOJO_PATH + '/dojo/resources/dojo.css'
				};
				if (!!params.plugins) {
					dojo.require("dijit._editor.plugins.AlwaysShowToolbar");
					dojo.require("dijit._editor.plugins.EnterKeyHandling");
					dojo.require("dijit._editor.plugins.FontChoice");
					dojo.require("dijit._editor.plugins.LinkDialog");
					dojo.require("dijit._editor.plugins.TabIndent");
					dojo.require("dijit._editor.plugins.TextColor");
					dojo.require("dijit._editor.plugins.ToggleDir");
					cparams.plugins = params.plugins;
				}
				var editor = new dijit.Editor(cparams);
				editor.attr('value', htmlo.value);
				editor.onChange = editor.onBlur = function () {
					htmlo.value = editor.attr('value');
					if (htmlo.onchange) htmlo.onchange();
				};
				htmlo.parentNode.insertBefore(editor.domNode, htmlo.nextSibling);
				if (params.width) editor.domNode.style.width = params.width;
				if (params.height) editor.domNode.style.height = params.height;
			}
		}
	};
	
	pub.ui.input.colorPalette = pub.ui.input.color;
	
	pub.sendForm = function (form, onLoadFunc) {
		var ifr = document.createElement('iframe');
		ifr.id = pub.generateId();
		ifr.setAttribute('style', 'display: none;');
		ifr.setAttribute('name', ifr.id);
		form.setAttribute('target', ifr.id);
		pub.$tag('body').appendChild(ifr);
		form.submit();
		ifr.onload = function () {
			var oDoc = (ifr.contentWindow || ifr.contentDocument);
			if (oDoc.document) oDoc = oDoc.document;
			if (!!onLoadFunc) onLoadFunc(oDoc.body.innerHTML);
			//pub.$tag('body').removeChild(ifr);
		};
	};
	
	priv._declareTreeEditor = function () {
		dojo.require('dijit.Tree');
		dojo.require('dijit.form.TextBox');
		dojo.declare('dijit.treeEditor', dijit.Tree, {
			'editing': false,
			'postCreate': function () {
				this.inherited(arguments);
				dojo.connect(this.domNode, 'ondblclick', this, this._onDblClick);
			},
			'onKeyPress': function (e) {
				if (this.tree.editing) return false;
				this.inherited(arguments);
			},
			'_onKeyPress': function (e) {
				if (this.tree.editing) return false;
				this.inherited(arguments);
			},
			'onClick': function (e) {
				if (this.tree.editing) return false;
				this.inherited(arguments);
			},
			'_onClick': function (e) {
				if (this.tree.editing) return false;
				this.inherited(arguments);
			},
			'_onDblClick': function (e) {
				var domElement = e.target;
				var nodeWidget = dijit.getEnclosingWidget(domElement);
				if (!nodeWidget || !nodeWidget.isTreeNode || this.editing) return false;
				this.editing = true;
				var labelNode = nodeWidget.labelNode;
				
				var editor = new dijit.form.TextBox({
					'tree': this,
					'value': labelNode.innerHTML,
					'onChange': function () {
						var val = editor.attr('value');
						this.tree.model.store.setValue(nodeWidget.item, 'title', [val]);
						this.tree.onLabelChange(nodeWidget.item);
					},
					'onBlur': function () {
						this.tree.editing = false;
						labelNode.innerHTML = editor.attr('value');
						!!editor && editor.domNode.parentNode.removeChild(editor.domNode);
					},
					'onKeyDown': function (e) {
						if (e.keyCode == '13' || e.keyCode == '10') {
							e.target.blur();
						}
					}
				});
				labelNode.parentNode.insertBefore(editor.domNode, labelNode.nextSibling);
				labelNode.innerHTML = '';
				editor.domNode.focus();
			}
		});
	};
	
	priv._declareAdvancedTreeEditor = function () {
		dojo.require('dijit.Tree');
		dojo.require('dijit.form.TextBox');
		dojo.declare('dijit._AdvancedTreeEditor', dijit.Tree, {
			'editing': false,
			'postCreate': function () {
				this.inherited(arguments);
				dojo.connect(this.domNode, 'ondblclick', this, this._onDblClick);
			},
			'onClick': function (e) {
				if (this.tree.editing) return false;
				this.inherited(arguments);
			},
			'_onClick': function (e) {
				if (this.tree.editing) return false;
				this.inherited(arguments);
			},
			'setNodeLabel': function (node, label) {
				this.tree.model.store.setValue(node.item, 'title', [label]);
				node.labelNode.innerHTML = '' + label;
			},
			'_onDblClick': function (e) {
				var domElement = e.target;
				var nodeWidget = dijit.getEnclosingWidget(domElement);
				if (!nodeWidget || !nodeWidget.isTreeNode || this.editing) return false;
				this.editing = true;
				this.onDblClick(e, nodeWidget);
				var tree = this;
				setTimeout(function () {
					tree.editing = false;
				}, 500);
			}
		});
	};
	
	priv.animate = {
		'makeCtr': function (params) {
			dojo.require('dojo._base.fx');
			var ctr = {
				'node': params.node,
				'duration': !!params.duration ? params.duration : 0.35,
				'delay': !!params.delay ? params.delay : 0,
				'repeat': parseInt(params.repeat)
			};
			if (!!params.easing && typeof params.easing != 'function') {
				dojo.require('dojo.fx.easing');
				params.easing = '' + params.easing;
				params.easing = dojo.fx.easing[params.easing];
			}
			if (typeof params.easing == 'function')
				ctr.easing = params.easing;
			if (typeof params.beforeBegin == 'function')
				ctr.beforeBegin = params.beforeBegin;
			if (typeof params.onAnimate == 'function')
				ctr.onAnimate = params.onAnimate;
			if (typeof params.onBegin == 'function')
				ctr.onBegin = params.onBegin;
			if (typeof params.onEnd == 'function')
				ctr.onEnd = params.onEnd;
			return ctr;
		}
	};
	
	pub.animate = {
		'props': function (params /** node, props, [easing], [delay]
				[duration], [repeat], [onAnimate], [beforeBegin], [onBegin], [onEnd] */) {
			var ctr = priv.animate.makeCtr(params);
			pub.forEach(params.props, function(k, v) {
				if (typeof v != 'object')
					params.props[k] = { 'end': v };
			});
			ctr.properties = params.props;
			(dojo.animateProperty(ctr)).play(!!params.delay ? parseInt(params.delay) : 0);
		},
		'fade': function (type, params/** delay, node, props, [easing],
				 [duration], [repeat], [onAnimate], [beforeBegin], [onBegin], [onEnd] */) {
			var ctr = priv.animate.makeCtr(params);
			var anim;
			if (type == 'in') {
				dojo.style(params.node, 'opacity', '0');
				anim = dojo.fadeIn(ctr);
			} else {
				dojo.style(params.node, 'opacity', '1');
				anim = dojo.fadeOut(ctr);
			}
			anim.play();
		},
		'setOpacity': function (node, opacity) {
			dojo.style(node, 'opacity', opacity);
		}
	};
	
	pub.types = {
		'Xhr': function () {
			try {
				this.xhr = new XMLHttpRequest();
				this.valid = true;
			} catch (e) { try {
				this.xhr = new ActiveXObject('Msxml2.XMLHTTP');
				this.valid = true;
			} catch (e) { try {
				this.xhr = new ActiveXObject('Microsoft.XMLHTTP');
				this.valid = true;
			} catch (e) {
				this.xhr = null;
				this.valid = false;
				pub.debug('#36: ' + e.message);
				pub.debug(e);
			} } }
		},
		'XmlParser': function () {
			/// xml.async
			/// xml.load (file)
			/// xml.loadXML (string)
			try {
				this.xml = new ActiveXObject('Microsoft.XMLDOM');
				this.valid = true;
			} catch(e) {
				try {
					this.xml = document.implementation.createDocument('', '', null);
					this.valid = true;
				} catch(e) {
					this.xml = null;
					this.valid = false;
					pub.debug('#37: ' + e.message);
					pub.debug(e);
				}
			}
		},
		'Stack': function () {
			var ar = [];
			this.push = function (item) { ar.push(item); };
			this.pop = function () { return ar.pop(); };
			this.top = function () { return ar[ar.length - 1]; };
			this.length = function () { return ar.length; };
		},
		'Queue': function () {
			var ar = [];
			this.push = function (item) { ar.push(item); };
			this.pop = function () { return ar.shift(); };
			this.top = function () { return ar[0]; };
			this.length = function () { return ar.length; };
		},
		'PriorityQueue': function () {
			var ar = [];
			this.push = function (item, prio) {
				for (var i = 0; i < ar.length; ++i) {
					if (prio >= ar[i].p) continue;
					for (var j = ar.length; i < j; --j) {
						ar[j] = ar[j - 1];
					}
					ar[i] = { 'i': item, 'p': prio };
					break;
				}
			};
			this.pop = function () { return ar.shift().i; };
			this.top = function () { return ar[0].i; };
			this.length = function () { return ar.length; };
		},
		'MenuBar': function (parent, id, label, onClick) {
			dojo.require('dijit.Menu');
			dojo.require('dijit.Toolbar');
			dojo.require('dijit.form.Button');
			
			this._id = id;
			this._parent = typeof (parent) == 'object' ? parent : null;
			this._root = parent ? false : true;
			
			var root = parent;
			while (root._parent) root = root._parent;
			
			if (this._root) {
				this._obj = new dijit.Toolbar();
				this._menus = [];
				this.draw = function (htmlo) {
					for (var i = 0; i < this._menus.length; ++i)
						this._menus[i].startup();
					this._obj.placeAt(htmlo);
				};
			} else {
				if (onClick) {
					if (this._parent._root) {
						this._obj = new dijit.form.Button({ 'id': pub.makeId(id), 'label': label });
					} else {
						this._obj = new dijit.MenuItem({ 'id': pub.makeId(id), 'label': label });
					}
					this._obj.onClick = onClick;
				} else {
					this._menu = new dijit.Menu({
						'class': 'menubar_' + pub.makeSymbol(root._id)
					});
					root._menus.push(this._menu);
					if (this._parent._root) {
						this._obj = new dijit.form.DropDownButton({
							'id': pub.makeId(id),
							'label': label,
							'dropDown': this._menu
						});
					} else {
						this._obj = new dijit.PopupMenuItem({
							'id': pub.makeId(id),
							'label': label,
							'popup': this._menu
						});
					}
				}
				if (this._parent._root) {
					this._parent._obj.addChild(this._obj);
				} else {
					this._parent._menu.addChild(this._obj);
				}
			}
		},
		'LinearGradient': function (obj, x1, y1, x2, y2, colors, params) {
			dojo.require('dojo._base.Color');
			dojo.require('dojox.gfx._base');
			dojo.require('dojox.gfx.shape');
			dojo.require('dojox.gfx.path');
			dojo.require('dojox.gfx.arc');
			dojo.require('dojox.gfx');
			dojo.require('dojo.colors');
			
			var linear = {
			    'type': 'linear',
			    'x1': x1, 'y1': y1,
			    'x2': x2, 'y2': y2,
			    'colors': colors
			};
			
			var width = obj.offsetWidth;
			var height = obj.offsetHegiht;
			
			if (typeof params == 'object') {
				if (params.width) width = params.width;
				if (params.height) height = params.height;
			}
			
			var surface = dojox.gfx.createSurface(obj, width, height);
			var group = surface.createGroup();
			var rectParams = {
				'width':  width,
				'height': height
			};
			if (typeof params == 'object') {
				if (params.round) rectParams.r = params.round;
			}
			var rect = surface.createRect(rectParams);
			rect.setFill(linear);
			if (typeof params == 'object') {
				if (params.stroke) rect.setStroke(params.stroke);
			}
			group.add(rect);
		},
		'Tree': function (params /** [id], [forest], [ordered], [onRename], [onDblClick] */) {
			dojo.require("dojo.data.ItemFileWriteStore");
			if (!!params.forest) dojo.require("dijit.tree.ForestStoreModel");
			else dojo.require("dijit.tree.TreeStoreModel");
			dojo.require("dijit.Tree");
			
			this.id = '' + params.id;
			if (!this.id) this.id = this.generateId();
			this._data = {
				'identifier': 'id',
				'label': 'title',
				'items': [ ]
			};
			
			this._hasToolTips = false;
			this._forest = !!params.forest;
			this._ordered = !!params.ordered;
			this._onRename = !!params.onRename ? params.onRename : null;
			this._onDblClick = !!params.onDblClick ? params.onDblClick : null;
			this._pendingAddNodes = [];
			
			this.addNode = function (pars /** title, [parent], [id],
					 [data], [iconClass], [position /node, at/] */) {
				var max = 0;
				if (this._data.items && this._data.items.length)
					max = this._data.items.length;
				if (!pars.id) pars.id = max;
				var pos = max;
				
				if (typeof pars.parent != "undefined" && pars.parent != null) {
					var dontHaveParent = true;
					for (var i = 0; i < max; i++) 
						if (this._data.items[i] && (this._data.items[i].id == pars.parent)) {
							dontHaveParent = false;
							break;
						}
					if (dontHaveParent) {
						if (!this._pendingAddNodes[pars.parent])
							this._pendingAddNodes[pars.parent] = [];
						this._pendingAddNodes[pars.parent].push(pars);
						return pars.id;
					}
				}
				
				var nodeVals = {
					'id': pars.id,
					'title': pars.title,
					'type': !!pars.haveChildren ? 'spring' : 'leaf',
					'parent': pars.parent,
					'data': pars.data,
					'iconClass': pars.iconClass,
					'toolTip': pars.toolTip,
					'mayHaveChildren': typeof pars.mayHaveChildren == "undefined" ?
						true : !!pars.mayHaveChildren
				};
				
				if (!!pars.toolTip) this._hasToolTips = true;
				
				if (pars.position) {
					this._data.items = pub.arrayInsert(this._data.items, nodeVals, function (item) {
						if (item.id == pars.position.node)
							return pars.position.at;
						else return false;
					});
				} else {
					this._data.items[pos] = nodeVals;
				}
				
				if (typeof pars.parent != "undefined" && pars.parent != null) {
					for (var i = 0; i < max; i++) {
						if (this._data.items[i] && (this._data.items[i].id == pars.parent)) {
							if (this._data.items[i].children) {
								if (pars.position) {
									this._data.items[i].children = pub.arrayInsert(
										this._data.items[i].children, {
											'_reference': pars.id
										}, function (child) {
											if (child._reference == pars.position.node)
												return pars.position.at;
											else return false;
										});
								} else {
									var imax = this._data.items[i].children.length;
									this._data.items[i].children[imax] = { '_reference': pars.id };
								}
							} else {
								this._data.items[i].children = [{ '_reference': pars.id }];
								if (this._data.items[i].type == 'leaf')
									this._data.items[i].type = 'spring';
							}
						}
					}
				} else {
					this._data.items[pos].type = 'top';
				}
				
				if (!!this._pendingAddNodes[pars.id]) {
					var t = this;
					pub.forEach(this._pendingAddNodes[pars.id], function (k, p) {
						t.addNode(p);
					});
					delete this._pendingAddNodes[pars.id];
				}
				
				return pars.id;
			};
			
			this.removeNode = function (removeId) {
				var tree = this;
				var node = null;
				var data = {
					'identifier': 'id',
					'label': 'title',
					'items': [ ]
				};
				var added = [null];
				pub.forEach(this._data.items, function (key, item) {
					if (item.id != removeId && (typeof item.parent == 'undefined' ||
							pub.inArray(added, item.parent))) {
						added.push(item.id);
						data.items.push({
							'id': item.id,
							'title': item.title,
							'type': item.type,
							'parent': item.parent,
							'data': item.data,
							'iconClass': item.iconClass,
							'toolTip': item.toolTip,
							'mayHaveChildren': item.mayHaveChildren
						});
						var id = item.id;
						var parent = item.parent;
						pub.forEach(data.items, function (key, item) {
							if (item.id == parent) {
								if (!data.items[key].children) data.items[key].children = [ ];
								data.items[key].children.push({ '_reference': id });
							}
						});
					}
				});
				this._data = data;
			};
			
			this.relocateNode = function (node /** id, parent, [to, at] */) {
				var tree = this;
				var data = pub.arrayRemove(this._data.items, function (item) {
					return item.id == node.id;
				});
				
				if (!!data.elem) {
					pub.forEach(data.array, function (key, item) {
						if (item.id == data.elem.parent) {
							data.array[key].children = pub.arrayRemove(data.array[key].children,
								function (child) { return child._reference == node.id; }).array;
						}
						if (item.id == node.parent) {
							if (!data.array[key].children) data.array[key].children = [ ];
							if (node.to && node.at) {
								data.array[key].children = pub.arrayInsert(data.array[key].children,
									{ '_reference': node.id }, function (child) {
										if (child._reference == node.to)
											return node.at;
										else return false;
									});
							} else
								data.array[key].children.push({ '_reference': node.id });
						}
					});
					if (node.to && node.at) {
						data.array = pub.arrayInsert(data.array, data.elem, function (el) {
							if (el.id == node.to) return node.at;
							else return false;
						});
					} else {
						data.array.push(data.elem);
					}
					this._data.items = data.array;
				}
			};
			
			this.draw = function (htmlo) {
				var t = this;
				this._dataStore = new dojo.data.ItemFileWriteStore({ 'data': pub.clone(this._data) });
				
				if (this._forest) {
					this._model = new dijit.tree.ForestStoreModel({
						'store': this._dataStore,
						'childrenAttrs': ['children'],
						'query': { 'type': 'top' },
						'labelAttr': 'title',
						'typeAttr': 'type',
						'rootId': '0',
						'rootLabel': 'root',
						'mayHaveChildren': function (item) {
							return !!item.mayHaveChildren && !!item.mayHaveChildren[0];
						}
					});
				} else {
					this._model = new dijit.tree.TreeStoreModel({
						'store': this._dataStore,
						'childrenAttrs': ['children'],
						'query': { 'type': 'top' },
						'labelAttr': 'title',
						'typeAttr': 'type',
						'mayHaveChildren': function (item) {
							return !!item.mayHaveChildren && !!item.mayHaveChildren[0];
						}
					});
				}
				
				var treeParams = {
					'id': this.id,
					'model': this._model,
					'openOnClick': true
				};
				
				if (!!params.useIcons) {
					treeParams.getIconClass = function (item, opened) {
						return '' + item.iconClass;
					};
				}
				
				if (this._forest)
					treeParams.showRoot = false;
				
				if (!!this._onDblClick) {
					if (!dijit._AdvancedTreeEditor) priv._declareAdvancedTreeEditor();
					treeParams.onDblClick = this._onDblClick;
					this._treeWidget = new dijit._AdvancedTreeEditor(treeParams);
				} else if (!!this._onRename) {
					if (!dijit.treeEditor) priv._declareTreeEditor();
					treeParams.onLabelChange = this._onRename;
					this._treeWidget = new dijit.treeEditor(treeParams);
				} else {
					this._treeWidget = new dijit.Tree(treeParams);
				}
				
				if (this._hasToolTips) {
					dojo.require('dijit.Tooltip');
					this._lastDrawnToolTipAt = null;
					this._lastDrawnToolTipTimeout = null;
					this._drawToolTips = function(e) {
						var domElement = e.target;
						var nodeWidget = dijit.getEnclosingWidget(domElement);
						if (!nodeWidget || !nodeWidget.isTreeNode) return;
						var label = '' + nodeWidget.item.toolTip;
						if (this._lastDrawnToolTipAt) {
							dijit.hideTooltip(this._lastDrawnToolTipAt);
							this._lastDrawnToolTipAt = null;
						}
						if (this._lastDrawnToolTipTimeout) {
							clearTimeout(this._lastDrawnToolTipTimeout);
							this._lastDrawnToolTipTimeout = null;
						}
						if (!!label) {
							var t = this;
							this._lastDrawnToolTipTimeout = setTimeout(function(){
								dijit.showTooltip(label, nodeWidget.contentNode);
								t._lastDrawnToolTipAt = nodeWidget.contentNode;
								t._lastDrawnToolTipTimeout = null;
							}, 400);
						}
					};
					this._hideToolTips = function(e) {
						if (this._lastDrawnToolTipTimeout) {
							clearTimeout(this._lastDrawnToolTipTimeout);
							this._lastDrawnToolTipTimeout = null;
						}
						if (this._lastDrawnToolTipAt)
							dijit.hideTooltip(this._lastDrawnToolTipAt);
						this._lastDrawnToolTipAt = null;
					};
					dojo.connect(this._treeWidget, 'onMouseMove', this, this._drawToolTips);
					dojo.connect(this._treeWidget, 'onMouseLeave', this, this._hideToolTips);
				}
				
				this._treeWidget.placeAt(htmlo);
				this._treeWidget.startup();
			};
			
			this.remove = function () {
				this._treeWidget.destroy();
			};
			
			this._getItemByNode = function (mainBranch, node) {
				if (mainBranch.contentNode == node)
					return mainBranch.item;
				
				var branches = mainBranch.getChildren();
				var ret;
				for (var i = 0; i < branches.length; i++) {
					if (ret = this._getItemByNode(branches[i], node))
						return ret;
				}
				return false;
			};
			
			this.getItemByNode = function (node) {
				return this._getItemByNode(this._treeWidget.rootNode, node);
			};
			
			this.repair = function (mainBranch) {
				if (!mainBranch) {
					mainBranch = this._treeWidget.rootNode;
					mainBranch.item.type = 'top';
					mainBranch.item.parent = '';
				} else {
					mainBranch.item.type = 'spring';
				}
				var branches = mainBranch.getChildren();
				for (var i = 0; i < branches.length; i++) {
					branches[i].item.parent = mainBranch.item.id;
					if (!!params.forest && (mainBranch.item.id == '0'))
						branches[i].item.type = 'top';
					this.repair(branches[i]);
				}
				if (!branches)
					mainBranch.item.type = 'leaf';
			};
		}
		
	};
	
	priv.dnd = {
		'_inited': false,
		'_connected': [ ],
		'_areas': { },
		'_init': function () {
			dojo.subscribe('/dnd/drop', function (source, nodes, copy) {
				var target = dojo.dnd.manager().target;
				var sid = (source && source.node) ? source.node.id :
					((source && source.domNode) ? source.domNode.id : null);
				var tid = (target && target.node) ? target.node.id :
					((target && target.domNode) ? target.domNode.id : null);
				var n = [ ]; var p = { };
				if (source.tree) {
					pub.forEach(nodes, function (key, it) {
						n.push(dijit.getEnclosingWidget(it));
					});
				} else {
					n = nodes;
				}
				if (target.tree) {
					p.dropPosition = target.dropPosition;
					p.dropAt = dijit.getEnclosingWidget(target.targetAnchor);
				}
				var called = false;
				var dndAnswer = true;
				pub.forEach(priv.dnd._connected, function (key, it) {
					if (it.source == sid && it.target == tid) {
						called = true;
						dndAnswer = it.dndFunc(n, p);
						return pub.BREAK;
					}
				});
				pub.assert(called, objectName + '.dnd: Uncovered dnd; from: ' + sid + '; to: ' + tid);
			});
			dojo.subscribe('/dnd/source/over', function (source) {
				var target = dojo.dnd.manager().target;
				var sid = (source && source.node) ? source.node.id :
					((source && source.domNode) ? source.domNode.id : null);
				var tid = (target && target.node) ? target.node.id :
					((target && target.domNode) ? target.domNode.id : null);
				if (!!priv.dnd._areas[sid] && !!priv.dnd._areas[tid])
					dojo.dnd.manager().canDrop(!pub.inArray(
						priv.dnd._areas[tid]._accept,
						priv.dnd._areas[sid]._provide
					));
			});
			priv.dnd._inited = true;
		}
	};
	
	pub.dnd = {
		'Area': function (o, params /** [targetOnly], [copy], [accept], [provide], [creator] */) {
			dojo.require('dojo.dnd.Source');
			if (!priv.dnd._inited) priv.dnd._init();
			
			this._isDnd = true;
			this._obj = o;
			if (!o.id) o.id = pub.generateId();
			this._id = o.id;
			this._targetOnly = params ? (!!params.targetOnly) : false;
			this._copy = params ? (!!params.copy) : false;
			this._accept = params && (params.accept instanceof Array) ? params.accept : ['text'];
			this._provide = params && !!params.provide ? '' + params.provide : 'text';
			this._creator = params && !!params.creator ? params.creator : function (str) {
				return { 'text': str, 'data': str };
			};
			this.blocked = false;
			this.block = function () { this.blocked = true; };
			this.release = function () { this.blocked = false; };
			
			var selfAccept = pub.inArray(this._accept, this._provide);
			var area = this;
			
			if (o && !!o._treeWidget) {
				dojo.require('dijit.tree.dndSource');
				
				this._isTree = true;
				var dndClass = this._targetOnly ? dijit.tree.dndTarget : dijit.tree.dndSource;
				this._dndSrc = new dndClass(o._treeWidget, {
					'accept': area._accept,
					'type': area._provide,
					'allowBetween': !!o._ordered,
					'betweenThreshold': o._ordered ? 5 : 0,
					'copyOnly': area._copy,
					'selfAccept': selfAccept,
					'checkAcceptance': function (source, nodes) {
						return !area.blocked && !!this.accept[source.type];
					},
					'deleteSelectedNodes': function () {
						this._removeSelection();
						return this;
					},
					'checkItemAcceptance': function (node, source, pos) {
						var titem = dijit.getEnclosingWidget(node).item;
						var snTop = o._forest || ((!!source.tree) ?
							dijit.getEnclosingWidget(source.anchor).item.type != 'top' : true);
						return !area.blocked && (
							!!titem.mayHaveChildren && !!titem.mayHaveChildren[0] || pos != 'over'
						) && snTop;
					}
				});
				
				if (o._forest) {
					o._model.onAddToRoot = function (item) {
						o._dataStore.setValue(item, 'type', ['top']);
						o._dataStore.setValue(item, 'parent', ['0']);
						o._model._requeryTop();
						o._treeWidget.buildRendering();
						return true;
					};
					
					o._model.onLeaveRoot = function (item) {
						o._dataStore.setValue(item, 'type', [(item.children &&
							item.children.length > 0) ? 'spring' : 'leaf']);
						o._model._requeryTop();
						o._treeWidget.buildRendering();
						return true;
					};
				}
				
			} else {
				var dndClass = this._targetOnly ? dojo.dnd.Target : dojo.dnd.Source;
				this._dndSrc = new dndClass(o, {
					'accept': area._accept,
					'type': area._provide,
					'copyOnly': area._copy,
					'selfAccept': selfAccept,
					'creator': function (item, hint) {
						var c = area._creator('' + item);
						var n = document.createElement('div'); n.innerHTML = c.text;
						var d = document.createElement('div'); d.innerHTML = c.data;
						return {
							'node': n,
							'data': d,
							'type': [area._provide]
						};
					},
					'deleteSelectedNodes': function () {
						this._removeSelection();
						return this;
					},
					'checkAcceptance': function (source, nodes) {
						return !area.blocked && !!this.accept[source.type];
					}
				});
			}
			if (typeof this._dndSrc.startup == 'function')
				this._dndSrc.startup();
			priv.dnd._areas[this._id] = this;
		},
		'connect': function (source, target, onDnd) {
			if (!source._isDnd) source = new pub.dnd.Area(source, { });
			if (!target._isDnd) target = new pub.dnd.Area(target, { });
			
			priv.dnd._connected.push({
				'source': source._id,
				'target': target._id,
				'dndFunc': onDnd
			});
		}
	};
	
	return window[objectName] = pub;
})();
