/*
 * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
 * Digest Algorithm, as defined in RFC 1321.
 * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
 * Distributed under the BSD License
 * See http://pajhome.org.uk/crypt/md5 for more info.
 */

/*
 * Configurable variables. You may need to tweak these to be compatible with
 * the server-side, but the defaults work in most cases.
 */
var hexcase = 0;  /* hex output format. 0 - lowercase; 1 - uppercase        */
var b64pad  = ""; /* base-64 pad character. "=" for strict RFC compliance   */
var chrsz   = 8;  /* bits per input character. 8 - ASCII; 16 - Unicode      */

/*
 * These are the functions you'll usually want to call
 * They take string arguments and return either hex or base-64 encoded strings
 */
function hex_md5(s){ return binl2hex(core_md5(str2binl(s), s.length * chrsz));}
function b64_md5(s){ return binl2b64(core_md5(str2binl(s), s.length * chrsz));}
function str_md5(s){ return binl2str(core_md5(str2binl(s), s.length * chrsz));}
function hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); }
function b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); }
function str_hmac_md5(key, data) { return binl2str(core_hmac_md5(key, data)); }

/*
 * Perform a simple self-test to see if the VM is working
 */
function md5_vm_test()
{
  return hex_md5("abc") == "900150983cd24fb0d6963f7d28e17f72";
}

/*
 * Calculate the MD5 of an array of little-endian words, and a bit length
 */
function core_md5(x, len)
{
  /* append padding */
  x[len >> 5] |= 0x80 << ((len) % 32);
  x[(((len + 64) >>> 9) << 4) + 14] = len;

  var a =  1732584193;
  var b = -271733879;
  var c = -1732584194;
  var d =  271733878;

  for(var i = 0; i < x.length; i += 16)
  {
    var olda = a;
    var oldb = b;
    var oldc = c;
    var oldd = d;

    a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
    d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
    c = md5_ff(c, d, a, b, x[i+ 2], 17,  606105819);
    b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
    a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
    d = md5_ff(d, a, b, c, x[i+ 5], 12,  1200080426);
    c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
    b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
    a = md5_ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
    d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
    c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
    b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
    a = md5_ff(a, b, c, d, x[i+12], 7 ,  1804603682);
    d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
    c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
    b = md5_ff(b, c, d, a, x[i+15], 22,  1236535329);

    a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
    d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
    c = md5_gg(c, d, a, b, x[i+11], 14,  643717713);
    b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
    a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
    d = md5_gg(d, a, b, c, x[i+10], 9 ,  38016083);
    c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
    b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
    a = md5_gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
    d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
    c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
    b = md5_gg(b, c, d, a, x[i+ 8], 20,  1163531501);
    a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
    d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
    c = md5_gg(c, d, a, b, x[i+ 7], 14,  1735328473);
    b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);

    a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
    d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
    c = md5_hh(c, d, a, b, x[i+11], 16,  1839030562);
    b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
    a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
    d = md5_hh(d, a, b, c, x[i+ 4], 11,  1272893353);
    c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
    b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
    a = md5_hh(a, b, c, d, x[i+13], 4 ,  681279174);
    d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
    c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
    b = md5_hh(b, c, d, a, x[i+ 6], 23,  76029189);
    a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
    d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
    c = md5_hh(c, d, a, b, x[i+15], 16,  530742520);
    b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);

    a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
    d = md5_ii(d, a, b, c, x[i+ 7], 10,  1126891415);
    c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
    b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
    a = md5_ii(a, b, c, d, x[i+12], 6 ,  1700485571);
    d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
    c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
    b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
    a = md5_ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
    d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
    c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
    b = md5_ii(b, c, d, a, x[i+13], 21,  1309151649);
    a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
    d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
    c = md5_ii(c, d, a, b, x[i+ 2], 15,  718787259);
    b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);

    a = safe_add(a, olda);
    b = safe_add(b, oldb);
    c = safe_add(c, oldc);
    d = safe_add(d, oldd);
  }
  return Array(a, b, c, d);

}

/*
 * These functions implement the four basic operations the algorithm uses.
 */
function md5_cmn(q, a, b, x, s, t)
{
  return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
}
function md5_ff(a, b, c, d, x, s, t)
{
  return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
}
function md5_gg(a, b, c, d, x, s, t)
{
  return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
}
function md5_hh(a, b, c, d, x, s, t)
{
  return md5_cmn(b ^ c ^ d, a, b, x, s, t);
}
function md5_ii(a, b, c, d, x, s, t)
{
  return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
}

/*
 * Calculate the HMAC-MD5, of a key and some data
 */
function core_hmac_md5(key, data)
{
  var bkey = str2binl(key);
  if(bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz);

  var ipad = Array(16), opad = Array(16);
  for(var i = 0; i < 16; i++)
  {
    ipad[i] = bkey[i] ^ 0x36363636;
    opad[i] = bkey[i] ^ 0x5C5C5C5C;
  }

  var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz);
  return core_md5(opad.concat(hash), 512 + 128);
}

/*
 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
 * to work around bugs in some JS interpreters.
 */
function safe_add(x, y)
{
  var lsw = (x & 0xFFFF) + (y & 0xFFFF);
  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
  return (msw << 16) | (lsw & 0xFFFF);
}

/*
 * Bitwise rotate a 32-bit number to the left.
 */
function bit_rol(num, cnt)
{
  return (num << cnt) | (num >>> (32 - cnt));
}

/*
 * Convert a string to an array of little-endian words
 * If chrsz is ASCII, characters >255 have their hi-byte silently ignored.
 */
function str2binl(str)
{
  var bin = Array();
  var mask = (1 << chrsz) - 1;
  for(var i = 0; i < str.length * chrsz; i += chrsz)
    bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);
  return bin;
}

/*
 * Convert an array of little-endian words to a string
 */
function binl2str(bin)
{
  var str = "";
  var mask = (1 << chrsz) - 1;
  for(var i = 0; i < bin.length * 32; i += chrsz)
    str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask);
  return str;
}

/*
 * Convert an array of little-endian words to a hex string.
 */
function binl2hex(binarray)
{
  var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
  var str = "";
  for(var i = 0; i < binarray.length * 4; i++)
  {
    str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +
           hex_tab.charAt((binarray[i>>2] >> ((i%4)*8  )) & 0xF);
  }
  return str;
}

/*
 * Convert an array of little-endian words to a base-64 string
 */
function binl2b64(binarray)
{
  var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  var str = "";
  for(var i = 0; i < binarray.length * 4; i += 3)
  {
    var triplet = (((binarray[i   >> 2] >> 8 * ( i   %4)) & 0xFF) << 16)
                | (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 )
                |  ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF);
    for(var j = 0; j < 4; j++)
    {
      if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;
      else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
    }
  }
  return str;
}

// OneCode Application Framework | Copyright (C) 2005-2009 Radek Tetik | www.onecode.cz

Prototype.Browser.IE6 = Prototype.Browser.IE && parseInt(navigator.userAgent.substring(navigator.userAgent.indexOf("MSIE")+5)) == 6;
Prototype.Browser.IE7 = Prototype.Browser.IE && parseInt(navigator.userAgent.substring(navigator.userAgent.indexOf("MSIE")+5)) == 7;
Prototype.Browser.IE8 = Prototype.Browser.IE && !Prototype.Browser.IE6 && !Prototype.Browser.IE7;

/**
* Inheritance, traits, dynamic loading etc.
*/
var Wsf =
{
    /**
    * From the book Pro Javascript Design Patterns, page 43.
    */
    extend: function (subClass, superClass)
    {
        var F = function() {};
        F.prototype = superClass.prototype;
        subClass.prototype = new F();
        subClass.prototype.constructor = subClass;
    },

    use: function (class_, trait)
    {
        for (o in trait) class_.prototype[o] = trait[o];
    },

    requireClass: function (className)
    {
        if (eval("typeof "+className) != "undefined") return;

        var url = className.replace(/\_/g, '/');
        var code = null;

        Wsf.requireScript("/"+url.substr(1)+".js");
    },

    requireScript: function (file)
    {
        var code = false;

        var r = new Ajax.Request(file, {
            method: "get",
            asynchronous: false,
            onSuccess: function(transport) { code = transport.responseText; }
        });

        if (!code) { alert('Error loading class. Please restart the application by refreshing the page (F5).'); return; }

        var script = document.createElement('script');
        script.type = 'text/javascript';
        script.text = code;
        document.body.appendChild(script);
    },

    requireStylesheet: function (file)
    {
        var code = false;

        var r = new Ajax.Request(file, {
            method: "get",
            asynchronous: false,
            onSuccess: function(transport) { code = transport.responseText; }
        });

        if (!code) { alert('Error loading stylesheet. Please restart the application by refreshing the page (F5).'); return; }

        var e = document.createElement('style');
        e.setAttribute('type', 'text/css');

        if (e.styleSheet)
            e.styleSheet.cssText = code;
        else
            e.appendChild(document.createTextNode(code));

        document.getElementsByTagName("head")[0].appendChild(e);
    },

    sprintf: function ( ) {
        // Return a formatted string
        //
        // version: 909.322
        // discuss at: http://phpjs.org/functions/sprintf
        // +   original by: Ash Searle (http://hexmen.com/blog/)
        // + namespaced by: Michael White (http://getsprink.com)
        // +    tweaked by: Jack
        // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
        // +      input by: Paulo Ricardo F. Santos
        // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
        // +      input by: Brett Zamir (http://brett-zamir.me)
        // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
        // *     example 1: sprintf("%01.2f", 123.1);
        // *     returns 1: 123.10
        // *     example 2: sprintf("[%10s]", 'monkey');
        // *     returns 2: '[    monkey]'
        // *     example 3: sprintf("[%'#10s]", 'monkey');
        // *     returns 3: '[####monkey]'
        var regex = /%%|%(\d+\$)?([-+\'#0 ]*)(\*\d+\$|\*|\d+)?(\.(\*\d+\$|\*|\d+))?([scboxXuidfegEG])/g;
        var a = arguments, i = 0, format = a[i++];

        // pad()
        var pad = function (str, len, chr, leftJustify) {
            if (!chr) {chr = ' ';}
            var padding = (str.length >= len) ? '' : Array(1 + len - str.length >>> 0).join(chr);
            return leftJustify ? str + padding : padding + str;
        };

        // justify()
        var justify = function (value, prefix, leftJustify, minWidth, zeroPad, customPadChar) {
            var diff = minWidth - value.length;
            if (diff > 0) {
                if (leftJustify || !zeroPad) {
                    value = pad(value, minWidth, customPadChar, leftJustify);
                } else {
                    value = value.slice(0, prefix.length) + pad('', diff, '0', true) + value.slice(prefix.length);
                }
            }
            return value;
        };

        // formatBaseX()
        var formatBaseX = function (value, base, prefix, leftJustify, minWidth, precision, zeroPad) {
            // Note: casts negative numbers to positive ones
            var number = value >>> 0;
            prefix = prefix && number && {'2': '0b', '8': '0', '16': '0x'}[base] || '';
            value = prefix + pad(number.toString(base), precision || 0, '0', false);
            return justify(value, prefix, leftJustify, minWidth, zeroPad);
        };

        // formatString()
        var formatString = function (value, leftJustify, minWidth, precision, zeroPad, customPadChar) {
            if (precision != null) {
                value = value.slice(0, precision);
            }
            return justify(value, '', leftJustify, minWidth, zeroPad, customPadChar);
        };

        // doFormat()
        var doFormat = function (substring, valueIndex, flags, minWidth, _, precision, type) {
            var number;
            var prefix;
            var method;
            var textTransform;
            var value;

            if (substring == '%%') {return '%';}

            // parse flags
            var leftJustify = false, positivePrefix = '', zeroPad = false, prefixBaseX = false, customPadChar = ' ';
            var flagsl = flags.length;
            for (var j = 0; flags && j < flagsl; j++) {
                switch (flags.charAt(j)) {
                    case ' ': positivePrefix = ' '; break;
                    case '+': positivePrefix = '+'; break;
                    case '-': leftJustify = true; break;
                    case "'": customPadChar = flags.charAt(j+1); break;
                    case '0': zeroPad = true; break;
                    case '#': prefixBaseX = true; break;
                }
            }

            // parameters may be null, undefined, empty-string or real valued
            // we want to ignore null, undefined and empty-string values
            if (!minWidth) {
                minWidth = 0;
            } else if (minWidth == '*') {
                minWidth = +a[i++];
            } else if (minWidth.charAt(0) == '*') {
                minWidth = +a[minWidth.slice(1, -1)];
            } else {
                minWidth = +minWidth;
            }

            // Note: undocumented perl feature:
            if (minWidth < 0) {
                minWidth = -minWidth;
                leftJustify = true;
            }

            if (!isFinite(minWidth)) {
                throw new Error('sprintf: (minimum-)width must be finite');
            }

            if (!precision) {
                precision = 'fFeE'.indexOf(type) > -1 ? 6 : (type == 'd') ? 0 : undefined;
            } else if (precision == '*') {
                precision = +a[i++];
            } else if (precision.charAt(0) == '*') {
                precision = +a[precision.slice(1, -1)];
            } else {
                precision = +precision;
            }

            // grab value using valueIndex if required?
            value = valueIndex ? a[valueIndex.slice(0, -1)] : a[i++];

            switch (type) {
                case 's': return formatString(String(value), leftJustify, minWidth, precision, zeroPad, customPadChar);
                case 'c': return formatString(String.fromCharCode(+value), leftJustify, minWidth, precision, zeroPad);
                case 'b': return formatBaseX(value, 2, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
                case 'o': return formatBaseX(value, 8, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
                case 'x': return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
                case 'X': return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad).toUpperCase();
                case 'u': return formatBaseX(value, 10, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
                case 'i':
                case 'd':
                    number = parseInt(+value, 10);
                    prefix = number < 0 ? '-' : positivePrefix;
                    value = prefix + pad(String(Math.abs(number)), precision, '0', false);
                    return justify(value, prefix, leftJustify, minWidth, zeroPad);
                case 'e':
                case 'E':
                case 'f':
                case 'F':
                case 'g':
                case 'G':
                    number = +value;
                    prefix = number < 0 ? '-' : positivePrefix;
                    method = ['toExponential', 'toFixed', 'toPrecision']['efg'.indexOf(type.toLowerCase())];
                    textTransform = ['toString', 'toUpperCase']['eEfFgG'.indexOf(type) % 2];
                    value = prefix + Math.abs(number)[method](precision);
                    return justify(value, prefix, leftJustify, minWidth, zeroPad)[textTransform]();
                default: return substring;
            }
        };

        return format.replace(regex, doFormat);
    }
}

/**
* DOM element extensions
*/
var WsfElement =
{
    /**
    * Sets the position and optionally sets the content size.
    */
    move: function(element,x,y,w,h) {
        element.style.left = x + "px";
        element.style.top = y + "px";
        if (w !== null) element.style.width = w + "px";
        if (h !== null) element.style.height = h + "px";
    },

    /**
    * Sets the content size (=not including padding and border)
    */
    setContentSize: function(element,w,h) {
        if (w !== null) element.style.width = w + "px";
        if (h !== null) element.style.height = h + "px";
    },

    getTextContent: function (element) {
        return Prototype.Browser.IE ? element.innerText : element.textContent;
    },

    setTextContent: function (element, text) {
        if (Prototype.Browser.IE)
            element.innerText = text;
        else
            element.textContent = text;
    }
}

Element.addMethods(WsfElement);

/**
* Events trait
*/
var Wsf_EventsTrait = {
    attachEventHandler:  function (eventName, fnCallback)
    {
        if (this._events[eventName] == undefined) { this._events[eventName] = []; }
        this._events[eventName].push(fnCallback);
    },

    /**
    * @param eventData    an object { sender } plus custom properties
    */
    raiseEvent: function (eventName, eventData)
    {
        if (this._events[eventName] == undefined) return;
        if (!eventData) eventData = {};
        eventData.sender = this;

        for (var i=this._events[eventName].length; i--;) {
            this._events[eventName][i](eventData);
        }
    }
};

/*****************************************************************************/
function Wsf_App(state)
{
    this._ctrls = {};
    this._rootCtrl = null;
    this.lang = state.lang;
    this._deletedServerCtrls = [];
    this._nextCtrlId = state._nextCtrlId;
    this._nextServerCtrlId = this._nextCtrlId;

    // RSH

    if (window.dhtmlHistory) {
        window.dhtmlHistory.create({
            toJSON: function(o) { return Object.toJSON(o); },
            fromJSON: function(s) { return s.evalJSON(); }
        });
        window.dhtmlHistory.initialize();
    }

    // ...

    document.observe('dom:loaded', this.onDomReady.bind(this));
}

/*****************************************************************************/
Wsf_App.prototype.onDomReady = function()
{
    if (Prototype.Browser.IE6) $(document.body).addClassName('ie6');
    if (Prototype.Browser.Opera) $(document.body).addClassName('opera');

    // Init controls

    var ctrls = this._createCtrlTree();

    for (var id in ctrls) {
        this._ctrls[id] = ctrls[id][0];
    }

    for (var id in ctrls) {
        this._ctrls[id].createFromServerState(ctrls[id][1]);
    }

    this._rootCtrl._makeStateSnapshot();
    this._rootCtrl.initFromMarkup();
    this._rootCtrl.onCreate();

    // Resize

    Event.observe(window, 'resize', this._onResize.bindAsEventListener(this));
    this._onResize();

    // In chrome and IE CSS is not ready in the domready event, so update the the layout in the load event

    Event.observe(window, 'load', this._onResize.bind(this));

    // Start automatic UI update

    this._rootCtrl.updateUI();
    this._startUpdateUI();
}

/*****************************************************************************/
Wsf_App.prototype._startUpdateUI = function()
{
    if (!this._updateUIExecuter) {
        this._updateUIExecuter = new PeriodicalExecuter(this._rootCtrl.updateUI.bind(this._rootCtrl), .25);
    }
}

/*****************************************************************************/
Wsf_App.prototype._stopUpdateUI = function()
{
    if (this._updateUIExecuter) {
        this._updateUIExecuter.stop();
        this._updateUIExecuter = null;
    }
}

/**
* Sets a cookie.
* The valus is encoded using encodeURIComponent(). You must use urldecode() in PHP.
* @param expireDate       Date object or null for session duration
*/
Wsf_App.prototype.setCookie = function(name, value, expireDate, isGlobal)
{
    var s = name + "=" + encodeURIComponent(value);
    if (expireDate !== null) s += "; expires=" + expireDate.toGMTString();
    if (isGlobal) s += "; path" + "=/";      // to make HTTRACK dizzy ;-)
    document.cookie = s;
}

/*****************************************************************************/
Wsf_App.prototype.getCookie = function (name, defValue)
{
    // cookies are separated by semicolons
    // IE bug: cookies in total can have up-to 4096KB
    var cookies = document.cookie.split("; ");

    for (var i=0; i < cookies.length; i++) {
        // a name/value pair (a crumb) is separated by an equal sign
        var aCrumb = cookies[i].split("=");
        if (name == aCrumb[0]) return unescape(aCrumb[1]);
    }

    // a cookie with the requested name does not exist
    return defValue;
}

/*****************************************************************************/
Wsf_App.prototype.getCookieInt = function (name, defValue)
{
    return parseInt(this.getCookie(name, defValue));
}

/*****************************************************************************/
Wsf_App.prototype.refresh = function ()
{
    window.location.reload();
}

/*****************************************************************************/
Wsf_App.prototype.navigate = function (url)
{
    window.location.href = url;
}

/*******************************************************************************/
/*
/* CONTROLS
/*
/*******************************************************************************/

/*****************************************************************************/
Wsf_App.prototype.getRootCtrl = function()
{
    return this._rootCtrl;
}

/*****************************************************************************/
Wsf_App.prototype._registerCtrl = function (id, ctrl)
{
    this._ctrls[id] = ctrl;
}

/*****************************************************************************/
Wsf_App.prototype._deregisterCtrl = function (ctrl)
{
    var id = ctrl.getId();
    delete this._ctrls[id];

    if (id.substr(1) < this._nextServerCtrlId) {
        this._deletedServerCtrls.push(id);
    }
}

/*****************************************************************************/
Wsf_App.prototype.getCtrlById = function (id)
{
    return id ? this._ctrls[id] : null;
}

/*******************************************************************************/
/*
/* CMDS
/*
/*******************************************************************************/

/*******************************************************************************/
Wsf_App.prototype.updateUI = function ()
{
    this._rootCtrl.updateUI();
}

/*******************************************************************************/
Wsf_App.prototype.invokeUpdateCmd = function (sourceCtrl, cmdId, CmdUI)
{
    var method = "onUpdate" + cmdId;

    var a = sourceCtrl.getCmdTarget();

    for (; a; a=a.getParent()) {
        if (a[method] != undefined) {
            a[method](CmdUI);
            break;
        }
        else if (a.onUpdateCmdUI(cmdId, CmdUI))
            break;
    }
}

/*******************************************************************************/
Wsf_App.prototype.invokeCmd = function (sourceCtrl, cmdId, params)
{
    // Ensure cmd is enabled

    var cmdUI = {
        isEnabled: true,
        enable: function (isEnabled) { this.isEnabled = isEnabled; },
        check: function (isChecked) { }
    };

    this.invokeUpdateCmd(sourceCtrl, cmdId, cmdUI);
    if (!cmdUI.isEnabled) return;

    // Handle on client
    // Try concrete OnXYZ() handler and then generic OnCommand() handler

    var method = "on" + cmdId;
    var cmdTarget = sourceCtrl.getCmdTarget();
    params.sender = sourceCtrl;
    params.cmdId = cmdId;

    for (var a=cmdTarget; a; a=a.getParent()) {
        if (a[method] != undefined) {
            if (a[method](params) !== false) return;
            break;
        }
        else if (a['onCommand'] != undefined) {
            if (a.onCommand(params) !== false) return;
        }
    }

    // Handle on server

    delete params.sender;
    delete params.cmdId;

    Wsf_Web_CallServerMethodHandler.invoke("Wsf_Web_MVC_CtrlManager", "_HandleCommand",
        { cmdId: cmdId, cmdParams: params, srcCtrlId: cmdTarget.getId() },
        null, null,
        false);
}

/*******************************************************************************/
/*
/* EVENTS
/*
/*******************************************************************************/

Wsf_App.prototype._onResize = function (ev)
{
    if (this._inResize) return;   // IE calls resize multiple times
    this._inResize = true;
    this._rootCtrl.onSize();
    this._inResize = false;
}


var Wsf_Web_MVC_ControlStateCoder = {

    /*******************************************************************************/
    decodeStateVariable : function (value)
    {
        if (value === null) {
            return value;
        }
        else if (typeof(value) == "string") {
            return value.charAt(0) == '*' ? value.substr(1) : g_app.getCtrlById(value);
        }
        else if (typeof(value) == "object") {
            if (Object.isArray(value)) {
                for (var i=0; i < value.length; ++i) {
                    value[i] = this.decodeStateVariable(value[i]);
                }
                return value;
            }
            else {
                for (var name in value) {
                    value[name] = this.decodeStateVariable(value[name]);
                }
                return value;
            }
        }
        else {
            return value;
        }
    },

    /*******************************************************************************/
    encodeVariable : function (value)
    {
        if (value === null) {
            return value;
        }
        else if (typeof(value) == "object") {
            if (value.isA) {
                return value.getId();
            }
            else if (Object.isArray(value)) {
                var newArray = [];
                for (var i=0; i < value.length; ++i) {
                    newArray[i] = this.encodeVariable(value[i]);
                }
                return newArray;
            }
            else {
                var newObj = {};
                for (var name in value) {
                    newObj[name] = this.encodeVariable(value[name]);
                }
                return newObj;
            }
        }
        else if (typeof(value) == "string") {
            return "*"+value;
        }
        else {
            return value;
        }
    }
};


/**
* Command handling:
* - define concrete handler OnXYZ(params)
*   Return TRUE or nothing if handled or FALSE to handle on the server
* - define generic handler OnCommand(eventData), where eventData = { cmdId, sender, params1, param2, etc. }
*   Return TRUE if handled or FALSE to continue routing.
*/

/*******************************************************************************/
function Wsf_Ctrls_Control()
{
    this._classes = ['Wsf_Ctrls_Control'];
    this._offsetWidth = null;
    this._offsetHeight = null;
    this._clientWidth = null;
    this._clientHeight = null;
    this._events = {};
    this._children = [];
    this._serverState = {};
    this._positionFixed = false;
    this._positionSet = false;
}

Wsf_Ctrls_Control.EVENT_AFTER_DELETE =  100001;

Wsf.use(Wsf_Ctrls_Control, Wsf_EventsTrait);

/*******************************************************************************/
Wsf_Ctrls_Control.prototype.createFromServerState = function (serverState)
{
    for (var name in serverState) this._serverState[name] = null;
    this._loadState(serverState);

    if (this._parent)
        this._parent._children.push(this);
    else
        g_app._rootCtrl = this;
}

/**
* Init control from markup. May be called multiple times during the life of the control.
*/
Wsf_Ctrls_Control.prototype.initFromMarkup = function ()
{
    this.elem = $(this._id);
    if (!this.elem) console.log(this._id, " not found");
    this.elem.WsfCtrl = this;     // Got error? The control has not been rendered

    this._layout = this._childrenLayout ? eval('new '+this._childrenLayout+'(this);') : null;
    if (!this._enabled) this._enableInternal(false);

    if (this._layout) this.elem.style.overflow = "hidden";

    for (var i=0,n=this._children.length ; i < n; ++i) this._children[i].initFromMarkup();
}

/**
* Called after all controls have been properly inited.
* Overload to init the control, you may do RPC here.
*/
Wsf_Ctrls_Control.prototype.onCreate = function ()
{
    if (this._parent) this._parent.onChildAdded(this);
    for (var i=0,n=this._children.length ; i < n; ++i) this._children[i].onCreate();
}

/**
* Called in ajax, when state is updated.
*/
Wsf_Ctrls_Control.prototype.onStateChanged = function ()
{
    this.show(this._visible);
}

Wsf_Ctrls_Control.prototype.onChildAdded = function (child)
{
}

Wsf_Ctrls_Control.prototype.onChildDeleted = function (child)
{
}

/*******************************************************************************
*
* STATE
*
*******************************************************************************/

/**
* Load state from the server.
*/
Wsf_Ctrls_Control.prototype._loadState = function (state)
{
    for (var name in state) {
        this[name] = Wsf_Web_MVC_ControlStateCoder.decodeStateVariable(state[name]);
    }
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype.saveState = function (ctrlsState)
{
    var state = this._getControlState();
    // save only if non-empty
    for (var name in state) { ctrlsState[this.getId()] = state; break; }

    for (var i=0; i < this._children.length; ++i) {
        this._children[i].saveState(ctrlsState);
    }
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype._makeStateSnapshot = function ()
{
    for (var name in this._serverState) {
        this._serverState[name] = Object.toJSON(Wsf_Web_MVC_ControlStateCoder.encodeVariable(this[name]));
    }

    for (var i=0; i < this._children.length; ++i) this._children[i]._makeStateSnapshot();
}

/**
* Returns a state object to send to the server.
* You can overload it to add custom state to be passed to the server.
*/
Wsf_Ctrls_Control.prototype._getControlState = function ()
{
    if (!this._serverState) return {};

    var state = { };
    this._visible = this.elem.visible();

    for (var name in this._serverState) {
        var newValue = Wsf_Web_MVC_ControlStateCoder.encodeVariable(this[name]);
        if (Object.toJSON(newValue) !== this._serverState[name]) state[name] = newValue;
    }

    return state;
}

/*******************************************************************************/
/*
/* ...
/*
/*******************************************************************************/

/*******************************************************************************/
Wsf_Ctrls_Control.prototype.onPreDelete = function ()
{
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype.deleteCtrl = function ()
{
    // Already deleted? View cannot be deleted so this test is ok.
    if (!this._parent) return;

    this._parent._children = this._parent._children.without(this);
    this.elem.parentNode.removeChild(this.elem);  // removes the whole HTML subtree

    this._deleteInternal();
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype._deleteInternal = function ()
{
    this.onPreDelete();

    this.elem = null;
    var parent = this._parent;
    this._parent = null;

    this._children.each(function (ctrl) { ctrl._deleteInternal(); });
    this._children = [];

    g_app._deregisterCtrl(this);

    parent.onChildDeleted(this);
    this.raiseEvent(Wsf_Ctrls_Control.EVENT_AFTER_DELETE, { sender: this });
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype.getElem = function ()
{
    return this.elem;
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype._getChildrenContainerElem = function ()
{
    return this.elem;
}

/**
* Register a class name to be able to RTTI (e.g. isA())
*/
Wsf_Ctrls_Control.prototype._addClass = function (className)
{
    this._classes.push(className);
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype.isA = function (className)
{
    return this._classes.indexOf(className) != -1;
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype.getAncestorByClass = function (className)
{
    for (var o=this; o; o=o._parent) {
        if (o.isA(className)) return o;
    }
    return null;
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype.getView = function ()
{
    return Wsf_Ctrls_ViewHtml.prototype._instance;
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype.getTitle = function ()
{
    return this._title;
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype.getId = function ()
{
    return this._id;
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype.getParent = function ()
{
    return this._parent;
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype.getChildByIdx = function (idx)
{
    return this._children[idx];
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype.getChildrenCount = function ()
{
    return this._children.length;
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype.getChildren = function ()
{
    return this._children;
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype.updateUI = function ()
{
    for (var i=0; i < this._children.length; ++i) {
        this._children[i].updateUI();
    }
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype.getForm = function ()
{
    for (var p = this; p && !p.isA("Wsf_Forms_CtrlForm"); p=p._parent) { }
    return p;
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype.getWindow = function ()
{
    for (var p = this; p && !p.isA("Wsf_Ctrls_Window"); p=p._parent) { }
    return p;
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype.focus = function ()
{
    if (!this.isVisible() || !this.isEnabled()) return false;
    this.elem.focus();
    return true;
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype.hasFocus = function ()
{
    return this.getView().getFocus() === this;
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype.enable = function (enable)
{
    if (enable == this._enabled) return;
    this._enableInternal(enable);
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype._enableInternal = function (enable)
{
    if (enable)
        this.elem.removeClassName('disabled');
    else
        this.elem.addClassName('disabled');

    this.elem.disabled = !enable;
    this._enabled = enable;
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype.isEnabled = function ()
{
    return this._enabled;
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype.isEnabled = function ()
{
    return this._enabled;
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype.isReadOnly = function ()
{
    return this._readOnly;
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype.getStyle = function (style, defValue)
{
    return this._styles && this._styles[style] ? this._styles[style] : defValue;
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype.setTitle = function (title, isHtml)
{
    if (isHtml) {
        this._titleHtml = title;
        this._title = null;
    }
    else {
        this._titleHtml = null;
        this._title = title;
    }

    return this;
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype.callServerMethod = function (method, params, fnOnSuccess, fnOnFailure)
{
    return Wsf_Web_CallServerMethodHandler.invoke(this, method, params, fnOnSuccess, fnOnFailure);
}


/*******************************************************************************/
/*
/* VISIBILITY
/*
/*******************************************************************************/

/*******************************************************************************/
Wsf_Ctrls_Control.prototype.isVisible = function ()
{
    return this.elem.visible();
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype.show = function (show)
{
    if (show || show == undefined)
        this.elem.show();
    else
        this.elem.hide();

    if (this._parent) this._parent.onSize();
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype.hide = function ()
{
    this.show(false);
}

/*******************************************************************************/
/*
/* POSITION & SIZE - this is slow
/*
/*******************************************************************************/

/*******************************************************************************/
Wsf_Ctrls_Control.prototype._cacheComputedStyles = function ()
{
    //   this._margin = [0,0,0,0];
    //   this._border = [0,0,0,0];
    //   this._padding = [0,0,0,0];
    //   return;

    var s = window.getComputedStyle ? window.getComputedStyle(this.elem, null) : this.elem.currentStyle;

    var a = [parseInt(s.paddingTop), parseInt(s.paddingRight), parseInt(s.paddingBottom), parseInt(s.paddingLeft) ];
    if (isNaN(a[0])) a[0] = 0;
    if (isNaN(a[1])) a[1] = 0;
    if (isNaN(a[2])) a[2] = 0;
    if (isNaN(a[3])) a[3] = 0;
    this._padding = a;

    a = [parseInt(s.marginTop), parseInt(s.marginRight), parseInt(s.marginBottom), parseInt(s.marginLeft)];
    if (isNaN(a[0])) a[0] = 0;
    if (isNaN(a[1])) a[1] = 0;
    if (isNaN(a[2])) a[2] = 0;
    if (isNaN(a[3])) a[3] = 0;
    this._margin = a;

    a = [parseInt(s.borderTopWidth), parseInt(s.borderRightWidth), parseInt(s.borderBottomWidth), parseInt(s.borderLeftWidth)];
    if (isNaN(a[0])) a[0] = 0;
    if (isNaN(a[1])) a[1] = 0;
    if (isNaN(a[2])) a[2] = 0;
    if (isNaN(a[3])) a[3] = 0;
    this._border = a;
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype._clearSizeCache = function ()
{
    this._offsetWidth = null;
    this._offsetHeight = null;
    this._clientWidth = null;
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype._cacheOffsetWidth = function ()
{
    this._offsetWidth = this.elem.offsetWidth;
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype._cacheOffsetHeight = function ()
{
    this._offsetHeight = this.elem.offsetHeight;
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype._cacheClientSize = function ()
{
    this._clientWidth = this.elem.clientWidth;
    this._clientHeight = this.elem.clientHeight;
}

/**
* content+padding+border
*/
Wsf_Ctrls_Control.prototype.getOffsetSize = function ()
{
    if (this._offsetWidth === null) this._cacheOffsetWidth();
    if (this._offsetHeight === null) this._cacheOffsetHeight();
    return { width: this._offsetWidth, height: this._offsetHeight };
}

/**
* content+padding+border
*/
Wsf_Ctrls_Control.prototype.getOffsetWidth = function ()
{
    if (this._offsetWidth === null) this._cacheOffsetWidth();
    return this._offsetWidth;
}

/**
* content+padding+border
*/
Wsf_Ctrls_Control.prototype.getOffsetHeight = function ()
{
    if (this._offsetHeight === null) this._cacheOffsetHeight();
    return this._offsetHeight;
}

/**
* content+padding
*/
Wsf_Ctrls_Control.prototype.getClientSize = function ()
{
    if (this._clientWidth === null || this._clientHeight === null) this._cacheClientSize();
    return { width: this._clientWidth, height: this._clientHeight };
}

/**
* Slow!
*/
Wsf_Ctrls_Control.prototype.getMargin = function()
{
    if (!this._margin) this._cacheComputedStyles();
    return this._margin;
}

/**
* Slow!
*/
Wsf_Ctrls_Control.prototype.getPadding = function()
{
    if (!this._padding) this._cacheComputedStyles();
    return this._padding;
}

/**
* Slow!
*/
Wsf_Ctrls_Control.prototype._getMarginBorderPadding = function()
{
    if (!this._margin) this._cacheComputedStyles();

    return [this._margin[0]+this._border[0]+this._padding[0],
        this._margin[1]+this._border[1]+this._padding[1],
        this._margin[2]+this._border[2]+this._padding[2],
        this._margin[3]+this._border[3]+this._padding[3]
        ];
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype.move = function (x,y,w,h)
{
    var s = this.elem.style;

    if (x < 0) x = 0;
    if (y < 0) y = 0;

    if (!this._positionSet) {
        s.position = this._positionFixed ? "fixed":"absolute";
        this._positionSet = true;
    }

    s.left = x+"px";
    s.top = y+"px";

    if (w !== null || h !==null) {
        this.setSize(w, h);
    }
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype.move2 = function (x1,y1,x2,y2,w,h)
{
    var s = this.elem.style;

    if (!this._positionSet) {
        s.position = this._positionFixed ? "fixed":"absolute";
        this._positionSet = true;
    }

    if (x1 !== null) s.left = x1+"px";
    if (y1 !== null) s.top = y1+"px";
    if (x2 !== null) s.right = x2+"px";
    if (y2 !== null) s.bottom = y2+"px";

    if ((x1 !== null && x2 !== null) || (y1 !== null && y2 !== null)) this._clearSizeCache();

    if (w !== null || h !== null) {
        this.setSize(w, h);
    }
    else this.onSize();
}

/**
* Sets the offset size: w/h = margin+border+padding+content
*/
Wsf_Ctrls_Control.prototype.setSize = function (w, h)
{
    var s = this.elem.style;
    var b = this._getMarginBorderPadding();

    if (w !== null) {
        this._offsetWidth = w;
        w -= b[1] + b[3];
        if (w < 0) w = 0;
        s.width = w + "px";
        this._offsetWidth = w + b[1] + b[3];
    }

    if (h !== null) {
        this._offsetHeight = h;
        h -= b[0] + b[2];
        if (h < 0) h = 0;
        s.height = h  + "px";
        this._offsetHeight = h + b[0] + b[2];
    }

    this.onSize();
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype.onSize = function ()
{
    if (!this.isVisible()) return;

    if (this._layout) {
        for (var i=0,n=this._children.length; i < n; ++i) {
            this._children[i]._clearSizeCache();
        }
        this._layout.layoutChildren(this);
    }
    else {
        // Default CSS layout of children
        for (var i=0,n=this._children.length; i < n; ++i) {
            var o = this._children[i];
            o._clearSizeCache();
            o.onSize();
        }
    }
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype.setZIndex = function (zIndex)
{
    this.elem.style.zIndex = zIndex;
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype.moveBelow = function (ctrl)
{
    this.elem.style.zIndex = ctrl.elem.style.zIndex - 1;
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype.setOpacity = function (opacityFloat)
{
    this.elem.setOpacity(opacityFloat);
}

/*******************************************************************************/
/*
/* COMMANDS
/*
/*******************************************************************************/

/*******************************************************************************/
Wsf_Ctrls_Control.prototype.getCmdTarget = function ()
{
    return this._cmdTarget ? this._cmdTarget : this;
}

/*******************************************************************************/
Wsf_Ctrls_Control.prototype.onUpdateCmdUI = function (cmdId, cmdui)
{
    return false;
}


/*******************************************************************************/
function Wsf_Ctrls_LayoutStack(ctrl)
{
   ctrl.elem.style.overflow = "auto";
}

/*******************************************************************************/
Wsf_Ctrls_LayoutStack.prototype.layoutChildren = function (ctrl)
{  
   // Get the height of all fixed controls and # of stretch controls
   
   var fixedH = 0;
   var stretchCount = 0;
   
   for (var i=ctrl._children.length; i--;)
   {
      var o = ctrl._children[i];
      if (!o.isVisible()) continue;
      
      if (o.getStyle('stretch',false))
      {
         ++stretchCount;
         o.elem.style.overflow = "auto";
      }
      else {                           
         var m = o.getMargin();
         fixedH += o.getOffsetHeight() + m[0] + m[2];
      }
   }
   
   // Layout

   var padding = ctrl.getPadding();
   var a = ctrl.getClientSize();
   var w = a['width'] - padding[1] - padding[3];
   var h = a['height'] - padding[0] - padding[2];
//   var x = padding[3];
   var y = padding[0];
   var stretchH = Math.max(h - fixedH, 0);
   
   if (stretchCount > 0)
   {
      var avgStretchH = Math.floor(stretchH / stretchCount);
      var lastStretchH = h - avgStretchH*(stretchCount-1) - fixedH;
   }
   
   for (var i=0,n=ctrl._children.length; i < n; ++i)
   {
      var o = ctrl._children[i];
      if (!o.isVisible()) continue;

      // e.g. input[text] does not stretch to 100% by default -> we must set also width
      
      if (o.getStyle('stretch',false))
      {
         h = (--stretchCount ? avgStretchH : lastStretchH);
//         o.move(x, y, w, h);
         o.setSize(w,h);
//         o.elem.style.position = "relative";
      }
      else
      {
         o.setSize(w,null);
//         var m = o.getMargin();
//         h = o.getOffsetHeight() + m[0] + m[2];
//         o.move(x, y, w, null);
//         o.elem.style.position = "relative";
      }
      
//      y += h;
   }   
}


/*******************************************************************************/
function Wsf_Ctrls_LayoutFullsize(ctrl)
{
}

/*******************************************************************************/
Wsf_Ctrls_LayoutFullsize.prototype.layoutChildren = function (ctrl)
{  
   for (var i=0,n=ctrl.getChildrenCount(); i<n; ++i)
   {
      var t = ctrl.getChildByIdx(i);

      if (t.isVisible())
      {
         var p = ctrl.getPadding();
         t.move2(p[3],p[0],p[1],p[2],null,null);
         break;
      }
   }
}


/*****************************************************************************/
var Wsf_Web_Vocabulary = { 
   
   // Vocabularies are added as new classes are loaded via AJAX
   _vocabularies : [],
   
   add : function (vocabulary)
   {
      this._vocabularies.push(vocabulary);
   },
   
   getWord : function (id)
   {
      a = id.split('.');
   
      for (var i=0,n=this._vocabularies.length; i < n; ++i)
      {
         var ns = this._vocabularies[i][a[0]];
         
         if (ns)
         {
            var w = ns[a[1]];
            if (w) return w;
         }
      }
      
      return "[***"+id+"***]";
   }
};

/*****************************************************************************/
function W(id)
{
   return Wsf_Web_Vocabulary.getWord(id);
}

/*******************************************************************************/
function Wsf_Web_CallServerMethodHandler(ctrl, method, params, fnOnSuccess, fnOnFailure, async)
{
    if (async === undefined) async = true;

    // Busy info

    var div = $(document.createElement('div'));
    div.id = 'wsfAjaxStatus';
    div.update(W('Wsf_Ctrls.LOADING')+"...");
    document.body.appendChild(div);

    // Overlay is needed to disable clicking

    var div = $(document.createElement('div'));
    div.id = 'wsfDisableUiOverlay';
    document.body.appendChild(div);

    // DTO

    var postParams = {
        wsfState: {
            async: async,
            object: Wsf_Web_MVC_ControlStateCoder.encodeVariable(ctrl),
            method: method,
            params: params === undefined ? {} : Wsf_Web_MVC_ControlStateCoder.encodeVariable(params),
            ctrlsState: {},
            deletedServerCtrls: g_app._deletedServerCtrls
        }
    };

    g_app.getRootCtrl().saveState(postParams.wsfState.ctrlsState);
    postParams.wsfState = Object.toJSON(postParams.wsfState);

    // GO!

    if (async) {
        var a = $$('input','select','textarea');
        var filePresent = false;

        for (var i=0,n=a.length; i < n; ++i) {
            var elem = a[i];
            var name = elem.name;
            if (name == 'wsfState') continue;

            if (elem.type == 'file')
                {
                if (!elem.files.length) continue;
                postParams[name] = { filename: elem.files[0].fileName, fileObject: elem.files[0] };
                filePresent = true;
            }
            else {
                if (elem.type == 'checkbox' && !elem.checked) continue;
                var value = elem.value;

                if (postParams[name] === undefined)
                    postParams[name] = value;
                else {
                    if (!Object.isArray(postParams[name])) postParams[name] = [postParams[name]];
                    postParams[name].push(value);
                }
            }
        }

        if (filePresent) {
            var boundary = "wsfAjax--------------" + (new Date).getTime();
            new Ajax.Request(document.location.href, {
                asynchronous: true,
                method: 'post',
                encoding: null, // default is UTF-8 which as appended to contentType, but this is an error for multipart
                contentType: "multipart/form-data; boundary=" + boundary,
                postBody: this._buildMessage(postParams, boundary),
                wsfSendAsBinary: true,
                onSuccess: this.onSuccess.bind(this, fnOnSuccess, fnOnFailure),
                onFailure: this.onFailure.bind(this, fnOnFailure)
            });
        }
        else {
            new Ajax.Request(document.location.href, {
                asynchronous: true,
                method: 'post',
                parameters: postParams,
                onSuccess: this.onSuccess.bind(this, fnOnSuccess, fnOnFailure),
                onFailure: this.onFailure.bind(this, fnOnFailure)
            });
        }
    }
    else {
        $('wsfState').value = postParams.wsfState;
        $('wsfForm').submit();
        this.done();
    }
}

/*******************************************************************************/
Wsf_Web_CallServerMethodHandler._instance = null;
Wsf_Web_CallServerMethodHandler._queue = [];

/**
* From http://www.webtoolkit.info/javascript-utf8.html
*/
Wsf_Web_CallServerMethodHandler._encodeUtf8 = function (string)
{
    //    string = string.replace(/\r\n/g,"\n");
    var utftext = "";

    for (var n=0, m=string.length; n < m; 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;
}

/*******************************************************************************/
Wsf_Web_CallServerMethodHandler.prototype._buildMessage = function(postParams, boundary)
{
    var parts = [];

    for (var name in postParams) {
        var part  = "";
        var param = postParams[name];

        if (typeof(param) == "object") {
            if (Object.isArray(param)) {
                alert("Wsf_Web_CallServerMethodHandler.prototype._buildMessage: NOT IMPLEMENTED");
            }
            else {
                part += 'Content-Disposition: form-data; ';
                part += 'name="' + name + '"; ';
                part += 'filename="'+ Wsf_Web_CallServerMethodHandler._encodeUtf8(param.filename) + '"' + "\r\n";
                part += "Content-Type: application/octet-stream" + "\r\n\r\n";
                part += param.fileObject.getAsBinary() + "\r\n";
            }
        } else {
            part += 'Content-Disposition: form-data; ';
            part += 'name="' + name + '"' + "\r\n\r\n";
            //part += "Content-Type: text/plain; charset=UTF-8" + "\r\n\r\n";
            part += Wsf_Web_CallServerMethodHandler._encodeUtf8(param) + "\r\n";
        }

        parts.push(part);
    };

    var request = "--" + boundary + "\r\n";
    request += parts.join("--" + boundary + "\r\n");
    request += "--" + boundary + "--" + "\r\n";

    return request;
}

/**
* Parameters can be passed as an object of named parameters or an array of unnamed parameters.
* NOTE: You may not alter the control tree until method call has finished.
*/
Wsf_Web_CallServerMethodHandler.invoke = function (ctrl, method, params, fnOnSuccess, fnOnFailure, async)
{
    if (Wsf_Web_CallServerMethodHandler._instance) {
        Wsf_Web_CallServerMethodHandler._queue.push([ctrl, method, params, fnOnSuccess, fnOnFailure, async]);
        return;
    }

    Wsf_Web_CallServerMethodHandler._instance = new Wsf_Web_CallServerMethodHandler(ctrl, method, params, fnOnSuccess, fnOnFailure, async);
    return true;
}

/*******************************************************************************/
Wsf_Web_CallServerMethodHandler.prototype.done = function ()
{
    var e = $('wsfAjaxStatus');
    e.parentNode.removeChild(e);
    e = $('wsfDisableUiOverlay');
    e.parentNode.removeChild(e);

    Wsf_Web_CallServerMethodHandler._instance = null;

    if (Wsf_Web_CallServerMethodHandler._queue.length) {
        var p = Wsf_Web_CallServerMethodHandler._queue.shift();
        new Wsf_Web_CallServerMethodHandler(p[0], p[1], p[2], p[3], p[4], p[5]);
    }
}

/*******************************************************************************/
Wsf_Web_CallServerMethodHandler.prototype.onFailure = function (fnOnFailure, transport)
{
    alert(W('Wsf_Web.AJAX_SERVER_ERROR') + (DEBUG ? "\n\n"+transport.responseText : ''));
    if (fnOnFailure) fnOnFailure();
    this.done();
}

/*******************************************************************************/
Wsf_Web_CallServerMethodHandler.prototype.onSuccess = function (fnOnSuccess, fnOnFailure, transport)
{
    try {
        // If PHP failed, transport.responseText is invalid and usually contains a PHP error message (in debug mode).
        var response = transport.responseText.evalJSON();
    }
    catch (e) {
        this.onFailure(fnOnFailure, transport);
        return;
    }

    try
    {
        g_app._stopUpdateUI();

        g_app._nextCtrlId = response.nextCtrlId;
        g_app._nextServerCtrlId = response.nextCtrlId;
        g_app._deletedServerCtrls = [];

        // Load style sheets

        if (response.styleSheet) Wsf.requireStylesheet(response.styleSheet);

        // Load scripts

        for (var i=0,n=response.scripts.length; i < n; ++i) {
            var s = response.scripts[i];
            if (s.charAt(0) == '/' || s.substr(0,4) == 'http')
                Wsf.requireScript(s);
            else
                eval(s);
        }

        // Deleted controls

        for (var i=0,n=response.deletedCtrls.length; i < n; ++i) {
            var c = g_app.getCtrlById(response.deletedCtrls[i]);
            if (c) c.deleteCtrl();
        }

        // 1. Add/update markup to the DOM

        if (response.renderedSubtrees !== undefined) {
            for (var id in response.renderedSubtrees) {
                var s = response.renderedSubtrees[id];
                var o = g_app.getCtrlById(id);

                if (o)
                    o.getElem().replace(s.html);
                else {
                    // TODO: insert before the right child
                    var p = g_app.getCtrlById(s.pid);
                    p._getChildrenContainerElem().insert(s.html);
                }
            }
        }

        // 2a) Create new controls

        if (response.newCtrls !== undefined) {
            eval('var ctrls = ' + response.newCtrls);
            for (var id in ctrls) {
                g_app._registerCtrl(id, ctrls[id][0]);
            }
            for (var id in ctrls) {
                g_app.getCtrlById(id).createFromServerState(ctrls[id][1]);
            }
        }

        // 2b) Load state of updated controls
        // Must be called after all new controls have been created

        if (response.ctrlsState !== undefined) {
            for (var id in response.ctrlsState) {
                var o = g_app.getCtrlById(id);
                o._loadState(response.ctrlsState[id]);
            }
        }

        // 4) MakeStateSnapshot() for all controls

        g_app.getRootCtrl()._makeStateSnapshot();

        // 5) .initFromMarkup(), OnCreate() for all rendered controls

        if (response.renderedSubtrees !== undefined) {
            for (var id in response.renderedSubtrees) {
                var c = g_app.getCtrlById(id);
                c.initFromMarkup();
            }
            for (var id in response.renderedSubtrees) {
                g_app.getCtrlById(id).onCreate();
            }
        }

        // 6) OnStateChanged() for updated controls

        if (response.ctrlsState !== undefined) {
            for (var id in response.ctrlsState) {
                var o = g_app.getCtrlById(id);
                o.onStateChanged();
            }
        }

        // Done

        g_app.getRootCtrl().onSize();  // force reflow
        if (fnOnSuccess) fnOnSuccess(Wsf_Web_MVC_ControlStateCoder.decodeStateVariable(response.returnValue));
        g_app._startUpdateUI();
        this.done();
    }

    catch (e) {
        alert(W('Wsf_Web.AJAX_FATAL_ERROR') + (DEBUG ? "\r\n\r\n"+e+"\r\n\r\n"+e.stack : ""));
        g_app.refresh();
    }
}

function Wsf_Ctrls_View(p) { Wsf_Ctrls_Control.call(this, p); } Wsf.extend(Wsf_Ctrls_View, Wsf_Ctrls_Control);


/*******************************************************************************/
function Wsf_Ctrls_ViewHtml(serverState)
{
   Wsf_Ctrls_View.call(this, serverState);
   
   // TODO: BUGGY
   Event.observe(document.body, 'activate', this._OnFocus.bindAsEventListener(this));
   
   Wsf_Ctrls_ViewHtml.prototype._instance = this;
}

Wsf.extend(Wsf_Ctrls_ViewHtml, Wsf_Ctrls_View);

/*******************************************************************************/
Wsf_Ctrls_ViewHtml.prototype.onCreate = function ()
{
   Wsf_Ctrls_View.prototype.onCreate.call(this);

   if (this._focusedCtrl) 
   {   
      if (!this._focusedCtrl.focus()) this._focusedCtrl = null;
   }
}

Wsf_Ctrls_ViewHtml.prototype.set_focusedCtrl = function (value)
{
   if (this._focusedCtrl = value) 
   {   
      if (!this._focusedCtrl.focus()) this._focusedCtrl = null;
   }
}

/*******************************************************************************/
Wsf_Ctrls_ViewHtml.prototype._getChildrenContainerElem = function ()
{
   return this.elem.down('form');
}

/*******************************************************************************/
Wsf_Ctrls_ViewHtml.prototype._OnFocus = function (ev)
{
   // BUGGY
   
   // NOTE: IE: Do not pass newly focused controls back to improve focus-stealing resistance :-)
   // Optimize when we know how
   if (Prototype.Browser.IE) this._focusedCtrl = g_app.getCtrlById(ev.element().id);
}

/*******************************************************************************/
Wsf_Ctrls_ViewHtml.prototype.getFocus = function()
{
   return this._focusedCtrl;
}

/*******************************************************************************/
function Wsf_Ctrls_Menu(state)
{
   Wsf_Ctrls_Control.call(this, state);

   this.aItemElems = [];
   this._isAnyItemEnabled = false;
   this.isForceUpdateUIWhenInvisible = false;
}

Wsf.extend(Wsf_Ctrls_Menu, Wsf_Ctrls_Control);

Wsf_Ctrls_Menu.EVENT_ITEM_CLICKED = 1;

/*******************************************************************************/
Wsf_Ctrls_Menu.prototype.initFromMarkup = function ()
{
   Wsf_Ctrls_Control.prototype.initFromMarkup.call(this);  
   this.elem.descendants().each(this._decorateElem.bind(this));
}

/*******************************************************************************/
Wsf_Ctrls_Menu.prototype._decorateElem = function (e)
{
   if (e.tagName != "LI") return;

   this.aItemElems.push(e);

   var a = e.id.split('.');
   e.sCmdId = a.length == 2 ? a[1] : null;

   if (this._isClientExpandMode)
   {
      if (e.sCmdId != '' && e.hasClassName('clickable'))
      {
         e.observe('click', this.onClickItem.bindAsEventListener(this, e));
      } 
      else if (e.hasClassName('expandable'))
      {
         e.observe('click', this.onExpandItem.bindAsEventListener(this, e));
      }

      e.observe('mouseover', this.LI_OnMouseOver.bindAsEventListener(this, e));
      e.observe('mouseout', this.LI_OnMouseOut.bindAsEventListener(this, e));   
   }
}

/*******************************************************************************/
Wsf_Ctrls_Menu.prototype._getControlState = function ()
{
   var a = Wsf_Ctrls_Control.prototype._getControlState.apply(this);

   a.aSelIds = [];
   
   for (var i=this.aItemElems.length; i--;)
   {
      var e = this.aItemElems[i];
      if (e.hasClassName('sel')) a.aSelIds.push(e.id.split('.')[1]);
   }
   
   return a;
}

/*******************************************************************************/
Wsf_Ctrls_Menu.prototype.updateUI = function ()
{
   if (!this._isCmdMode || (!this.isForceUpdateUIWhenInvisible && !this.isVisible())) return;

   var CmdUI = 
   { 
      isEnabled : true,
      isChecked : false,
      enable: function (isEnabled) { this.isEnabled = isEnabled; },
      check: function (isChecked) { this.isChecked = isChecked; }
   };
   
   this._isAnyItemEnabled = false;
                                          
   for (var i=this.aItemElems.length; i--;)
   {
      var e = this.aItemElems[i];
      var sCmdId = e.id.split('.')[1];
      if (!sCmdId) continue;
      CmdUI.isEnabled = true;
      CmdUI.isChecked = false;
      
      g_app.invokeUpdateCmd(this.getCmdTarget(), sCmdId, CmdUI);

      if (CmdUI.isEnabled) {
         this._isAnyItemEnabled = true;
         e.removeClassName('disabled'); 
      }
      else
         e.addClassName('disabled'); 
         
      if (CmdUI.isChecked)
         e.addClassName('checked'); 
      else
         e.removeClassName('checked');          
   }
}

/*******************************************************************************/
Wsf_Ctrls_Menu.prototype.isAnyItemEnabled = function()
{
   return this._isAnyItemEnabled;
}

/*****************************************************************************/
/*
/* SELECTION
/*
/*****************************************************************************/

/*****************************************************************************/
Wsf_Ctrls_Menu.prototype.selectItem = function (id, isSelect)
{
   for (var i=this.aItemElems.length; i--;)
   {
      var e = this.aItemElems[i];
      
      if (id == e.id.split('.')[1])
      {
         if (isSelect)
            e.addClassName('sel');
         else
            e.removeClassName('sel');
         break;
      }
   }
}

/*****************************************************************************/
Wsf_Ctrls_Menu.prototype.getSelId = function ()
{
   for (var i=this.aItemElems.length; i--;)
   {
      var e = this.aItemElems[i];
      
      if (e.hasClassName('sel')) return e.id.split('.')[1];
   }
   
   return null;
}

/*****************************************************************************/
/*
/* EVENTS
/*
/*****************************************************************************/

/*****************************************************************************/
Wsf_Ctrls_Menu.prototype.onClickItem = function (ev, LI)
{
   Event.stop(ev);
   
   if ($(LI).hasClassName("disabled")) return;
   if ($(LI).hasClassName("expandable")) return;
   
   if (this.isContextMenu) this.hideContextMenu();
   
   // Notify

   this.raiseEvent(Wsf_Ctrls_Menu.EVENT_ITEM_CLICKED, { sender: this, id: LI.sCmdId });
   
   if (this._isCmdMode) g_app.invokeCmd(this.getCmdTarget(), LI.sCmdId, {});
}

/*****************************************************************************/
/*
/* CONTEXT MENU
/*
/*****************************************************************************/

/*******************************************************************************/
Wsf_Ctrls_Menu.prototype.trackContextMenu = function(nX, nY)
{
   this.isContextMenu = true;
   this.isWithin = false;
   
   this.elem.style.left = nX + "px";
   this.elem.style.top = nY + "px";
   this.show(true);
   this.focus();

   this.fnTCMOnClickDoc = this.TcmOnClickDoc.bindAsEventListener(this);
   this.fnTCMOnClickMenu = this.TcmOnClickMenu.bindAsEventListener(this);
   this.fnTCMOnKeyDown = this._TcmOnKeyDown.bindAsEventListener(this);
   
   Event.observe(document, "mousedown", this.fnTCMOnClickDoc);
   Event.observe(this.elem, "mousedown", this.fnTCMOnClickMenu);         
   Event.observe(this.elem, "keydown", this.fnTCMOnKeyDown);         
   
   this.updateUI();
}

/*******************************************************************************/
Wsf_Ctrls_Menu.prototype.hideContextMenu = function()
{
   Event.stopObserving(document, "mousedown", this.fnTCMOnClickDoc);
   Event.stopObserving(this.elem, "mousedown", this.fnTCMOnClickMenu);
   Event.stopObserving(this.elem, "keydown", this.fnTCMOnKeyDown);
   this.show(false);
   this.isContextMenu = false;
}

/*****************************************************************************/
Wsf_Ctrls_Menu.prototype.TcmOnClickDoc = function (event)
{
   if (!this.isWithin) this.hideContextMenu();
   this.isWithin = false;
}

/*****************************************************************************/
Wsf_Ctrls_Menu.prototype.TcmOnClickMenu = function (event)
{
   this.isWithin = true;
}

/*******************************************************************************/
Wsf_Ctrls_Menu.prototype._TcmOnKeyDown = function (ev)
{
   if (ev.keyCode == Event.KEY_ESC)
   {
      this.hideContextMenu();
   }
}

/*******************************************************************************/
/*
/* Expanding subitems
/*
/*******************************************************************************/

/*****************************************************************************/
function WsfCtrlMenu_DelayedHide(LI)
{
   var subItemsUl = $(LI).down().next('ul');
   var TitleDIV = $(LI).down();
   TitleDIV.removeClassName("exp");
   subItemsUl.hide();

   window.clearTimeout(subItemsUl.nTimeID);
   subItemsUl.nTimeID = null;
}

/*****************************************************************************/
function WsfCtrlMenu_DelayedShow(LI)
{
   var subItemsUl = $(LI).down().next('ul');
   var TitleDIV = $(LI).down();
   TitleDIV.addClassName("exp");
   subItemsUl.show();

   window.clearTimeout(subItemsUl.nTimeID);
   subItemsUl.nTimeID = null;
}

/*****************************************************************************/
Wsf_Ctrls_Menu.prototype.LI_OnMouseOver = function (ev, LI)
{
   if ((!LI.hasClassName("clickable") && !LI.hasClassName("expandable")) || LI.hasClassName("disabled")) return;

   var subItemsUl = LI.down().next('ul');
   if (!subItemsUl) return;
      
   if (subItemsUl.nTimeID)
   {
      window.clearTimeout(subItemsUl.nTimeID);
      subItemsUl.nTimeID = null;
   }   
   else
   {
      subItemsUl.nTimeID = window.setTimeout(function() { WsfCtrlMenu_DelayedShow(LI); }, 150);
   }
}

/*****************************************************************************/
Wsf_Ctrls_Menu.prototype.onExpandItem = function (ev, li)
{
   var subItemsUl = li.down().next();
   if (subItemsUl.nTimeID)
   {
      window.clearTimeout(subItemsUl.nTimeID);
      subItemsUl.nTimeID = null;
   }   

   WsfCtrlMenu_DelayedShow(li);
}

/*****************************************************************************/
Wsf_Ctrls_Menu.prototype.LI_OnMouseOut = function (ev, LI)
{
   if (LI.hasClassName("disabled")) return;

   var subItemsUl = LI.down().next('ul');
   if (!subItemsUl) return;
   
   if (subItemsUl.nTimeID)
   {
      window.clearTimeout(subItemsUl.nTimeID);
      subItemsUl.nTimeID = null;
   }   
   else
   {
      subItemsUl.nTimeID = window.setTimeout(function() { WsfCtrlMenu_DelayedHide(LI); }, 500);
   }
}


/*******************************************************************************/
function Wsf_Forms_CtrlForm(state)
{
   Wsf_Ctrls_Control.call(this, state);
   this._addClass('Wsf_Forms_CtrlForm');             
}

Wsf.extend(Wsf_Forms_CtrlForm, Wsf_Ctrls_Control);

/*******************************************************************************/
Wsf_Forms_CtrlForm.prototype.initFromMarkup = function ()
{
   Wsf_Ctrls_Control.prototype.initFromMarkup.call(this);
   this.elem.observe('keydown', this._OnKeyDown.bindAsEventListener(this));
}

/*******************************************************************************/
Wsf_Forms_CtrlForm.prototype._OnKeyDown= function (ev)
{
   if (ev.keyCode == Event.KEY_RETURN)
   {                             
      Event.stop(ev);
      if (this.defaultCmdId) g_app.invokeCmd(this, this.defaultCmdId, {});
   }
}

/*******************************************************************************/
function Wsf_Forms_CtrlText(Params)
{
   Wsf_Ctrls_Control.call(this, Params);
}

Object.extend(Wsf_Forms_CtrlText.prototype, Wsf_Ctrls_Control.prototype);

/*******************************************************************************/
Wsf_Forms_CtrlText.prototype.initFromMarkup = function ()
{
   Wsf_Ctrls_Control.prototype.initFromMarkup.call(this);

   if (this.getStyle('showLabelInside'))
   {
      this._empty = this.elem.value == '';

      if (this.hasFocus())
         this.onFocus(null);
      else
         this.onBlur(null);

      this.elem.observe('focus', this.onFocus.bindAsEventListener(this));
      this.elem.observe('blur', this.onBlur.bindAsEventListener(this));
   }  
}

/*******************************************************************************/
Wsf_Forms_CtrlText.prototype.saveState = function (ctrlsState)
{
   if (this.getStyle('showLabelInside') && !this._hasFocus && this._empty) 
   {
      this.elem.value = "";
   }
   
   Wsf_Ctrls_Control.prototype.saveState.call(this, ctrlsState);
}

/*******************************************************************************/
Wsf_Forms_CtrlText.prototype.enable = function (enable)
{
   this.elem.disabled = !enable;
   Wsf_Ctrls_Control.prototype.enable.call(this, enable);
}

/*******************************************************************************/
Wsf_Forms_CtrlText.prototype.setValue = function (s) 
{
   this.elem.value = s;   
}

/*******************************************************************************/
Wsf_Forms_CtrlText.prototype.getValue = function () 
{
   return this.elem.value;   
}

/*******************************************************************************/
Wsf_Forms_CtrlText.prototype.onFocus = function (ev) 
{
   if (this._empty) 
   {
      this.elem.value = '';
      this.elem.removeClassName('hint');
   }
   
   this._hasFocus = true;
}

/*******************************************************************************/
Wsf_Forms_CtrlText.prototype.onBlur = function (ev) 
{
   this._empty = this.elem.value == '';
   
   if (this._empty) 
   {
      this.elem.value = this.getTitle();   
      this.elem.addClassName('hint');
   }

   this._hasFocus = false;
}

/*******************************************************************************/
function Wsf_Forms_CtrlButton(state)
{
   Wsf_Ctrls_Control.call(this, state);
}

Wsf.extend(Wsf_Forms_CtrlButton, Wsf_Ctrls_Control);

/*******************************************************************************/
Wsf_Forms_CtrlButton.prototype.check = function (isChecked)
{
   if (isChecked)
      this.elem.addClassName('checked');
   else
      this.elem.removeClassName('checked');
}

/*******************************************************************************/
function Wsf_Ctrls_CmdButton(state)
{
    Wsf_Forms_CtrlButton.call(this, state);
}

Wsf.extend(Wsf_Ctrls_CmdButton, Wsf_Forms_CtrlButton);

/*******************************************************************************/
Wsf_Ctrls_CmdButton.prototype.initFromMarkup = function ()
{
    Wsf_Forms_CtrlButton.prototype.initFromMarkup.call(this);

    this.getElem().observe('click', this._onClick.bindAsEventListener(this));
}

/*******************************************************************************/
Wsf_Ctrls_CmdButton.prototype._onClick = function (ev)
{
    ev.stop();
    g_app.invokeCmd(
        this._cmdTarget ? this._cmdTarget : this,
        this.cmdId,
        this.cmdParams ? this.cmdParams : { });
}

/*******************************************************************************/
Wsf_Ctrls_CmdButton.prototype.updateUI = function ()
{
    if (!this.isVisible()) return;

    var cmdui = {
        ctrl : this,
        isEnabled : true,
        isChecked : false,
        enable: function (enable) { this.ctrl.enable(enable); },
        check: function (check) { this.ctrl.check(check); }
    };

    g_app.invokeUpdateCmd(this._cmdTarget ? this._cmdTarget : this, this.cmdId, cmdui);
}


function Wsf_UIMods_Users_CtrlLoginLogout(state)
{
    Wsf_Ctrls_Control.call(this, state);
}

Wsf.extend(Wsf_UIMods_Users_CtrlLoginLogout, Wsf_Ctrls_Control);

Wsf_UIMods_Users_CtrlLoginLogout.prototype.onCreate = function ()
{
    Wsf_Ctrls_Control.prototype.onCreate.call(this);

    var f = function (event)
    {
        event.stop();
        this.callServerMethod('onLogin');
    }

    var e = $(this.getId()+'.login');
    if (e) e.observe('click', f.bindAsEventListener(this));
}
Wsf_Web_Vocabulary.add({"Web" : {"MOTIVES" : "témata","AUTHORS" : "umělci","SEARCH" : "hledat","KEYWORDS" : "klíčová slova","VARIANT_SET_COLOR" : "barva samolepky","VARIANT_SET_SIZE" : "velikost archu","VARIANT_SET_MODEL" : "model","VARIANT_SET_VALUE" : "hodnota","PRICE" : "cena","ADD_TO_CART" : "přidat do košíku","ERROR" : "Omlouváme se, při zpracování Vašeho požadavku došlo k chybě. Zkuste, prosím, akci opakovat.","CALCULATING" : "počítá se...","ITEMS_IN_CART" : "položek v košíku","AMOUNT_PCS" : "počet ks","CONTINUE_SHOPPING" : "<< pokračovat v nákupu","ORDER" : "zvolit dopravu a platbu >>","UPDATE" : "uložit","DELETE" : "odebrat","ITEMS_TOTAL" : "celkem položky","SHIPMENT_TOTAL" : "dobírka (volitelně)","TOTAL" : "celkem","CART_NOTE" : "Nejsme plátci DPH.<br/>Osobní vyzvednutí zboží ZDARMA (ve vzorkové prodejně v Praze na Letné).<br/><b>Doprava a dobírka ZDARMA</b> po ČR pri nákupu za více než <b>%s</b>,- Kč","CART_EMPTY" : "Košík je prázdný, pokračujte v nákupu.","RELATED_PRODUCTS" : "související produkty","CLIENT" : "Zákazník","SIGNIN" : "Přihlašte se","SIGNOUT" : "Odhlásit","MORE" : "více >>","ARCHIVE" : "archiv"},"Wsf_Ctrls" : {"WSF_ROWS_PER_PAGE" : "Řádků na stránce","WSF_PAGE" : "Stránka","WSF_PREV_PAGE" : "« Předchozí","WSF_NEXT_PAGE" : "Další »","WSF_THUMBS" : "Náhledy","SORT_BY" : "Seřadit podle","WSF_LOCATION" : "Umístění","RESULTS" : "Výsledky","OF" : "z","CLOSE_WINDOW" : "Zavřít okno","LOADING" : "Pracuji"},"Wsf" : {"MONTH_1" : "leden","MONTH_2" : "únor","MONTH_3" : "březen","MONTH_4" : "duben","MONTH_5" : "květen","MONTH_6" : "červen","MONTH_7" : "červenec","MONTH_8" : "srpen","MONTH_9" : "září","MONTH_10" : "říjen","MONTH_11" : "listopad","MONTH_12" : "prosinec","MONTH_3LTRS_1" : "Led","MONTH_3LTRS_2" : "Úno","MONTH_3LTRS_3" : "Bře","MONTH_3LTRS_4" : "Dub","MONTH_3LTRS_5" : "Kvě","MONTH_3LTRS_6" : "Čer","MONTH_3LTRS_7" : "Čev","MONTH_3LTRS_8" : "Srp","MONTH_3LTRS_9" : "Zář","MONTH_3LTRS_10" : "Říj","MONTH_3LTRS_11" : "Lis","MONTH_3LTRS_12" : "Pro","DAY2_0" : "Po","DAY2_1" : "Út","DAY2_2" : "St","DAY2_3" : "Čt","DAY2_4" : "Pá","DAY2_5" : "So","DAY2_6" : "Ne","CANCEL" : "Storno","APPLY" : "Použít","INVALID_ARGUMENT" : "Neplatný parametr","INVALID_OPERATION" : "Neplatná operace: v aktuálním stavu nelze operaci provést.","OPERATION_FAILED" : "Akce se nezdařila. Zkuste jí zopakovat.","YES" : "Ano","NO" : "Ne"},"Wsf_Forms" : {"WSF_FORM_CAPTCHA" : "Opište kód: <b>%s</b>","WSF_FORM_CAPTCHA_HELP" : "<a href=\"http://cs.wikipedia.org/wiki/CAPTCHA\" target=\"_blank\">Co je to?: Opatření proti automatickým robotům</a>","WSF_FORM_CAPTCHA_INVALID" : "Špatně opsaný kód","WSF_FILEUPLOAD_ERROR" : "Chyba #%d při přijímaní souboru","WSF_FILEUPLOAD_TOO_BIG" : "Soubor je příliš veliký. Maximální velikost je %d KB.","WSF_FILEUPLOAD_BAD_TYPE" : "Nepovolený typ souboru. Povolené jsou pouze tyto typy: %s","WSF_FORM_REQUIRED_FIELDS" : "Pole označená <span class=\"required\">*</span> jsou povinná","WSF_FORM_INVALID_NUMBER" : "Zadejte číslo od <b>%d</b> do <b>%d</b>","WSF_FORM_REQUIRED_FIELD_EMPTY" : "Pole je povinné","WSF_FORM_INCORRECT_SYNTAX" : "Špatný tvar","WSF_FORM_PWD_NOT_SAME" : "Kontrolní heslo není stejné jako nové heslo","WSF_FORM_PWD_UNSAFE" : "Heslo musí mít alespoň 4 znaky","WSF_EMAIL_SENT_CLICK_BACK_TO_RETURN" : "E-mail odeslán. Vraťte se zpět na stránku:<br/><br/><a href=%s>%s</a>","WSF_YOUR_NAME" : "Vaše jméno","WSF_RECEPIENT_EMAIL" : "E-mail příjemce","WSF_MSG" : "Zpráva","WSF_SEND_EMAIL" : "Odeslat odkaz","WSF_PAGE" : "Stránka","INVALID_DATETIME_FORMAT" : "Neplatný formát data (DD.MM.RRRR H:M:S)","INVALID_DATE_FORMAT" : "Neplatný formát data (DD.MM.RRRR)","INVALID_PHONE_NUMBER" : "Neplatné mezinárodní telefonní číslo (+420 123 456 789)","COMBO_NO_SELECTION" : "[žádný výběr]"},"Wsf_UIMods_Users" : {"LOGOUT" : "Odhlásit","LOGIN_NAME" : "Uživatelské jméno","PWD" : "Heslo","LOGIN" : "Přihlásit","LOGIN_FAILED" : "Přihlášení se nezdařilo","NEW_ACCOUNT" : "Nový účet","FORGOT_PWD" : "Zapomenuté heslo","REGISTERED_EMAIL" : "Registrovaný e-mail","NAME" : "Jméno","SURNAME" : "Příjmení","EMAIL" : "E-mail","PWD_CHECK" : "Kontrola hesla","CREATE_NEW_ACCOUNT" : "Registrovat","MY_ACCOUNT" : "Můj účet","PWD_REMINDER" : "Připomenutí hesla","NICKNAME_HELP" : "Zvolte si přezdívku, pod kterou vás uvidí ostatní uživatelé webu","NICKNAME" : "Přezdívka","EMAIL_HELP" : "E-mail společně s heslem slouží jako přihlašovací údaje k webu","LOGIN_SUBJECT" : "Přihlášení"},"Wsf_UIMods_Eshop" : {"LOGIN_VERB" : "Přihlásit se >>","EMAIL" : "Váš e-mail","PWD" : "Heslo","NAME_SURNAME" : "Jméno a příjmení","PHONE" : "Telefon","STREET" : "Ulice","ZIP" : "PSČ","CITY" : "Město","ICO" : "IČ","DIC" : "DIČ","PWD_CHECK" : "Kontrola hesla","GO_TO_PAYMENT_SHIPMENT" : "Pokračovat k volbě dopravy a platby >>","GO_TO_ADDRESSES" : "Pokračovat k dodacím údajům >>","BACK" : "<< Zpět","GO_TO_FINAL_SUMARY" : "Pokračovat k závěrečnému shrnutí >>","ORDER" : "Závazně objednat","NOTES" : "Poznámka","ITEMS" : "Objednané produkty","CATALOG_CHANGED" : "Omlouváme se, ale došlo k aktualizaci katalogu zboží. Prosím, zkontrolujte, zda objednané položky a ceny odpovídají Vašim požadavkům.","SHIPMENT_DATA" : "Dodací adresa","INVOICE_DATA" : "Fakturační údaje","SHIPMENT_PAYMENT" : "Doručení a platba (krok 1/3)","PAYMENT" : "Platba","PAYMENT_PERSONAL" : "Hotově","PAYMENT_COD" : "Dobírka","PAYMENT_TRANSFER" : "Bankovní převod","CLIENT" : "Zákazník","DELIVERY_DATA" : "Dodací údaje (krok 2/3)","RULES_AGREE" : "Souhlasím s obchodními podmínkami","NEW_WINDOW" : "odkaz se otevře v novém okně","SEND_EMAIL" : "Souhlasím se zasíláním obchodních sdělení (např. speciální nabídky, novinky)","INVOICE_ADDRESS" : "Fakturační adresa","SHIPMENT" : "Doprava","CODE" : "Kód","ITEM_NAME" : "Název položky","UOM_COUNT" : "Počet MJ","UOM_PRICE" : "Cena/MJ","TOTAL" : "Celkem","GRAND_TOTAL" : "Celkem k zaplacení","MUST_AGREE" : "K dokončení objednávky musíte souhlasit s obchodními podmínkami","ORDER_SUMMARY" : "Závěrečné shrnutí (krok 3/3)","ORDER_FINISHED" : "Odesláno","SHIPMENT_ADDRESS_NOTE" : "Adresu nemusíte vyplňovat, pokud je stejná jako fakturační.","ACCOUNT_NUMBER" : "Číslo účtu","VARIABLE_SYMBOL" : "Variabilní symbol","SORT_BY" : "Seřadit podle","DEFAULT" : "Výchozí","TITLE" : "Název","MANUFACTURER" : "Výrobce","PRICE_ASC" : "Cena vzestupně","PRICE_DESC" : "Cena sestupně","KEYWORDS" : "Hledaná slova","NOT_FOUND" : "Nebylo nalezeno žádné zboží. Zkuste použít přibuzná slova nebo pozměnit tvar slov.","SHIPMENT_TOTAL" : "Dobírka (volitelně)","ITEMS_TOTAL" : "Položky celkem","CONTINUE_SHOPPING" : "<< Zpět na výběr zboží","CHOOSE_SHIPMENT" : "Zvolit doručení a platbu >>","UPDATE" : "Uložit","DELETE" : "Odebrat","AMOUNT_PCS" : "Počet","PRICE" : "Cena","CART_EMPTY" : "Košík je prázdný, pokračujte v nákupu.","NUMBER" : "Číslo","ISSUE_DATE" : "Datum vytvoření","ORDER_INFO" : "Objednávka","WITH_VAT" : "s DPH","AVAILABILITY" : "Dostupnost","ADD_TO_CART" : "Přidat do košíku","COMMON_PRICE" : "Běžná cena","FROM" : "od","SEARCH" : "Hledat","WITHIN_48H" : "skladem","PWD_CHANGE" : "Změna hesla","CHANGES_SAVED" : "Změny byly uloženy"},"Wsf_Web" : {"OLD_IE_NOTICE" : "Používáte <strong>zastaralý prohlížeč</strong> Internet Explorer %s. Zobrazená <strong>stránka nemusí fungovat správně</strong>.<br/>Požádejte vašeho administrátora o aktualizaci prohlížeče nebo si ji <a href=\"http://www.microsoft.com/cze/windows/internet-explorer/default.aspx\">stáhněte sami</a>.","OLD_FF_NOTICE" : "Používáte <strong>zastaralý prohlížeč</strong> Mozilla Firefox %s. Zobrazená <strong>stránka nemusí fungovat správně</strong>.<br/>Požádejte vašeho administrátora o aktualizaci prohlížeče nebo si ji <a href=\"http://www.getfirefox.com\">stáhněte sami</a>.","AJAX_SERVER_ERROR" : "Chyba při komunikaci se serverem. Zkuste akci opakovat nebo obnovit stránku (klávesa F5).","AJAX_FATAL_ERROR" : "Chyba webové aplikace. Aplikace bude znovu načtena."}});

