/* * +----------------------------------------------------------------+
*    | Author: www.program-ace.com                                    |
*    | 2005-2006 Copyright (c) Program-Ace.Inc                        |
*    | All rights reserved                                            |
*    +----------------------------------------------------------------+
*/

/* -- */

/*@*
*	PaValidator
*
*	------------------
*
*	Set of basic static validation methods
*@*/

var PaValidator = new Object;

/* *
*	Find out if value provided is not of undefined type.
*
*	@access public
*	@static
*	@param Value to examine
*	@return boolean
*/
PaValidator.isDefined = function (mValue)
{
    return typeof mValue != 'undefined';
};

/* *
*	Find out if value provided is null.
*
*	@access public
*	@static
*	@param Value to examine
*	@return boolean
*/
PaValidator.isNull = function (mValue)
{
    return PaValidator.isDefined(mValue) && mValue == null;
};

/* *
*	Find out if value provided is not null and is not empty string and is not empty array and is not false and is not zero :).
*
*	@access public
*	@static
*	@param Value to examine
*	@return boolean
*/
PaValidator.isEmpty = function (mValue)
{
    return !PaValidator.isDefined(mValue)
    ||
    PaValidator.isNull(mValue)
    ||
    (PaValidator.isBool(mValue) && mValue === false)
    ||
    (PaValidator.isArray(mValue) && PaArray.rebuild(mValue).length == 0)
    ||
    (PaValidator.isString(mValue) && mValue == '')
    ||
    parseInt(mValue) == 0;
};

/* *
*	Find out if value provided is a string.
*
*	@access public
*	@static
*	@param Value to examine
*	@return boolean
*/
PaValidator.isString = function (mValue)
{
    return typeof mValue == 'string';
};

/* *
*	Find out if value provided is an object. Null is not considered to be object but functions are.
*
*	@access public
*	@static
*	@param Value to examine
*	@return boolean
*/
PaValidator.isObject = function (mValue)
{
    return PaValidator.isDefined(mValue) && mValue != null && (typeof mValue == 'object' || typeof mValue == 'function');
};

/* *
*	Find out if value provided is node (whether temporal or appended to tree).
*
*	@access public
*	@static
*	@param Value to examine
*	@return boolean
*/
/*PaValidator.isNode = function (mValue)
{
    return PaValidator.isObject(mValue) && PaValidator.isDefined(mValue.tagName);
};*/
PaValidator.isNode = function (mValue)
{
    return PaValidator.isObject(mValue)
    &&
    /*PaValidator.isObject(mValue.parentNode)
    &&*/
    PaValidator.isDefined(mValue.nodeType);
};

/* *
*	Find out if value provided is element (tagged) node (whether temporal or appended to tree).
*
*	@access public
*	@static
*	@param Value to examine
*	@return boolean
*/
/*PaValidator.isElement = function (mValue)
{
    return PaValidator.isNode(mValue) && PaValidator.isDefined(mValue.parentNode);
};*/
PaValidator.isElement = function (mValue)
{
    return PaValidator.isNode(mValue) && mValue.nodeType == 1;
};

/* *
*	Find out if value provided is text (ex. space) node (whether temporal or appended to tree).
*
*	@access public
*	@static
*	@param Value to examine
*	@return boolean
*/
/*PaValidator.isElement = function (mValue)
{
    return PaValidator.isNode(mValue) && PaValidator.isDefined(mValue.parentNode);
};*/
PaValidator.isTextNode = function (mValue)
{
    return PaValidator.isNode(mValue) && mValue.nodeType == 3;
};

/* *
*	Find out if value provided is node of specified type (tag name).
*
*	@access public
*	@static
*	@param mixed Value to examine
*	@param mixed tag name to compare with
*	@return boolean
*/
PaValidator.isTag = function (mValue, sTag)
{
    return PaValidator.isElement(mValue) && mValue.tagName.toLowerCase() == sTag;
};

/* *
*	Find out if value provided is a function.
*
*	@access public
*	@static
*	@param Value to examine
*	@return boolean
*/
PaValidator.isFunction = function (mValue)
{
    return typeof mValue == 'function';
};

/* *
*	Find out if value provided is an Array object.
*
*	@access public
*	@static
*	@param Value to examine
*	@return boolean
*/
PaValidator.isArray = function (mValue)
{
    return PaValidator.isObject(mValue) && /*mValue.hasOwnProperty('constructor')*/ PaValidator.isDefined(mValue.constructor) && mValue.constructor == Array;
};

/* *
*	Find out if value provided is boolean.
*
*	@access public
*	@static
*	@param Value to examine
*	@return boolean
*/
PaValidator.isBool = function (mValue)
{
    return PaValidator.isDefined(mValue) && (mValue == true || mValue == false);
};

/* *
*	Find out if value provided is integer.
*
*	@access public
*	@static
*	@param Value to examine
*	@return boolean
*/
PaValidator.isInt = function (mValue)
{
    var oReg = new RegExp('^[+-]?\\d+$', 'g');

    return (typeof mValue == 'number' || typeof mValue == 'string') && oReg.test(mValue);
};

/* *
*	Find out if value provided is a number.
*
*	@access public
*	@static
*	@param Value to examine
*	@return boolean
*/
PaValidator.isFloat = function (mValue)
{
    var oReg = new RegExp('^(\\d*\\.\\d+|\\d+\\.\\d*|\\d+\\.\\d*[eE][+-]?\\d+)$', 'g');

    return (typeof mValue == 'number' || typeof mValue == 'string') && (oReg.test(mValue) || this.isInt(mValue));
};

/* *
*	Find out if value provided is a number with a floating point.
*
*	@access public
*	@static
*	@param Value to examine
*	@return boolean
*/
PaValidator.isRealFloat = function (mValue)
{
    var oReg = new RegExp('^(\\d*\\.\\d+|\\d+\\.\\d*|\\d+\\.\\d*[eE][+-]?\\d+)$', 'g');

    return (typeof mValue == 'number' || typeof mValue == 'string') && oReg.test(mValue);
};

/* *
*	Find out if value provided is a string containing float or integer.
*
*	@access public
*	@static
*	@param Value to examine
*	@return boolean
*/
PaValidator.isNumeric = function (mValue)
{
    return !PaValidator.isObject(mValue) && (PaValidator.isInt(mValue) || PaValidator.isFloat(mValue));
};

/* *
*	Find out if value provided is a string containing only numbers and letters.
*
*	@access public
*	@static
*	@param Value to examine
*	@return boolean
*/
PaValidator.isAlphaNumeric = function (mValue)
{
    var oReg = new RegExp('^\\w+$', 'g');

    return PaValidator.isString(mValue) && oReg.test(mValue);
};

/* *
*	Find out if value provided is a string containing email.
*
*	@access public
*	@static
*	@param Value to examine
*	@return boolean
*/
PaValidator.isEmail = function (mValue)
{
    var oReg = new RegExp("^[-!#\$%&'*+\\./0-9=?A-Z^_\\`a-z{|}~]+@[^.@]+(\.[^.@]+)+$", 'g');

    return PaValidator.isString(mValue) && oReg.test(mValue);
};

/* *
*	Find out if value provided is a string containing URL.
*
*	@access public
*	@static
*	@param Value to examine
*	@return boolean
*/
PaValidator.isUrl = function (mValue)
{
    var oReg = new RegExp("^((http(s?))|(ftp))://([^:#&?/\\\\]+?(:[^:#&?/\\\\]+?)?@)?((([a-zA-Z0-9\\-]+\\.)+[a-zA-Z0-9\\-]+)|((\\d{1,3}\\.){3}\\d{1,3}))(:\\d{1,5})?((((/[^/]+)+/?)|/))?(\\?.+?)?(\\#[^:#&?/\\\\]+?)?$", 'gi');

    return PaValidator.isString(mValue) && oReg.test(mValue);
};

/* *
*	Find out if value provided can possibly be a date.
*
*	@access public
*	@static
*	@param Value to examine
*	@return boolean
*/
PaValidator.isDate = function (mValue)
{
    var sAllowedDateDelimiters = './-';
    var sAllowedTimeDelimiters = '.:-';

    var oReg = new RegExp('^(\\d{2}|\\d{4})[' + sAllowedDateDelimiters + ']\\d{2}[' + sAllowedDateDelimiters + '](\\d{2}|\\d{4})(?: (\\d{2})[' + sAllowedTimeDelimiters + '](\\d{2})(?:[' + sAllowedTimeDelimiters + '](\\d{2}))?)?$', 'g');

    if (PaValidator.isString(mValue) && oReg.test(mValue))
    {
        if (RegExp.$1.length == 4 && RegExp.$2.length == 4)
        {
            return false;
        }

        if (RegExp.$3 != '' && !(RegExp.$3 >= 0 && RegExp.$3 < 25 && RegExp.$4 >= 0 && RegExp.$4 < 60))
        {
            return false;
        }

        if (RegExp.$5 != '' && !(RegExp.$5 >= 0 && RegExp.$5 < 60))
        {
            return false;
        }

        return true;
    }

    return false;
};

/* *
*	Find out if value provided is valid MySQL date.
*
*	@access public
*	@static
*	@param Value to examine
*	@return boolean
*/
PaValidator.isSqlDate = function (mValue)
{
    var oReg = new RegExp('^(\\d{1,4})-(\\d{1,2})-(\\d{1,2})$', 'g');

    if (PaValidator.isString(mValue) && oReg.test(mValue))
    {
        if (RegExp.$3 <= 0 && RegExp.$3 > 31)
        {
            return false;
        }

        if (RegExp.$2 <= 0 && RegExp.$2 > 12)
        {
            return false;
        }

        return true;
    }

    return false;
};

/* *
*	Find out if value provided is valid MySQL date.
*
*	@access public
*	@static
*	@param Value to examine
*	@return boolean
*/
PaValidator.isSqlTime = function (mValue)
{
    var oReg = new RegExp('^(\\d{1,2}):(\\d{1,2})(?:(?::(\\d{1,2}))?)$', 'g');

    if (PaValidator.isString(mValue) && oReg.test(mValue))
    {
        if (RegExp.$1 < 0 && RegExp.$1 > 23)
        {
            return false;
        }

        if (RegExp.$2 < 0 && RegExp.$2 > 59)
        {
            return false;
        }

        if (RegExp.$3 != '' (RegExp.$3 < 0 || RegExp.$3 > 60))
        {
            return false;
        }

        return true;
    }

    return false;
};

/* *
*	Find out if value provided is a string conforming to MySQL Datetime type.
*
*	@access public
*	@static
*	@param Value to examine
*	@return boolean
*/
PaValidator.isSqlDateTime = function (mValue)
{
    if (PaValidator.isString(mValue))
    {
        var aChunks = mValue.split(/\040/);

        if (aChunks.length == 2 && PaValidator.isSqlDate(aChunks[0]) && PaValidator.isSqlTime(aChunks[1]))
        {
            return true;
        }
    }

    return false;
};

/* *
*	Find out if value provided is a string containing only space characters.
*
*	@access public
*	@static
*	@param Value to examine
*	@return boolean
*/
PaValidator.isOnlySpaces = function (mValue)
{
    var oReg = new RegExp('^\\s+$', 'g');

    return PaValidator.isString(mValue) && oReg.test(mValue);
};

/* -- */

/*@*
*	PaCast
*
*	------------------
*
*	Set of basic static cast methods
*@*/

var PaCast = new Object;

/* *
*	Cast to integer.
*   3 tries given. 1 - logical cast. 2 - forced. 3 - "find something"
*   0 is default
*
*	@access public
*	@static
*	@param Value to parse
*	@return integer
*/
PaCast.toInt = function (mValue)
{
    var mTemp = Number(mValue);

    if (!isNaN(mTemp))
    {
        return mTemp;
    }

    var sTemp = mValue.toString();

    var mTemp = parseInt(sTemp);

    if (!isNaN(mTemp))
    {
        return mTemp;
    }

    var aMatches = sTemp.match(/[0-9]+/);

    if (!isNaN(aMatches[0]))
    {
        return aMatches[0];
    }
    else
    {
        return 0;
    }
};

/* -- */

/*@*
*	PaDebug
*
*	------------------
*
*	Set of basic static dump methods
*@*/

var PaDebug = new Object;

/* *
*	Find out object`s type and value length. In Mozilla literal representation length used in case of composite objects.
*
*	<code>
*		PaDebug.dump('medved');
*		PaDebug.dump({prop1 : 1, prop2 : 2, dummy : true});
*	</code>
*
*	<result>
*		string (6) "medved"
*		object (32) "({prop1:1, prop2:2, dummy:true})" (OR: object (15) "[object Object]")
*	</result>
*
*	@access public
*	@static
*	@param mixed
*	@return string
*/
PaDebug.dump = function (mValue)
{
    if (PaValidator.isObject(mValue) && PaClient.isMozilla())
    {
        return typeof mValue + ' (' + mValue.toSource().length + ') ' + '"' + mValue.toSource() + '"';
    }
    else
    {
        return typeof mValue + ' (' + String(mValue).length + ') ' + '"' + mValue + '"';
    }
};

/* -- */

/*@*
*	PaString
*
*	------------------
*
*	Set of static methods for string manipulations
*@*/

var PaString = new Object;

PaString._oHtmlEntities = {'39' : '&#039;', '160' : '&nbsp;', '161' : '&iexcl;', '162' : '&cent;', '163' : '&pound;', '164' : '&curren;', '165' : '&yen;', '166' : '&brvbar;', '167' : '&sect;', '168' : '&uml;', '169' : '&copy;', '170' : '&ordf;', '171' : '&laquo;', '172' : '&not;', '173' : '&shy;', '174' : '&reg;', '175' : '&macr;', '176' : '&deg;', '177' : '&plusmn;', '178' : '&sup2;', '179' : '&sup3;', '180' : '&acute;', '181' : '&micro;', '182' : '&para;', '183' : '&middot;', '184' : '&cedil;', '185' : '&sup1;', '186' : '&ordm;', '187' : '&raquo;', '188' : '&frac14;', '189' : '&frac12;', '190' : '&frac34;', '191' : '&iquest;', '192' : '&Agrave;', '193' : '&Aacute;', '194' : '&Acirc;', '195' : '&Atilde;', '196' : '&Auml;', '197' : '&Aring;', '198' : '&AElig;', '199' : '&Ccedil;', '200' : '&Egrave;', '201' : '&Eacute;', '202' : '&Ecirc;', '203' : '&Euml;', '204' : '&Igrave;', '205' : '&Iacute;', '206' : '&Icirc;', '207' : '&Iuml;', '208' : '&ETH;', '209' : '&Ntilde;', '210' : '&Ograve;', '211' : '&Oacute;', '212' : '&Ocirc;', '213' : '&Otilde;', '214' : '&Ouml;', '215' : '&times;', '216' : '&Oslash;', '217' : '&Ugrave;', '218' : '&Uacute;', '219' : '&Ucirc;', '220' : '&Uuml;', '221' : '&Yacute;', '222' : '&THORN;', '223' : '&szlig;', '224' : '&agrave;', '225' : '&aacute;', '226' : '&acirc;', '227' : '&atilde;', '228' : '&auml;', '229' : '&aring;', '230' : '&aelig;', '231' : '&ccedil;', '232' : '&egrave;', '233' : '&eacute;', '234' : '&ecirc;', '235' : '&euml;', '236' : '&igrave;', '237' : '&iacute;', '238' : '&icirc;', '239' : '&iuml;', '240' : '&eth;', '241' : '&ntilde;', '242' : '&ograve;', '243' : '&oacute;', '244' : '&ocirc;', '245' : '&otilde;', '246' : '&ouml;', '247' : '&divide;', '248' : '&oslash;', '249' : '&ugrave;', '250' : '&uacute;', '251' : '&ucirc;', '252' : '&uuml;', '253' : '&yacute;', '254' : '&thorn;', '255' : '&yuml;', '402' : '&fnof;', '913' : '&Alpha;', '914' : '&Beta;', '915' : '&Gamma;', '916' : '&Delta;', '917' : '&Epsilon;', '918' : '&Zeta;', '919' : '&Eta;', '920' : '&Theta;', '921' : '&Iota;', '922' : '&Kappa;', '923' : '&Lambda;', '924' : '&Mu;', '925' : '&Nu;', '926' : '&Xi;', '927' : '&Omicron;', '928' : '&Pi;', '929' : '&Rho;', '931' : '&Sigma;', '932' : '&Tau;', '933' : '&Upsilon;', '934' : '&Phi;', '935' : '&Chi;', '936' : '&Psi;', '937' : '&Omega;', '945' : '&alpha;', '946' : '&beta;', '947' : '&gamma;', '948' : '&delta;', '949' : '&epsilon;', '950' : '&zeta;', '951' : '&eta;', '952' : '&theta;', '953' : '&iota;', '954' : '&kappa;', '955' : '&lambda;', '956' : '&mu;', '957' : '&nu;', '958' : '&xi;', '959' : '&omicron;', '960' : '&pi;', '961' : '&rho;', '962' : '&sigmaf;', '963' : '&sigma;', '964' : '&tau;', '965' : '&upsilon;', '966' : '&phi;', '967' : '&chi;', '968' : '&psi;', '969' : '&omega;', '977' : '&thetasym;', '978' : '&upsih;', '982' : '&piv;', '8226' : '&bull;', '8230' : '&hellip;', '8242' : '&prime;', '8243' : '&Prime;', '8254' : '&oline;', '8260' : '&frasl;', '8472' : '&weierp;', '8465' : '&image;', '8476' : '&real;', '8482' : '&trade;', '8501' : '&alefsym;', '8592' : '&larr;', '8593' : '&uarr;', '8594' : '&rarr;', '8595' : '&darr;', '8596' : '&harr;', '8629' : '&crarr;', '8656' : '&lArr;', '8657' : '&uArr;', '8658' : '&rArr;', '8659' : '&dArr;', '8660' : '&hArr;', '8704' : '&forall;', '8706' : '&part;', '8707' : '&exist;', '8709' : '&empty;', '8711' : '&nabla;', '8712' : '&isin;', '8713' : '&notin;', '8715' : '&ni;', '8719' : '&prod;', '8721' : '&sum;', '8722' : '&minus;', '8727' : '&lowast;', '8730' : '&radic;', '8733' : '&prop;', '8734' : '&infin;', '8736' : '&ang;', '8743' : '&and;', '8744' : '&or;', '8745' : '&cap;', '8746' : '&cup;', '8747' : '&int;', '8756' : '&there4;', '8764' : '&sim;', '8773' : '&cong;', '8776' : '&asymp;', '8800' : '&ne;', '8801' : '&equiv;', '8804' : '&le;', '8805' : '&ge;', '8834' : '&sub;', '8835' : '&sup;', '8836' : '&nsub;', '8838' : '&sube;', '8839' : '&supe;', '8853' : '&oplus;', '8855' : '&otimes;', '8869' : '&perp;', '8901' : '&sdot;', '8968' : '&lceil;', '8969' : '&rceil;', '8970' : '&lfloor;', '8971' : '&rfloor;', '9001' : '&lang;', '9002' : '&rang;', '9674' : '&loz;', '9824' : '&spades;', '9827' : '&clubs;', '9829' : '&hearts;', '9830' : '&diams;', '34' : '&quot;', '38' : '&amp;', '60' : '&lt;', '62' : '&gt;', '338' : '&OElig;', '339' : '&oelig;', '352' : '&Scaron;', '353' : '&scaron;', '376' : '&Yuml;', '710' : '&circ;', '732' : '&tilde;', '8194' : '&ensp;', '8195' : '&emsp;', '8201' : '&thinsp;', '8204' : '&zwnj;', '8205' : '&zwj;', '8206' : '&lrm;', '8207' : '&rlm;', '8211' : '&ndash;', '8212' : '&mdash;', '8216' : '&lsquo;', '8217' : '&rsquo;', '8218' : '&sbquo;', '8220' : '&ldquo;', '8221' : '&rdquo;', '8222' : '&bdquo;', '8224' : '&dagger;', '8225' : '&Dagger;', '8240' : '&permil;', '8249' : '&lsaquo;', '8250' : '&rsaquo;', '8364' : '&euro;'};

/* *
*	Get hash of all HTML entities.
*
*	@access public
*	@static
*	@return object
*/
PaString.getHtmlEntities = function ()
{
    return PaString._oHtmlEntities;
};

/* *
*	Strip tags.
*
*	@access public
*	@static
*	@param string
*	@return string
*/
PaString.stripTags = function (sValue)
{
    return sValue.replace(/<\/?[^>]+>/g, '');
};

/* *
*	Strip blank characters from the left of the string.
*
*	@access public
*	@static
*	@param string
*	@return string
*/
PaString.ltrim = function (sValue)
{
    return sValue.replace(/^\s+/, '');
};

/* *
*	Strip blank characters from the right of the string.
*
*	@access public
*	@static
*	@param string
*	@return string
*/
PaString.rtrim = function (sValue)
{
    return sValue.replace(/\s+$/, '');
};

/* *
*	Strip blank characters from both sides of the string.
*
*	@access public
*	@static
*	@param string
*	@return string
*/
PaString.trim = function (sValue)
{
    return PaString.rtrim(PaString.ltrim(sValue));
};

/* *
*	Wrap a string to a number of characters provided as a second parameter.
*
*	@author ask jeronimo[at]mail.ace
*	@access public
*	@static
*	@param string to wrap
*	@param int column width
*	@param string of text to force wrap right in it`s current position
*	@return string of text to wrap the text to the next line
*/
PaString.wrap = function (sText, linewidth, sSeparator, sBreakchar)
{
    var s    = '';
    var pos1 = 0;
    var pos2 = 0;

    if (sText.length < linewidth)
    {
        return sText;
    }

    while (pos1 < sText.length)
    {
        var ss      = sText.substr(pos1, linewidth);
        var pos_tmp = 0;

        if (ss.indexOf(sSeparator) >= 0)
        {
            while (ss.indexOf(sSeparator, pos_tmp) >= 0)
            {
                pos_tmp = ss.indexOf(sSeparator, pos_tmp) + 1;
            }

            if (pos_tmp > Math.round(linewidth / 4))
            {
                pos2 = pos1 + pos_tmp;
            }
            else
            {
                pos2 = pos1 + linewidth;
            }
        }
        else
        {
            pos2 = pos1 + linewidth;
        }

        s   += sText.substring(pos1, pos2) + sBreakchar;
        pos1 = pos2;
    }

    return s;
};

/* *
*	Escape Url components
*
*	@access public
*	@param string Url component
*	@static
*	@return escaped string
*/
PaString.encodeUrl = function (sString)
{
    if (PaValidator.isFunction(encodeURIComponent))
    {
        return encodeURIComponent(sString);
    }
    else
    {
        return escape(sString);
    }
};

/* *
*	Translate ampersand, double quote, single quote, less than and greater than signs into HTML entities
*   If second param is provided then single quote is not translated.
*
*	@access public
*	@param string
*	@param boolean
*	@static
*	@return translated string
*/
PaString.encodeHtml = function (sString, bNoEncodeSingle)
{
    var sResult = '';
    var aChars  = sString.split('');
    var oHtmlEntities = PaString.getHtmlEntities();

    for (var c = 0; c < aChars.length; c++)
    {
        var charCode = aChars[c].charCodeAt(0);

        switch (charCode)
        {
            case 34 :
            case 38 :
            case 60 :
            case 62 :
            {
                sResult += oHtmlEntities[charCode];
                break;
            }
            default :
            {
                if (charCode == 39 && !PaValidator.isDefined(bNoEncodeSingle))
                {
                    sResult += oHtmlEntities[charCode];
                    break;
                }

                sResult += aChars[c];
                break;
            }
        }
    }

    return sResult;
};

/* *
*	Translate all chars to their decimal HTML entities.
*   ASCII symbols won`t be affected.
*
*	@access public
*	@param string to translate
*	@static
*	@return translated string
*/
PaString.encodeEntities = function (sString)
{
    var oEntities = PaString.getHtmlEntities();
    var sResult   = '';
    var aChars    = sString.split('');

    for (var c = 0; c < aChars.length; c++)
    {
        var charCode = aChars[c].charCodeAt(0);

        if (PaValidator.isDefined(oEntities[charCode]))
        {
            sResult += oEntities[charCode];
        }
        else if
        (
            /\w|\s/.test(aChars[c])
            ||
            charCode < 129
        )
        {
            sResult += aChars[c];
        }
        else
        {
            sResult += '&#' + aChars[c].charCodeAt(0) + ';';
        }
    }

    return sResult;
};

/* *
*	Takes string and puts a backslash in front of every character that is
*	part of the regular expression syntax. This is useful if you have a run-time string
*	that you need to match in some text and the string may contain special regex characters.
*
*	@author varela[at]mail.ace
*	@access public
*	@param string pattern to prepare
*	@static
*	@return string prepared
*/
PaString.prepareRegExp = function (sString)
{
    var sSpecialChars = "\\.+*?[^]$(){}=!<>|";

    for (var i = 0; i < sSpecialChars.length; i++)
    {
        sString = sString.replace(new RegExp("\\" + sSpecialChars.charAt(i), "g"), "\\" + sSpecialChars.charAt(i));
    }

    return sString;
};

PaString.uppercaseFirst = function (sString)
{
    return sString.substr(0, 1).toUpperCase() + sString.substr(1);
};

/* -- */

/*@*
*	PaObject
*
*	------------------
*
*	Set of basic static methods for object and their properties manipulations.
*	Can be applied to arrays as well (but wisely, keeping in mind native enamerable array properties).
*@*/

/*Function.prototype.extends = function (oSuperClass)
{
    for (var sProperty in oSuperClass)
    {
        this.prototype[sProperty] = oSuperClass[sProperty];
    }
};*/

/*var PaObject = function (object)
{
    if (!PaValidator.isDefined(object))
    {
        object = new Object;
    }

    for (var i in this)
    {
        object[i] = this[i];
    }

    return object;
};*/

var PaObject = new Object;

/* *
*	Apply custom function to each object`s property.
*	Function should get two paramenters: first one for a property name, second - for the property value.
*	If custom function returns - this method returns the same (just in case you want to break the loop).
*
*	<code>
*	var oDump = function (p, v)
*	{
*		document.write(p + ' : ' + v + '<br />');
*	};
*
*	PaObject.iterateProps({prop1 : 1, prop2 : [1,2], dummy : true}, oDump);
*	</code>
*
*	<result>
*	prop1 : 1
*	prop2 : 1,2
*	0 : 1
*	1 : 2
*	dummy : true
*	</result>
*
*	@access public
*	@static
*	@param object Object to iterate
*	@param function Custom function to apply
*	@param mixed optional
*	@return mixed
*/
PaObject.iterateProps = function (oObject, oFunction)
{
    arguments.slice = Array.prototype.slice;
    var oArguments  = arguments.slice(2, arguments.length);

    for (var p in oObject)
    {
        if (PaValidator.isDefined(mResult = oFunction(p, oObject[p], oArguments)))
        {
            return mResult;
        }

        PaObject.iterateProps(oObject[p], oFunction, oArguments);
    }
};

/* *
*	Get property by value.
*	If value is provided as a second parameter optional compare will take place and boollean will be returned.
*
*	<code>
*		document.write(PaObject.findProp({prop1 : 1, prop2 : 2, dummy : true}, 'dummy'));
*	</code>
*
*	<result>
*		true
*	</result>
*
*	<code>
*		document.write(PaObject.findProp({prop1 : 1, prop2 : 2, dummy : true}, 'dummy', 42));
*	</code>
*
*	<result>
*		false
*	</result>
*
*	@access public
*	@static
*	@param object Object to examine
*	@param string Property name
*	@param mixed Optional. Property value to compare with existing one
*	@return mixed If third paramener is not provided property value will be returned.
*/
PaObject.findProp = function (oObject, sProperty, mValue)
{
    if
    (
        PaValidator.isDefined(
            mResult = PaObject.iterateProps(
                                oObject,
                                function (p, v)
                                {
                                    if (p == sProperty)
                                    {
                                        if (PaValidator.isDefined(mValue))
                                        {
                                            return v == mValue;
                                        }
                                        else
                                        {
                                            return v;
                                        }
                                    }
                                }
            )
        )
    )
    {
        return mResult;
    }
    else
    {
        return null;
    }
};

/* *
*	Dump by alert all enumerable object properties.
*
*	<code>
*		PaObject.alert({prop1 : 1, prop2 : 2, dummy : true});
*	</code>
*
*	@access public
*	@static
*	@param object
*	@return void
*/
PaObject.alert = function (oObject)
{
    /*if (!PaValidator.isDefined(oObject) || PaValidator.isNull(oObject)) oObject = this.getSelf();*/

    PaObject.iterateProps(
                        oObject,
                        function (p, v)
                        {
                            alert(p + ' : ' + PaDebug.dump(v));
                        }
                    );

};

/* *
*	Walk through properties on first level, get their values, encode and assemble URL.
*
*	<code>
*		document.write(PaObject.buildUrl({prop1 : 1, prop2 : 2, dummy : true}));
*	</code>
*
*	<result>
*		prop1=1&prop2=2&dummy=true
*	</result>
*
*	@access public
*	@static
*	@param object
*	@return string
*/
PaObject.buildUrl = function (oObject)
{
    /*if (!PaValidator.isDefined(oObject) || PaValidator.isNull(oObject)) oObject = this.getSelf();*/

    var aTemp = new Array;

    for (var p in oObject)
    {
        aTemp[aTemp.length] = encodeURIComponent(p) + '=' + encodeURIComponent(oObject[p]);
    }

    return aTemp.join('&');
};

/* *
*	Simply count properties on first level.
*
*	@access public
*	@static
*	@param object
*	@return integer
*/
PaObject.getLength = function (oObject)
{
    var n = 0;

    for (var p in oObject)
    {
        n++;
    }

    return n;
};

/* *
*	Copy all enumerable properties to the new object returned.
*
*	@access public
*	@static
*	@param object to copy properties from
*	@return integer
*/
PaObject.copy = function (oObject)
{
    var o = new Object;

    for (var p in oObject)
    {
        o[p] = oObject[p];
    }

    return o;
};

/* -- */

/*@*
*	PaNode
*
*	------------------
*
*	Class for basic operations with DOM node.
*	Should be a superclass for all element-targeted classes.
*@*/

var PaNode = function ()
{

};

/* *
*	Create node by tag name.
*
*	@access public
*	@param string element`s ID
*	@return object PaNode instance.
*/
PaNode.prototype.create = function (sTagName)
{
    var seed = document.createElement(sTagName);

    for (var i in this)
    {
        seed[i] = this[i];
    }

    return seed;
};

/* *
*	Create text node.
*
*	@access public
*	@param string element value
*	@return object PaNode instance.
*/
PaNode.prototype.createTextual = function (sValue)
{
    var seed = document.createTextNode(sValue);

    for (var i in this)
    {
        seed[i] = this[i];
    }

    return seed;
};

/* *
*	Getter for parent node.
*
*	@access public
*	@return object (remember that null is object is well)
*/
PaNode.prototype.getParent = function ()
{
    return this.parentNode;
};

/* *
*	Get all ancestors.
*
*	@access public
*	@return object ancestors list
*/
PaNode.prototype.getAncestors = function ()
{
    var aAncestors = new Array;

    var oParent = this;

    while (!PaValidator.isEmpty(oParent.parentNode))
    {
        aAncestors.push(oParent.parentNode);

        oParent = oParent.parentNode;
    }

    return aAncestors;
};

/* *
*	Getter for child nodes.
*
*	@access public
*	@return object
*/
PaNode.prototype.getChildren = function ()
{
    return this.childNodes;
};

/* *
*	Wrapper for native appendChild.
*
*	@access public
*	@return object
*/
PaNode.prototype.addChild = function (oChild)
{
    this.appendChild(oChild);
};

/* *
*	Get children by tag name.
*
*	@access public
*	@param string tag name
*	@return object children collection or null
*/
PaNode.prototype.getChildrenByTag = function (sTagName)
{
    return this.getElementsByTagName(sTagName);
};

/*PaNode.prototype.removeChild = function (oChild)
{
    [has native implementation]
};*/

/* *
*	Node to array convertion.
*
*	@access public
*	@return object array of values
*/
PaNode.prototype.toHtmlData = function ()
{
    var aTemp = new Array;

    var _saveValue = function (oNode, aData)
    {
        if (!(PaValidator.isTag(oNode, 'td') || PaValidator.isTag(oNode, 'li') || PaValidator.isTag(oNode, 'option')) && oNode.hasChildNodes() && oNode.childNodes.length > 0)
        {
            for (var c = 0; c < oNode.childNodes.length; c++)
            {
                if (!(oNode.childNodes[c].nodeType == 3 && typeof oNode.childNodes[c].tagName == 'undefined' && (oNode.childNodes.length > 1 || (oNode.childNodes.length > 0 && oNode.childNodes[c].nodeValue == ''))))
                {
                    var arrayLength = aData.length;

                    aData[arrayLength] = new Array;

                    _saveValue(oNode.childNodes[c], aData[arrayLength]);
                }
            }
        }
        else
        {
            if (PaValidator.isDefined(oNode.innerHTML))
            {
                aData.push(oNode.innerHTML);
            }
            else if (PaValidator.isDefined(oNode.textContent))
            {
                aData.push(oNode.textContent);
            }
            else if (PaValidator.isDefined(oNode.nodeValue))
            {
                aData.push(oNode.nodeValue);
            }
        }
    };

    _saveValue(this, aTemp);

    return aTemp;
};

/* *
*	Apply custom function to each child.
*
*	@access public
*	@param object Object to iterate or null
*	@param function Custom function to apply
*	@param mixed optional
*	@return void
*/
PaNode.prototype.iterateChildren = function (oNode, oFunction)
{
    if (!PaValidator.isDefined(oNode) || PaValidator.isNull(oNode))
    {
        oNode = this;
    }

    var oChildren = oNode.childNodes;

    if (oChildren.length)
    {
        for (var i = 0; i < oChildren.length; i++)
        {
            this.iterateChildren(oChildren[i], oFunction, arguments[2]);
        }
    }

    oFunction(oNode, arguments[2]);
};

/*make it static*/
/*PaObject = new PaObject;

PaNode.extends(PaObject);*/

/* -- */

/*@*
*	PaNodeList
*
*	------------------
*
*	Class for basic operations with DOM collections.
*@*/

var PaNodeList = new Object;

/* *
*
*	Remove empty space nodes.
*
*	@static
*	@access public
*	@param object node collection
*	@return void
*/
PaNodeList.condense = function (aCollection)
{
    for (var c = 0; c < aCollection.length; c++)
    {
        if (aCollection[c].nodeType == 3 && typeof aCollection[c].tagName == 'undefined')
        {
            aCollection[c].parentNode.removeChild(aCollection[c]);
        }
    }
};

/* *
*
*	Filter collention by custom function.
*
*	@static
*	@access public
*	@param object node collection
*	@param object function which decides whether to keep current node
*	@return object new nodes list
*/
PaNodeList.filter = function (aCollection, oFunction)
{
    var aTemp = new Array;

    for (var c = 0; c < aCollection.length; c++)
    {
        if (oFunction(aCollection[c]))
        {
            aTemp.push(aCollection[c]);
        }
    }

    return aTemp;
};

/* -- */

/*@*
*	PaElement
*
*	------------------
*
*	Class for operations with real DOM nodes (elements).
*	Should be a superclass for all element-targeted classes.
*@*/

var PaElement = function (mElement)
{
    if (PaValidator.isString(mElement))
    {
        var seed = document.getElementById(mElement);
    }
    else if (PaValidator.isObject(mElement))
    {
        var seed = mElement;
    }

    if (PaValidator.isDefined(seed) && !PaValidator.isNull(seed))
    {
        for (var i in this)
        {
            seed[i] = this[i];
        }
    }
    else if (PaValidator.isDefined(mElement))
    {
        alert('Element with ID "' + mElement + '" not found');

        return false;
    }

    return seed;
};

PaElement.prototype = new PaNode;

/* *
*	Get reference to an element.
*
*	@access public
*	@param string Element`s ID
*	@return object Element or null if element is not found
*/
/*PaElement.prototype.get = function (sElementId)
{
    return document.getElementById(sElementId);
};*/

/* *
*	Get coordinated for an element.
*
*	BUGGY: fails when called dynamicaly in mouseover event handler over CSS cutted page
*
*	@access public
*	@return object Object with x and y properties
*/
PaElement.prototype.getCoords = function ()
{
    var x = 0, y = 0, oElement = this;

    while (oElement.offsetParent != null)
    {
        x += parseInt(oElement.offsetLeft);
        y += parseInt(oElement.offsetTop);

        oElement = oElement.offsetParent;
    }

    x += oElement.offsetLeft;
    y += oElement.offsetTop;

    return {'x': x, 'y': y};
};

/* *
*	Hide element.
*
*	@access public
*	@return void
*/
PaElement.prototype.hide = function ()
{
    this.style.display = 'none';
};

/* *
*	Reveal element.
*
*	@access public
*	@return void
*/
PaElement.prototype.show = function ()
{
    var aBlockElements = ['ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DIR', 'DIV', 'DL', 'FIELDSET', 'FORM', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'HR', 'ISINDEX', 'MENU', 'NOFRAMES', 'NOSCRIPT', 'OL', 'P', 'PRE', 'TABLE', 'UL', 'DD', 'DT', 'FRAMESET', 'LI', 'TBODY', 'TD', 'TFOOT', 'TH', 'THEAD'];

    var aRowElements = ['TR'];

    if (PaArray.valueExists(aBlockElements, this.tagName) || (PaClient.isIe() && PaArray.valueExists(aRowElements, this.tagName)))
    {
        this.style.display = 'block';
    }
    else if (PaArray.valueExists(aRowElements, this.tagName))
    {
        this.style.display = 'table-row';
    }
    else
    {
        this.style.display = 'inline';
    }
};

/* *
*	Find out if element hasn`t been hidden by means of CSS.
*
*	@access public
*	@return void
*/
PaElement.prototype.isVisible = function ()
{
    return this.style.display != 'none';
};

/* *
*	Position element at coordinates provided.
*
*	@access public
*	@param object List of x and y coordinates
*	@return void
*/
PaElement.prototype.place = function (aCoords)
{
    this.style.left = aCoords[0] + 'px';
    this.style.top  = aCoords[1] + 'px';
};

/* *
*	Helps to make element to be on the same place of the visible page area upon mouse scrolling and window resize.
*
*	Note: In Opera works only when scrolling down.
*
*	@access public
*	@return boolean true
*/
/*PaElement.prototype.stickToPage = function ()
{
    var onScroll = function (oEvent, oArgs)
    {
        var oElement = new PaElement(oArgs[0]);
        console.debug(true);
        //var oElement = new PaElement(this);
console.debug([PaPage.getView().x, PaPage.getView().y]);
        //oElement.place([(PaPage.getSize().x - parseInt(oElement.getInlineStyle().width)) / 2, (PaPage.getSize().y - parseInt(oElement.getInlineStyle().height)) / 2]);
        oElement.place([PaPage.getView().x * parseInt(oElement.getInlineStyle().left) / oElement._viewPort.x, PaPage.getView().y * parseInt(oElement.getInlineStyle().top) / oElement._viewPort.y]);
    };

    var onResize = function (oEvent, oArgs)
    {
        var oElement = new PaElement(oArgs[0]);
        console.debug(true);
        //var oElement = new PaElement(this);
console.debug([PaPage.getView().x, PaPage.getView().y]);
        //oElement.place([(PaPage.getSize().x - parseInt(oElement.getInlineStyle().width)) / 2, (PaPage.getSize().y - parseInt(oElement.getInlineStyle().height)) / 2]);
        oElement.place([PaPage.getView().x * parseInt(oElement.getInlineStyle().left) / oElement._viewPort.x, PaPage.getView().y * parseInt(oElement.getInlineStyle().top) / oElement._viewPort.y]);
    };

    this._viewPort = PaPage.getView();


    //PaEvent.addHandler('onload', onScroll, __BODY__, this);

    PaEvent.addHandler('onresize', onResize, __WINDOW__, this);

    //PaEvent.addHandler('onmousewheel', onScroll, __BODY__, this);

    //PaEvent.addHandler('DOMMouseScroll', onScroll, __WINDOW__, this);

    return true;
};*/

/* *
*	Get style.
*	In case of Mozilla computed style returned. If IE - "style" attribute declaration.
*
*	@access public
*	@return object css style declaration
*/
PaElement.prototype.getStyle = function ()
{
    if (PaClient.isIe())
    {
        return this.currentStyle;
    }
    else
    {
        return document.defaultView.getComputedStyle(this, null);
    }
};

/* *
*	Get css declarations (both inline and external).
*	Does not support inheritance and multiple declarations.
*
*	Note: make sure to require ALL css files by the same protocol as the page.
*
*	@access public
*	@return object style declaration
*/
PaElement.prototype.getInlineStyle = function ()
{
    var aClasses = this.className.replace(/\s{2,}/, ' ').split(/\040/);

    for (var f = 0; f < document.styleSheets.length; f++)
    {
        if (PaClient.isIe())
        {
            var oStyleSheet = document.styleSheets[f].rules;
        }
        else
        {
            var oStyleSheet = document.styleSheets[f].cssRules;
        }

        for (var s = 0; s < oStyleSheet.length; s++)
        {
            var aRules = oStyleSheet[s].selectorText.split(/,/);

            aRules = PaArray.walk(aRules, PaString.trim);

            for (var r = 0; r < aRules.length; r++)
            {

                for (var c = 0; c < aClasses.length; c++)
                {
                    if
                    (
                        aRules[r] == this.tagName
                        ||
                        aRules[r] == this.tagName + '.' + aClasses[c]
                        ||
                        aRules[r] == '.' + aClasses[c]
                        ||
                        aRules[r] == this.tagName + '#' + this.id
                        ||
                        aRules[r] == '#' + this.id
                    )
                    {
                        return oStyleSheet[s].style;
                    }
                }
            }
        }
    }
};

/* *
*	Get element`s width.
*
*	@access public
*	@return integer
*/
PaElement.prototype.getWidth = function ()
{
    if (PaClient.isIe())
    {
        return parseInt(this.offsetWidth + 0);
    }
    else
    {
        return parseInt(this.getStyle().width + 0);
    }
};

/* *
*	Get element`s height.
*
*	@access public
*	@return integer
*/
PaElement.prototype.getHeight = function ()
{
    if (PaClient.isIe())
    {
        return parseInt(this.offsetHeight + 0);
    }
    else
    {
        return parseInt(this.getStyle().height + 0);
    }
};

/* *
*	Fire user-defined event. Only mouse events implemented for now.
*
*	TODO: implement support for other event plugins in Mozilla
*
*	@access public
*	@param string
*	@return integer
*/
PaElement.prototype.fireEvent = function (sEventName)
{
    if (!PaClient.isIe())
    {
        var oEvent = document.createEvent('MouseEvents');
        oEvent.initMouseEvent(sEventName.substr(2), true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);

        this.dispatchEvent(oEvent);
    }
    else
    {
        this.fireEvent(sEventName);
    }
};

/* *
*	Mutate element so it can be dragged and removed. Enable support for user-defined triggers.
*
*	@access public
*	@return boolean true
*/
PaElement.prototype.makeDraggable = function ()
{
    if (PaValidator.isDefined(this.id))
    {
        if (!PaValidator.isDefined(document._PaDraggables))
        {
            document._PaDraggables            = new Object;
            document._PaDraggables.ids        = new Object;
            document._PaDraggables.lastId     = null;
        }

        document._PaDraggables.ids[this.id] = {'target' : this.id, 'triggerX' : 0, 'triggerY' : 0};
    }
    else
    {
        alert('Element has no ID');

        return false;
    }

    var down = function (oEvent)
    {
        PaEvent.prepare(oEvent);

        if
        (
            PaValidator.isDefined(PaEvent.target.id)
            &&
            PaValidator.isDefined(document._PaDraggables)
            &&
            PaValidator.isDefined(document._PaDraggables.ids[PaEvent.target.id])
        )
        {
            document._PaDraggables.lastId = PaEvent.target.id;
            document._PaDraggables.ids[PaEvent.target.id].triggerX = PaEvent.event.elementX;
            document._PaDraggables.ids[PaEvent.target.id].triggerY = PaEvent.event.elementY;

            PaEvent.target.style.zIndex = 2000;

            PaEvent.stop();
            PaEvent.prevent();

            return false;
        }
    };

    var up = function (oEvent)
    {
        if
        (
            PaValidator.isDefined(document._PaDraggables)
            &&
            !PaValidator.isNull(document._PaDraggables.lastId)
        )
        {
            PaEvent.prepare(oEvent);

            document._PaDraggables.ids[document._PaDraggables.lastId].triggerX = 0;
            document._PaDraggables.ids[document._PaDraggables.lastId].triggerY = 0;

            document._PaDraggables.lastId = null;

            PaEvent.stop();
            PaEvent.prevent();

            return false;
        }
    };

    var move = function (oEvent)
    {
        if (!PaValidator.isNull(document._PaDraggables.lastId))
        {
            PaEvent.prepare(oEvent);

            var newLeft  = PaEvent.event.pageX - document._PaDraggables.ids[document._PaDraggables.lastId].triggerX;
            var newRight = PaEvent.event.pageY - document._PaDraggables.ids[document._PaDraggables.lastId].triggerY;

            document.getElementById(document._PaDraggables.ids[document._PaDraggables.lastId].target).style.left = newLeft + 'px';
            document.getElementById(document._PaDraggables.ids[document._PaDraggables.lastId].target).style.top  = newRight + 'px';

            var sIframeId = document._PaDraggables.ids[document._PaDraggables.lastId].target.replace(/_/, '-iframe_');

            document.getElementById(sIframeId).style.left = newLeft + 'px';
            document.getElementById(sIframeId).style.top  = newRight + 'px';

            PaEvent.stop();
            PaEvent.prevent();

            return false;
        }
    };

    PaEvent.addHandler('onmousemove', move);
    PaEvent.addHandler('onmousedown', down);
    PaEvent.addHandler('onmouseup',   up);

    return true;
};

/* *
*	Register element to be a dragging hot-spot.
*	Makes sense only for element already made draggable.
*
*	@access public
*	@param string element`s ID
*	@return boolean true
*/
PaElement.prototype.addDragTrigger = function (triggerId)
{
    if (PaValidator.isDefined(this.id))
    {
        document._PaDraggables.ids[triggerId] = {'target' : this.id, 'triggerX' : 0, 'triggerY' : 0};

        PaEvent.addHandler('onmousedown', function (oEvent) {this.style.cursor = 'move';}, triggerId);
        PaEvent.addHandler('onmouseup',   function (oEvent) {this.style.cursor = 'default';}, triggerId);
    }
    else
    {
        alert('Element has no ID');

        return false;
    }

    return true;
};

/* *
*	Register element to be a close hot-spot.
*	Makes sense only for element already made draggable.
*
*	@access public
*	@param string element`s ID
*	@return boolean true
*/
PaElement.prototype.addCloseTrigger = function (triggerId)
{
    if (PaValidator.isDefined(this.id))
    {
        var close = function (oEvent, oArgs)
        {
            var oElement = new PaElement(oArgs[0]);
            oElement.clonePrototype.cloned = false;
            oElement.remove();

            var oElement = new PaElement(oArgs[0].replace(/_/, '-iframe_'));
            oElement.remove();
        };

        PaEvent.addHandler('onmousedown', close, triggerId, this.id);
    }
    else
    {
        alert('Element has no ID');

        return false;
    }

    return true;
};

/* *
*	Source for future PaWindow class.
*
*	@access public
*	@param boolean flag to decide whether openning more than one instance of window from the same template is allowed
*	@return mixed false if trying to open another instance but it`s not allowed, or object - window
*/
PaElement.prototype.cloneConsole = function (bSingleInstance)
{
    if (!PaValidator.isDefined(this.id))
    {
        alert('Element has no ID');

        return false;
    }

    if
    (
        !(
            PaValidator.isDefined(bSingleInstance)
            &&
            PaValidator.isDefined(this.cloned)
            &&
            bSingleInstance
            &&
            this.cloned
        )
    )
    {
        var oDate = new Date;

        var sUnique = '_' + oDate.valueOf() + '' + oDate.getMilliseconds();



        /* clone panel itself */

        var oConsole = new PaElement(this.cloneNode(true));

        var iterate = function (oNode)
        {
            if (oNode.id == arguments[1][0])
            {
                oNode.uniqueId = arguments[1][1];
            }

            if (PaValidator.isDefined(oNode.id) && oNode.id != '')
            {
                oNode.id = oNode.id + arguments[1][1];
            }
        };

        oConsole.iterateChildren(null, iterate, [this.id, sUnique]);

        PaPage.getBody().appendChild(oConsole);

        oConsole.makeDraggable();

        oConsole.addDragTrigger(this.id + '-header' + oConsole.uniqueId);
        oConsole.addCloseTrigger(this.id + '-close' + oConsole.uniqueId);

        oConsole.style.display  = 'block';

        /* clone iframe hack */

        var oIframeHack = new PaElement(document.getElementById(this.id + '-iframe').cloneNode(true));

        oIframeHack.id            = oIframeHack.id + sUnique;
        oIframeHack.style.display = 'block';

        var iframeHeight = parseInt(oIframeHack.getInlineStyle().height);
        var iframeWidth  = parseInt(oIframeHack.getInlineStyle().width);

        if (PaClient.isIe())
        {
            iframeHeight -= 2;
            iframeWidth  -= 2;
        }

        oIframeHack.style.height = iframeHeight + 'px';
        oIframeHack.style.width  = iframeWidth + 'px';
        oIframeHack.style.zIndex = 1;

        PaPage.getBody().appendChild(oIframeHack);

        oConsole.clonePrototype = this;

        this.cloned = true;

        return oConsole;
    }
    else
    {
        return false;
    }
};

/* *
*	Remove element.
*
*	@access public
*	@return boolen true returned upon success
*/
PaElement.prototype.remove = function ()
{
    if (PaValidator.isDefined(this.parentNode))
    {
        this.parentNode.removeChild(this);

        return true;
    }
    else
    {
        return false;
    }
};

/* *
*	Make element to follow mouse cursor.
*
*	@access public
*	@param boolean true will make cursor to be at the center of element
*	@return void
*/
PaElement.prototype.attachToCursor = function (bCenter)
{
    var oAttach = function (oEvent, oArgs)
    {
        PaEvent.prepare(oEvent);

        var widthCorrection = heightCorrection = 0;

        if (oArgs[0])
        {
            widthCorrection = this.getWidth() / 2;
            heightCorrection = this.getHeight() / 2;
        }

        this.style.top  = PaEvent.event.pageY + heightCorrection + 'px';
        this.style.left = PaEvent.event.pageX + widthCorrection + 'px';
    }

    PaEvent.addHandler('onmousemove', oAttach, null, bCenter);
};

/* *
*	Disable children by tag
*
*	@access public
*	@param object tags list
*	@return void
*/
PaElement.prototype.disableByTag = function (aTagsToDisable, bEnable)
{
    if (!PaValidator.isDefined(bEnable))
    {
        bEnable = true;
    }

    for (var t = 0; t < aTagsToDisable.length; t++)
    {
        var oInputs = this.getChildrenByTag(aTagsToDisable[t]);

        for (var i = 0; i < oInputs.length; i++)
        {
            oInputs[i].disabled = bEnable;
        }
    }
};

/* *
*	Enable children by tag
*
*	@access public
*	@param object tags list
*	@return void
*/
PaElement.prototype.enableByTag = function (aTagsToEnable)
{
    this.disableElementsByTag(aTagsToEnable, false);
};

PaElement.prototype.constructor = PaElement;

/*
static shit

PaNode.prototype = new PaObject;

PaElement.prototype = new PaNode;

var PaElement = new PaElement;

var PaNode = new PaNode;

var PaObject = new PaObject;*/

/* -- */

/*@*
*	PaForm
*
*	------------------
*
*	Class for operations with form elements.
*	Like all other element-targeted classes it fully inherits from PaElement.
*@*/

/* *
*	Class constructor
*
*	@return object original tree node stuffed with useful methods
*/
var PaForm = function (mElement)
{
    return PaElement.call(this, mElement);
};

PaForm.prototype = new PaElement;

/* *
*	Create class-specific element.
*
*	@return object
*/
PaForm.prototype.create = function ()
{
    return new PaForm(this.prototype.create('form'));
};

/* *
*	Walk through form elements and assemble their values in a sigle array.
*
*	TODO: add support for selects with multiple selections allowed. Check if all element types covered.
*
*	@access public
*	@return object associative array of form elements` names and values
*/
PaForm.prototype.getData = function ()
{
    var aFormData = new Array;

    var aTriggeredTags = ['INPUT', 'TEXTAREA', 'SELECT'];

    for (var f = 0; f < this.elements.length; f++)
    {
        if (PaArray.valueExists(aTriggeredTags, this.elements[f].tagName))
        {
            if (this.elements[f].name && !this.elements[f].disabled)
            {
                if (this.elements[f].tagName != 'SELECT')
                {
                    aFormData[this.elements[f].name] = this.elements[f].value;
                }
                else
                {
                    aFormData[this.elements[f].name] = this.elements[f].options[this.elements[f].selectedIndex].value;
                }
            }
        }
    }

    return aFormData;
};

/* *
*	Walk through form elements and redefine their values if names match.
*
*	TODO: add support for selects with multiple selections allowed. Check if all element types covered.
*
*	@access public
*	@param associative array
*	@return void
*/
PaForm.prototype.setData = function (aData)
{
    for (var f = 0; f < this.elements.length; f++)
    {
        if (this.elements[f].name && PaArray.valueExists(aData, this.elements[f].name))
        {
            if (this.elements[f].tagName != 'SELECT')
            {
                this.elements[f].value = aData[this.elements[f].name];
            }
            else
            {
                for (var o = 0; o < this.elements[f].options.length; o++)
                {
                    if (this.elements[f].options[o].value == aData[this.elements[f].name])
                    {
                        this.elements[f].selected = true;

                        break;
                    }
                }
            }
        }
    }
};

/* *
 * Check if all checkboxes (in the form) which names start from the prefix are checked or not.
 *
 * @param string namePrefix
 * @param boolean byId if the namePrefix is the prefix of name (false) or id (true)
 * @return bolean
 */
PaForm.prototype.areAllChecked = function (namePrefix, byId)
{
    if (typeof byId == 'undefined') byId = false;

    var attr = 'name';
    if (byId) {
        attr = 'id';
    }

    for (var i = 0; i < this.elements.length; i++)
    {
        var e = this.elements[i];

        if (e[attr] && e[attr].slice(0, namePrefix.length) == namePrefix)
        {
            if (!e.checked)
            {
                return false;
            }
        }
    }

    return true;
};

/* *
 * Check or uncheck all checkboxes in the form which have the prefix in their names.
 *
 * @param string namePrefix
 * @param boolean checkAll Check (true) or uncheck (false) all
 * @param boolean byId if the namePrefix is the prefix of name (false) or id (true)
 * @return bolean
 */
PaForm.prototype.checkAllCheckboxes = function (namePrefix, checkAll, byId)
{
    if (typeof byId == 'undefined') byId = false;

    if (!PaValidator.isDefined(checkAll))
    {
        checkAll = true;
    }

    var attr = 'name';
    if (byId) {
        attr = 'id';
    }

    for (var i = 0; i < this.elements.length; i++)
    {
        var e = this.elements[i];

        if (e.type == 'checkbox' && e[attr] && e[attr].slice(0, namePrefix.length) == namePrefix)
        {
            e.checked = checkAll;
        }
    }
};

/* *
 * Reverse state of all checkboxes in the form which have the prefix in their names.
 *
 * @param string namePrefix
 * @param boolean byId if the namePrefix is the prefix of name (false) or id (true)
 * @return bolean
 */
PaForm.prototype.reverseAllCheckboxes = function (namePrefix, byId)
{
    if (typeof byId == 'undefined') byId = false;

    var attr = 'name';
    if (byId) {
        attr = 'id';
    }

    for (var i = 0; i < this.elements.length; i++)
    {
        var e = this.elements[i];

        if (e[attr] && e[attr].slice(0, namePrefix.length) == namePrefix)
        {
            e.checked = !e.checked;
        }
    }
};

/* *
 * Count the number of checked checkboxes (in the form) which names start from the prefix.
 *
 * @param object form
 * @param string namePrefix
 * @param boolean byId if the namePrefix is the prefix of name (false) or id (true)
 * @return bolean
 */
PaForm.prototype.numberOfChecked = function (namePrefix, byId)
{
    if (typeof byId == 'undefined') byId = false;

    var attr = 'name';
    if (byId) {
        attr = 'id';
    }

    var c = 0;

    for (var i = 0; i < this.elements.length; i++)
    {
        var e = this.elements[i];

        if (e[attr] && e[attr].slice(0, namePrefix.length) == namePrefix)
        {
            if (e.checked)
            {
                c++;
            }
        }
    }

    return c;
};

/* *
 * Prepears the form's checkboxes so that when all checked then "check\uncheck all" checkbox is checked too and when
 * all area unchecked then the checkbox is unchecked too.
 *
 * @param string namePrefix the prefix that is the same for all checkboxes that should be processed
 * @param string checkAllId id of the "check\uncheck all" checkbox
 * @param boolean byId if the namePrefix is the prefix of name (false) or id (true)
 */
PaForm.prototype.prepareForCheckAll = function (namePrefix, checkAllId, byId)
{
    if (typeof byId == 'undefined') byId = false;

    var attr = 'name';
    if (byId) {
        attr = 'id';
    }

    var self = this;

    for (var i = 0; i < this.elements.length; i++)
    {
        var e = this.elements[i];

        if (e[attr] && e[attr].slice(0, namePrefix.length) == namePrefix)
        {
            PaForm.prototype.assignOnClickForPrepareForCheckAll(e, checkAllId, namePrefix, byId, self);
        }
    }
};

PaForm.prototype.assignOnClickForPrepareForCheckAll = function (e, checkAllId, namePrefix, byId, self)
{
    var prevOnClick = e.onclick;
    if (typeof prevOnClick == "string") {
        var strOnClick = prevOnClick;
        prevOnClick = function () {eval(strOnClick);};
    }
    else if (typeof prevOnClick == "undefined" || prevOnClick == null) {
        prevOnClick = function () {};
    }
    e.onclick = function(e){
        if(typeof e == 'undefined') e=null;
            prevOnClick.call(this, e);
        document.getElementById(checkAllId).checked = self.areAllChecked(namePrefix, byId);
    }
};

PaForm.prototype.getCheckboxes = function ()
{
    var aCheckboxesRaw = this.getChildrenByTag('input');

    var oFunction = function (oNode)
    {
        return oNode.type == 'checkbox'
    }

    return PaNodeList.filter(aCheckboxesRaw, oFunction);
};

PaForm.prototype.disableElements = function (aElementsID, enabled){
    for(var i=0; i<aElementsID.length; i++){
        var obj = document.getElementById(aElementsID[i]);
        if (!obj) continue;
        if( !enabled ){
            obj.disabled = true;
        }else{
            obj.disabled = false;
        }
    }
};

/*
 * Not ready
 */
PaForm.prototype.addValidator = function (sElementName, mValidator, sErrorsPanelId, sErrorMsg)
{
    if (!PaValidator.isDefined(this._validation))
    {
        this._validation = {'positives' : 0, 'negatives' : 0, 'data' : new Array};
    }

    PaEvent.addHandler('onsubmit', function ()
        {
            if ($(sErrorsPanelId) && PaValidator.isDefined(this.elements[sElementName]))
            {
                var oErrors = new PaElement('error');

                if (PaValidator.isString(mValidator))
                {
                    switch (mValidator)
                    {
                        case 'int' :
                        case 'float' :
                        case 'email' :
                        {
                            if (!PaValidator['is' + PaString.uppercaseFirst(mValidator)](this.elements[sElementName].value))
                            {
                                oErrors.innerHTML += (oErrors.innerHTML + sErrorMsg + '<br />');

                                oErrors.show();

                                this._validation.negatives++;

                                return false;
                            }

                            break;
                        }
                    }
                }
                else if (PaValidator.isFunction(mValidator))
                {
                    if (!mValidator(this.elements[sElementName].value))
                    {
                        oErrors.innerHTML += (oErrors.innerHTML + sErrorMsg + '<br />');

                        oErrors.show();

                        this._validation.negatives++;

                        return false;
                    }
                }
            }

            this._validation.positives++;

            return true;
        }, this);

    this._validation.data.push({'name' : sElementName, 'validator' : mValidator, 'output' : sErrorsPanelId, 'message' : sErrorMsg});

    return this._validation.data.length - 1;
};

PaForm.prototype.constructor = PaForm;

/* -- */

/*@*
*	PaClient
*
*	------------------
*
*	Set of static methods to get browser and browser dependant info (and something else).
*@*/

var PaClient = new Object;

if (!window.navigator)
{
    window.navigator = new Object;
}

/* *
*	Check if browser is Opera.
*
*	@access public
*	@static
*	@return boolean
*/
PaClient.isOpera = function ()
{
    return window.opera ? true : false;
};

/* *
*	Check if browser is Internet Explorer or it`s clone.
*
*	@access public
*	@static
*	@return boolean
*/
PaClient.isIe = function ()
{
    return document.all && !PaClient.isOpera();
};

/* *
*	Check if browser is Mozilla or it`s clone.
*
*   SHOULD IT BE REPLACED WITH A CHECK FOR navigator.product = 'Gecko'???
*   AND RENAMED???
*
*	@access public
*	@static
*	@return boolean
*/
PaClient.isMozilla = function ()
{
    return window.sidebar ? true : false;
};

/* *
*	Check if browser is Safari.
*
*	@access public
*	@static
*	@return boolean
*/
PaClient.isSafari = function ()
{
    return PaClient.getName().indexOf('Safari') != -1 ? true : false;
};

/* *
*	Check if browser is Macintosh-based.
*
*	@access public
*	@static
*	@return boolean
*/
PaClient.isMac = function ()
{
    return PaClient.getName().indexOf('Mac') != -1 ? true : false;
};

/* *
*	Check if browser is Windows-based.
*
*	@access public
*	@static
*	@return boolean
*/
PaClient.isWin = function ()
{
    return PaClient.getPlatform().indexOf('Win') != -1 ? true : false;
};

/* *
*	Get user agent full name.
*
*	@access public
*	@static
*	@return string
*/
PaClient.getName = function ()
{
    if (PaValidator.isDefined(window.navigator.userAgent))
    {
        return window.navigator.userAgent;
    }

    return '';
};

/* *
*	Check if cookie enabled.
*
*	@access public
*	@static
*	@return boolean
*/
PaClient.isCookieEnabled = function ()
{
    return PaValidator.isDefined(window.navigator.cookieEnabled) && window.navigator.cookieEnabled === true;
};

/* *
*	Check if flash enabled.
*
*	@access public
*	@static
*	@return boolean
*/
PaClient.isFlashEnabled = function ()
{
    return !PaValidator.isEmpty(PaFlash.getObject());
};

/* *
*	Check if Java is enabled (not that JVM installed).
*
*	@access public
*	@static
*	@return boolean
*/
PaClient.isJavaEnabled = function ()
{
    return PaValidator.isDefined(window.navigator.javaEnabled) && window.navigator.javaEnabled();
};

/* *
*	Get user platform.
*
*   IS IT NEEDED???
*
*	@access public
*	@static
*	@return string
*/
PaClient.getPlatform = function ()
{
    if (PaValidator.isDefined(window.navigator.platform))
    {
        return window.navigator.platform;
    }

    return '';
};

/* *
*	Get user language.
*
*	@access public
*	@static
*	@return string
*/
PaClient.getLanguage = function ()
{
    if (PaValidator.isDefined(window.navigator.userLanguage))
    {
        return window.navigator.userLanguage;
    }

    return '';
};

/* *
*	Redirect user to URL provided.
*
*	@access public
*	@static
*	@param string containing URL
*	@return void
*/
PaClient.redirect = function (sUrl)
{
    document.location.href = sUrl;
};

/* *
*	Get URL of current session.
*
*	@access public
*	@return string
*/
PaClient.getUrl = function ()
{
    return document.URL;
};

/* *
*	Get browser`s Browse button size.
*
*	@access public
*	@static
*	@return object with width and height properties
*/
PaClient.getBrowseButtonSize = function ()
{
    /* create input text element */
    var oTempSampleTextInput                    = document.createElement('input');
    oTempSampleTextInput.type                   = 'text';
    oTempSampleTextInput.style.visibility       = 'hidden';

    /* create input file element */
    var oTempSampleFileInput                    = document.createElement('input');
    oTempSampleFileInput.type                   = 'file';

    /* create absolutely positioned div element to be parent for input file element */
    var oTempParentSampleFileInput              = document.createElement('div');
    oTempParentSampleFileInput.style.visibility = 'hidden';
    oTempParentSampleFileInput.style.position   = 'absolute';

    /* get body element */
    var oBody                                   = PaPage.getBody();

    /* add all element to document tree */
    oTempParentSampleFileInput.appendChild(oTempSampleFileInput);
    oBody.appendChild(oTempSampleTextInput);
    oBody.appendChild(oTempParentSampleFileInput);

    /* calculate size */
    var buttonWidth  = oTempParentSampleFileInput.clientWidth - oTempSampleTextInput.clientWidth;
    var buttonHeight = oTempSampleTextInput.clientHeight;

    return {'width': buttonWidth, 'height': buttonHeight};
};

/* -- */

/*@*
*	PaFlash
*
*	------------------
*
*	Set of static methods to handle adobe flash.
*@*/

var PaFlash = new Object;

/* *
*	Get flash object to retrieve info from.
*
*	@access private
*	@static
*	@return object activex, plugin or null
*/
PaFlash.getObject = function ()
{
    var oFlash = null;

    if (PaClient.isIe())
    {
        try
        {
            oFlash = new ActiveXObject('ShockwaveFlash.ShockwaveFlash');
        }
        catch (oError)
        {
            try
            {
                oFlash = new ActiveXObject('ShockwaveFlash.ShockwaveFlash.7');
            }
            catch (oError)
            {
                try
                {
                    oFlash = new ActiveXObject('ShockwaveFlash.ShockwaveFlash.6');
                }
                catch (oError)
                {

                }
            }
        }
    }
    else
    {
        if (PaValidator.isDefined(navigator.plugins['Shockwave Flash 2.0']))
        {
            oFlash = navigator.plugins['Shockwave Flash 2.0'];
        }

        if (PaValidator.isDefined(navigator.plugins['Shockwave Flash']))
        {
            oFlash = navigator.plugins['Shockwave Flash'];
        }
    }

    return oFlash;
};

/* *
*	Get flash version.
*
*	@access public
*	@static
*	@return object array [major version, minor, fix] or empty
*/
PaFlash.getVersion = function ()
{
    var oFlash         = PaFlash.getObject();
    var oVersions_ARRAY = new Array;

    if (PaClient.isIe())
    {
        try
        {
            oVersions_ARRAY = oFlash.GetVariable('$version').split(' ')[1].split(',');
        }
        catch (oError)
        {
            return false;
        }
    }
    else
    {
        if (oFlash.description)
        {
            oVersions_ARRAY = oFlash.description.replace(/([a-zA-Z]|\s)+/, '').replace(/(\s+r|\s+b[0-9]+)/, '.').split('.');
        }
        else
        {
            return false;
        }
    }

    if (!PaValidator.isEmpty(oVersions_ARRAY))
    {
        for (var e = 0; e < oVersions_ARRAY.length; e++)
        {
            if (!/^[0-9]+$/.test(oVersions_ARRAY[e]))
            {
                return false;
            }
        }

        return oVersions_ARRAY;
    }
    else
    {
        return false;
    }
};

/* *
*	Fix all flash objects on the page so that user should`t activate them by clicking.
*	Better to hang it on page onload.
*
*
*	@access public
*	@static
*	@return void
*/
PaFlash.fix = function ()
{
    var oObjects = document.getElementsByTagName('object');

    for (var e = 0; e < objects.length; e++)
    {
        oObjects[e].outerHTML = oObjects[e].outerHTML;
    }
};

/* -- */

/*@*
*	PaEvent
*
*	------------------
*
*	Set of weird static methods to do something with events.
*@*/

var PaEvent = new Object;

var __WINDOW__ = window;
var __BODY__   = document.getElementsByTagName('body')[0];

PaEvent.menuDisabled = false;

/* *
*	Prepare basic objects and properties for quick access, uniform names.
*
*	Everything prepared will be stored in PaEvent object
*
*	<code>
*		var oHandler = function (aParams)
*       {
*           PaEvent.prepare(aParams[0]);
*       };
*	</code>
*
*   In the above code PaEvent object will have such properties and sub-properties:
*       + target - object which refers to element which fired event (don`t mix it with this);
*       + event - mutated parameter passed to the method (aParams[0]);
*           - correspondentTarget - ex. element from which you moved mouse out to be on target;
*           - buttonCode - key code (if some key was pressed along with mouse action);
*           - elementX - mouse x coordinate inside of target;
*           - elementY - mouse y coordinate inside of target;
*           - mouseButtonLeft - is true if left button was pressed;
*           - mouseButtonMiddle - is true if middle button was pressed;
*           - mouseButtonRight - is true if right button was pressed;
*           - pageX - mouse x absolute position in viewport scope;
*           - pageY - mouse y absolute position in viewport scope;
*
*	@access public
*	@static
*	@param object Native event
*	@return void
*/
PaEvent.prepare = function (oEvent)
{
    var oEventTarget     = PaClient.isIe() ? oEvent.srcElement : oEvent.target;

    oEvent.correspondentTarget = PaClient.isIe() ? oEvent.fromElement : oEvent.relatedTarget;
    oEvent.buttonCode          = PaClient.isIe() ? oEvent.keyCode : oEvent.which;
    oEvent.elementX            = !PaClient.isMozilla() ? oEvent.offsetX : oEvent.layerX;
    oEvent.elementY            = !PaClient.isMozilla() ? oEvent.offsetY : oEvent.layerY;

    oEvent.mouseButtonLeft   = false;
    oEvent.mouseButtonMiddle = false;
    oEvent.mouseButtonRight  = false;

    if (PaClient.isIe())
    {
        switch (oEvent.button)
        {
            case 1 :
            {
                oEvent.mouseButtonLeft = true;
                break;
            }
            case 4 :
            {
                oEvent.mouseButtonMiddle = true;
                break;
            }
            case 2 :
            {
                oEvent.mouseButtonRight = true;
                break;
            }
        }
    }
    else
    {
        switch (oEvent.which)
        {
            case 1 :
            {
                oEvent.mouseButtonLeft = true;
                break;
            }
            case 2 :
            {
                oEvent.mouseButtonMiddle = true;
                break;
            }
            case 3 :
            {
                oEvent.mouseButtonRight = true;
                break;
            }
        }
    }

    /*oEvent.pageX - oTarget.getCoords().x*/

    if (PaClient.isIe())
    {
        oEvent.pageX = oEvent.clientX + document.body.scrollLeft;
        oEvent.pageY = oEvent.clientY + document.body.scrollTop;
    }

    this.event  = oEvent;
    this.target = oEventTarget;
};

/* *
*	Stop bubbling of supplied event.
*
*	@access public
*	@static
*	@param object Native event
*	@return void
*/
PaEvent.stop = function (oEvent)
{
    if (PaClient.isIe())
    {
        this.event.cancelBubble = true;
    }
    else
    {
        this.event.stopPropagation();
    }
};

/* *
*	Disable default native event behaviours.
*
*	@access public
*	@static
*	@param object Native event
*	@return void
*/
PaEvent.prevent = function (oEvent)
{
    if (PaClient.isIe())
    {
        this.event.returnValue  = false;
    }
    else
    {
        this.event.preventDefault();
    }
};

/* *
*	Register custom event handlers.
*   Multiple handlers may be registered for a single event and/or element.
*
*	@access public
*	@param string Event full name (with 'on')
*	@param object Custom function
*	@param mixed Element, element`s id string or null
*	@param mixed Additional argument(s) to pass to custom function upon event
*
*	Custom event handler (except for body onload handlers) will always receive the single array parameter.
*   It`s first parameter will be Event object.
*	Which can be enriched with different usefull properties by PaEvent.prepare.
*
*	Warning: For IE it won`t be original Event object but simple object with all enumerable properties copied from the original one.
*
*	Usage:
*
*	1) tracking mouse events document-wide
*
*	<code>
*		PaEvent.addHandler('onclick', trackOnclick_1);
*		PaEvent.addHandler('onclick', trackOnclick_2);
*	</code>
*
*	This way trackOnclick will fire each time user clicks on a page. And it`s up to developer to recognize intended target element by id, className etc.
*	Usefull for tracking a group of elements.
*
*	Warning: Be carefull with 'onmousemove'.
*
*	2) Bypass setting element ID but still be able to track mouse events document-wide with additional parameters
*
*	<code>
*		PaEvent.addHandler('onclick', trackOnclick, null, arg1 [, arg2 ...]);
*	</code>
*
*	3) tracking events for particular element when it`s already added into DOM
*
*	<code>
*		PaEvent.addHandler('onclick', trackOnclick_1, 'elementId');
*		PaEvent.addHandler('onclick', trackOnclick_2, 'elementId');
*	</code>
*
*   Pseudo-variable "this" inside of the handler will refer to element itself.
*
*	4) tracking events for particular element when element itself doesn`t currently exist
*
*	<code>
*		PaEvent.addHandler('onclick', trackOnclick_1, 'elementId');
*		PaEvent.addHandler('onclick', trackOnclick_2, 'elementId');
*	</code>
*
*	<code>
*		<body onload="PaEvent.init()" ... >
*	</code>
*
*	5) registering a handler for window or body
*
*	<code>
*		PaEvent.addHandler('onresize', trackOnResize_1, '__WINDOW__');
*		PaEvent.addHandler('onresize', trackOnResize_2, '__WINDOW__');
*	</code>
*
*	Only __WINDOW__ and __BODY__ constants are available for now.
*
*/
PaEvent.addHandler = function (sEventName, oFunction, mObject)
{
    /*get arguments passed to handler function*/
    arguments.slice = Array.prototype.slice;
    var oArguments  = arguments.slice(3, arguments.length);

    /*register handlers in old way because Document doesn`t support addEventListener.
    TODO: try to inherit it.*/
    if (!PaValidator.isDefined(mObject) || PaValidator.isNull(mObject))
    {
        /*redefine build-in event handler*/
        if
        (
            !PaValidator.isDefined(document._PaMouseHandlers)
            ||
            (
                PaValidator.isDefined(document._PaMouseHandlers)
                &&
                !PaValidator.isDefined(document._PaMouseHandlers[sEventName])
            )
        )
        {
            document[sEventName] = function (oEvent)
            {
                for (var i = 0; i < document._PaMouseHandlers[sEventName].length; i++)
                {
                    (function (oEvent) {return document._PaMouseHandlers[sEventName][i]['_func'].call(this, [oEvent || window.event].concat(document._PaMouseHandlers[sEventName][i]['_args']))})(oEvent);
                }

                return true;
            };
        }
        /*instantiate event handler storage*/
        if (typeof document._PaMouseHandlers == 'undefined')
        {
            document._PaMouseHandlers = new Array;
        }

        if (typeof document._PaMouseHandlers[sEventName] == 'undefined')
        {
            document._PaMouseHandlers[sEventName] = new Array;
        }
        /*store event handler*/
        document._PaMouseHandlers[sEventName][document._PaMouseHandlers[sEventName].length] = {'_func' : oFunction, '_args' : oArguments};
    }
    /*register named handlers*/
    else if
    (
        PaValidator.isObject(mObject)
        &&
        (
            !PaValidator.isElement(mObject)
            ||
            (
                PaValidator.isElement(mObject)
                &&
                PaValidator.isTag(mObject, 'body')
            )
        )
    )
    {
        if (mObject == __WINDOW__)
        {
            if (PaClient.isIe())
            {
                window.attachEvent(sEventName, function () {return oFunction.call(this, [window.event].concat(oArguments));});
            }
            else
            {
                window.addEventListener(sEventName.substr(0, 2) == 'on' ? sEventName = sEventName.substr(2) : sEventName, function (oEvent) {return oFunction.call(this, [oEvent].concat(oArguments));}, false);
            }

        }
        /*register onload so it can be called by init*/
        else if (mObject == __BODY__ && sEventName == 'onload')
        {
            /*instantiate event handler storage*/
            if (typeof document._PaDataHandlers == 'undefined')
            {
                document._PaDataHandlers = new Array;
            }
            if (typeof document._PaDataHandlers[mObject] == 'undefined')
            {
                document._PaDataHandlers[mObject] = new Array;
            }
            if (typeof document._PaDataHandlers[mObject][sEventName] == 'undefined')
            {
                document._PaDataHandlers[mObject][sEventName] = new Array;
            }

            document._PaDataHandlers[mObject][sEventName][document._PaDataHandlers[mObject][sEventName].length] = {'_func' : oFunction, '_args' : oArguments};
        }
    }
    else if (PaValidator.isString(mObject) || PaValidator.isElement(mObject))
    {
        /*try to find element in tree if already loaded or store for futher call by init*/
        try
        {
            if (PaValidator.isString(mObject))
            {
                var oElement = document.getElementById(mObject);
            }
            else
            {
                var oElement = mObject;
            }

            if (PaClient.isIe())
            {
                oElement.attachEvent(sEventName, function () {var _oCall = function (oElement, oFunction) {var aParams = [PaObject.copy(window.event)].concat(arguments[2]);return function() {return oFunction.call(oElement, aParams);};}; window.setTimeout(_oCall(oElement, oFunction, oArguments), 0);});
            }
            else
            {
                oElement.addEventListener(sEventName.substr(0, 2) == 'on' ? sEventName = sEventName.substr(2) : sEventName, function (oEvent) {return oFunction.call(this, [oEvent].concat(oArguments));}, false);
            }
        }
        catch (oError)
        {
            if (PaValidator.isString(mObject))
            {
                /*instantiate event handler storage*/
                if (typeof document._PaDataHandlers == 'undefined')
                {
                    document._PaDataHandlers = new Array;
                }
                if (typeof document._PaDataHandlers[mObject] == 'undefined')
                {
                    document._PaDataHandlers[mObject] = new Array;
                }
                if (typeof document._PaDataHandlers[mObject][sEventName] == 'undefined')
                {
                    document._PaDataHandlers[mObject][sEventName] = new Array;
                }

                document._PaDataHandlers[mObject][sEventName][document._PaDataHandlers[mObject][sEventName].length] = {'_func' : oFunction, '_args' : oArguments};
            }
        }
    }
    else
    {
        return false;
    }

    return true;
};

/* *
*	Use it in HTML to call body onload and other pending handlers.
*	It`s not needed if event handlers were registered inline after page loading.
*
*	TODO: Get rid of this method and make it possible to define handlers wherever developer wants
*
*	<code>
*		<body onload="PaEvent.init()" ... >
*	</code>
*
*	@access public
*	@static
*	@return void
*/
PaEvent.init = function ()
{
    for (var objectCounter in document._PaDataHandlers)
    {
        for (var eventCounter in document._PaDataHandlers[objectCounter])
        {
            /* run immediately */
            if (objectCounter == __BODY__ && eventCounter == 'onload')
            {
                for (var h in document._PaDataHandlers[objectCounter][eventCounter])
                {
                    document._PaDataHandlers[objectCounter][eventCounter][h]['_func'](null, null, document._PaDataHandlers[objectCounter][eventCounter][h]['_args']);
                }
            }
            /* register handlers for elements added to DOM later than script element */
            else
            {
                try
                {
                    if (objectCounter == __BODY__)
                    {
                        oElement = PaPage.getBody();
                    }
                    else
                    {
                        oElement = document.getElementById(objectCounter);
                    }

                    for (var handlerCounter in document._PaDataHandlers[objectCounter][eventCounter])
                    {
                        PaEvent._init(objectCounter, eventCounter, handlerCounter);
                    }
                }
                catch(oError)
                {

                }


            }
        }
    }
};

/* *
*	Creating artificial activation object for Mozilla.
*
*	@access private
*	@static
*	@param
*	@param
*	@param
*	@return void
*/
PaEvent._init = function (objectCounter, eventCounter, handlerCounter)
{
    if (PaClient.isIe())
    {
        oElement.attachEvent(eventCounter, function () {var _oCall = function (oElement, oFunction) {var aParams = [PaObject.copy(window.event)].concat(arguments[2]);return function() {return oFunction.apply(oElement, aParams);}}; window.setTimeout(_oCall(oElement, document._PaDataHandlers[objectCounter][eventCounter][handlerCounter]['_func'], document._PaDataHandlers[objectCounter][eventCounter][handlerCounter]['_args']), 0);});
    }
    else
    {
        var sMozEvent = eventCounter.substr(0, 2) == 'on' ? eventCounter.substr(2) : eventCounter;

        oElement.addEventListener(sMozEvent, function (oEvent) {return document._PaDataHandlers[objectCounter][eventCounter][handlerCounter]['_func'].call(this, [oEvent].concat(document._PaDataHandlers[objectCounter][eventCounter][handlerCounter]['_args']));}, false);
    }
};

/* *
*	Prevent site content manual copying and right-clicking.
*	Right-clicking in inputs will be still enabled.
*
*	TODO: renew Opera support
*
*	@access public
*	@static
*	@return void
*/
PaEvent.disableAll = function ()
{
    var handler = function (oEvent)
    {
        PaEvent.prepare(oEvent);

        var sTagName = PaEvent.target.tagName;

        if
        (
            PaEvent.event.mouseButtonRight
        )
        {
            if
            (
                ((PaClient.isMozilla() && PaEvent.event.type == 'contextmenu') || !PaClient.isMozilla())
            )
            {
                if
                (
                    sTagName != 'INPUT'
                    &&
                    sTagName != 'SELECT'
                    &&
                    sTagName != 'TEXTAREA'
                )
                {
                    alert(sCopyrightMessage);

                    PaEvent.stop();
                    PaEvent.prevent();

                    return false;
                }
                else
                {
                    return true;
                }
            }
        }
        else
        {
            if
            (
                (
                    PaValidator.isDefined(PaEvent.target.id)
                    &&
                    PaEvent.target.id == 'report_messages'
                )
                ||
                sTagName == 'INPUT'
                ||
                sTagName == 'SELECT'
                ||
                sTagName == 'TEXTAREA'
            )
            {
                return true;
            }
        }

        PaEvent.stop();
        PaEvent.prevent();

        return false;
    };


    if (PaClient.isIe())
    {

        PaEvent.addHandler('onmousedown', handler);

        PaEvent.addHandler('onselectstart', handler);
    }
    else
    {

        PaEvent.addHandler('oncontextmenu', handler);

        PaEvent.addHandler('onmousedown', handler);
    }
};

PaEvent.disableMenu = function (sMessage)
{
    if (!this.menuDisabled)
    {

        if (!PaClient.isMozilla())
        {
            document._PaTempOnMouseDown = document.onmousedown;
            PaEvent.addHandler('onmousedown', PaEvent._disableMenu, null, sMessage);

        }

        else
        {
            document._PaTempOnContextMenu = document.oncontextmenu;
            PaEvent.addHandler('oncontextmenu', PaEvent._disableMenu, null, sMessage);
        }

        this.menuDisabled = true;

        return true;
    }

    return false;
};

PaEvent.enableMenu = function ()
{
    if (this.menuDisabled)
    {

        if (!PaClient.isMozilla())
        {
            document.onmousedown = document._PaTempOnMouseDown;
        }

        else
        {
            document.oncontextmenu = document._PaTempOnContextMenu;
        }

        this.menuDisabled = false;

        return true;
    }

    return false;
};

PaEvent._disableMenu = function (oMyEvent, oMyTarget, oArguments)
{
    var sTagName = oMyTarget.tagName;

    if (oMyEvent.button == 2 || oMyEvent.button == 3)
    {
        if (sTagName != 'INPUT')
        {
            if (oArguments['0'])
            {
                alert(oArguments['0']);
            }
        }
        else if (sTagName == 'INPUT')
        {
            return true;
        }
    }

    return false;
};

/* -- */

/*@*
*	PaMath
*
*	------------------
*
*	Class for math and relative operations.
*@*/
var PaMath = new Object;

/* *
*	Generate random number from the specified range.
*
*	@access public
*	@static
*	@param int
*	@param int
*	@param boolean if generated number can be float
*	@return mixed
*/
PaMath.random = function (minimum, maximum, bFloatResult)
{
    if (PaValidator.isBool(bFloatResult) && bFloatResult === true)
    {
        return Math.random() * (maximum - minimum) + minimum;
    }
    else
    {
        return Math.floor(Math.random() * (maximum - minimum + 1)) + minimum;
    }
};

/* -- */

/*@*
*	PaRequest
*
*	------------------
*
*	Class for data transition.
*@*/
var PaRequest = function (sUrl, sMethod, bCaching, oData)
{
    if (sMethod != 'get' && sMethod != 'post')
    {
        sMethod = '';
    }

    this.url              = sUrl || PaClient.getUrl();
    this.data             = oData || null;
    this.caching          = bCaching || false;
    this.acceptedStatuses = [200];
    this.transport        = 'ajax';
    this.ajax             = null;
    this.method           = sMethod || 'get';
    this.errors           = document.createElement('errors');
    this.headers          = new Array;
    this.onstart          = null;
    this.onfinish         = null;
    this.onwrongstatus    = null;

    if (this.transport == 'ajax')
    {
        if (!this.getAjax())
        {
            return null;
        }
    }

    return this;
};

PaRequest.prototype.setUrl = function (sUrl)
{
    this.url = sUrl;
};

PaRequest.prototype.setMethod = function (sMethod)
{
    this.method = sMethod;
};

PaRequest.prototype.setTransport = function (sTransport)
{
    this.transport = sTransport;
};

PaRequest.prototype.setResponseType = function (sResponseType)
{
    this.responseType = sResponseType;
};

PaRequest.prototype.setData = function (aData)
{
    var data = PaObject.buildUrl(aData);

    if (data)
    {
        this.data = '&' + data;
    }
};

PaRequest.prototype.setDataList = function (aData, sVarName)
{
    var data = PaArray.buildUrl(aData, sVarName);

    if (data)
    {
        this.data = '&' + data;
    }
};

PaRequest.prototype.addHeader = function (sHeaderName, sHeaderContent)
{
    this.headers[sHeaderName] = sHeaderContent;
};

PaRequest.prototype.getAjax = function ()
{
    /* check for native XMLHttpRequest object */
    if (window.XMLHttpRequest)
    {
        this.ajax = new XMLHttpRequest();
    }
    /* check for ActiveX object */
    else if (window.ActiveXObject)
    {
        try
        {
            this.ajax = new ActiveXObject('Msxml2.XMLHTTP');
        }
        catch (eError)
        {
            try
            {
                this.ajax = new ActiveXObject('Microsoft.XMLHTTP');
            }
            catch (eError)
            {
                /*throw away your browser*/
                return false;
            }
        }
    }
};

PaRequest.prototype.getText = function ()
{
    return this.ajax.responseText;
};

PaRequest.prototype.getXml = function ()
{
    return this.ajax.responseXML;
};

PaRequest.prototype.hasXml = function ()
{
    return this.ajax.responseXML.firstChild !== null;
};

PaRequest.prototype.getState = function ()
{
    return this.ajax.readyState;
};

PaRequest.prototype.getStatus = function ()
{
    return this.ajax.status;
};

PaRequest.prototype.getErrors = function ()
{
    return this.errors.getElementsByTagName('error');
};

PaRequest.prototype.hasErrors = function ()
{
    return this.getErrors().length !== 0;
};

PaRequest.prototype.enableCaching = function (bStatus)
{
    this.caching = true;
};

PaRequest.prototype.exec = function ()
{
    if (this.transport == 'ajax')
    {
        if (!this.ajax)
        {
            if (!this.getAjax())
            {
                return false;
            }
        }

        this.ajax.overrideMimeType('text/xml');

        if (!this.caching)
        {
            this.addHeader('Cache-Control', 'no-cache');
        }

        this.ajax.onreadystatechange = function (_this)
        {
            return function ()
            {
                switch (_this.getState())
                {
                    case 1 :
                    {
                        if (_this.onstart)
                        {
                            _this.onstart();
                        }

                        break;
                    }
                    case 4 :
                    {
                        if (PaArray.valueExists(_this.acceptedStatuses, _this.getStatus()))
                        {
                            if (_this.hasXml() && _this.getXml().getElementsByTagName('errors').length)
                            {
                                _this.errors = _this.getXml().getElementsByTagName('errors')[0].cloneNode(true);
                            }

                            if (_this.onfinish)
                            {
                                _this.onfinish();
                            }
                        }
                        else
                        {
                            if (_this.onwrongstatus)
                            {
                                _this.onwrongstatus();
                            }
                        }

                        break;
                    }
                }
            }
        }(this);

        if (this.data)
        {
            this.addHeader('Content-Type', 'application/x-www-form-urlencoded');
        }

        try
        {
            this.ajax.open(!this.data ? this.method : 'POST', this.url, true);
        }
        catch (oError)
        {
            alert('Maybe cross-domain scripting?!');

            return false;
        }

        for (var sHeaderName in this.headers)
        {
            this.ajax.setRequestHeader(sHeaderName, this.headers[sHeaderName]);
        }

        this.ajax.send(!this.data ? null : encodeURI(this.data));
    }
    else if (this.transport == 'iframe')
    {

    }

    return true;
};

/* -- */

/*@*
*	PaArray
*
*	------------------
*
*	Set of static methods for manipulations with arrays.
*@*/

var PaArray = new Object;

/* *
*	Find out if element with value provide exists in array.
*
*	<code>
*		document.write(PaArray.getKeyByValue([1,2], 2));
*	</code>
*
*	<result>
*		true
*	</result>
*
*	@access public
*	@static
*	@param array
*	@param mixed
*	@return boolean
*/
PaArray.valueExists = function (aArray, sValue)
{
    for (var c in aArray)
    {
        if (aArray[c] == sValue)
        {
            return true;
        }
    }

    return false;
};

/* *
*	Get array key by value. Returns false if value is not found.
*
*	<code>
*		document.write(PaArray.getKeyByValue([1,2], 2));
*	</code>
*
*	<result>
*		1
*	</result>
*
*	@access public
*	@static
*	@param array
*	@param mixed
*	@return mixed
*/
PaArray.getKeyByValue = function (aArray, sValue)
{
    for (var c in aArray)
    {
        if (aArray[c] == sValue)
        {
            return c;
        }
    }

    return false;
};

/* *
*	Walk through first level elements and apply custom function to each of them. Original array affected.
*
*	<code>
*		PaArray.walk([' test1', '   test2 '], PaString.trim);
*	</code>
*
*	<result>
*		blank spaces stripped through out array
*	</result>
*
*	@access public
*	@static
*	@param array
*	@param object custom function to apply
*	@return void
*/
PaArray.walk = function (aArray, oFunction)
{
    for (var counter = 0; counter < aArray.length; counter++)
    {
        aArray[counter] = oFunction(aArray[counter]);
    }

    return aArray;
};

/* *
*	Walk through elemnts, get their values, encode and assemble URL.
*
*	<code>
*		document.write(PaArray.buildUrl([1, 2, true], 'test'));
*	</code>
*
*	<result>
*		test[0]=1&test[1]=2&test[2]=true
*	</result>
*
*	@access public
*	@static
*	@param object
*	@return string
*/
PaArray.buildUrl = function (oObject, sVarName, bSkipUndefined)
{
    if (!PaValidator.isDefined(bSkipUndefined))
    {
        bSkipUndefined = true;
    }

    var aTemp = new Array;

    for (var e = 0; e < oObject.length; e++)
    {
        if (bSkipUndefined && !PaValidator.isDefined(oObject[e]))
        {
            continue;
        }

        aTemp[aTemp.length] = encodeURIComponent(sVarName) + '[' + e + ']=' + encodeURIComponent(oObject[e]);
    }

    return aTemp.join('&');
};

/* *
*	Give away undefined elements.
*
*	@access public
*	@static
*	@param object
*	@return object new array
*/
PaArray.rebuild = function (oObject)
{
    if (!PaValidator.isArray(oObject))
    {
        return oObject;
    }

    var aTemp = new Array;

    for (var e = 0; e < oObject.length; e++)
    {
        if (PaValidator.isDefined(oObject[e]))
        {
            aTemp.push(oObject[e]);
        }
    }

    return aTemp;
};

/* -- */

/*@*
*	PaPage
*
*	------------------
*
*	Set of static page-related informational methods.
*@*/

var PaPage = new Object;

/* *
*	Get body element.
*
*	@access public
*	@return object
*/
PaPage.getBody = function ()
{
    return document.getElementsByTagName('body')[0];
};

PaPage.getSize = function ()
{
    return {
                'x': this.getBody().scrollWidth,
                'y': this.getBody().scrollHeight
            };
};

/* *

*/
PaPage.getViewSize = function ()
{
    return {
                'x': this.getBody().clientWidth,
                'y': this.getBody().clientHeight
            };
};

/* *

*/
PaPage.getScroll = function ()
{
    return {
                'x': PaPage.getBody().scrollLeft,
                'y': PaPage.getBody().scrollTop
            };
};

/* *
*	Get coordinates for a center of visible page area.
*
*	BUGGY: fails on intermediate scroll positions
*
*	@access public
*	@return object With x and y properties
*/
PaPage.getViewCenter = function ()
{
    return {
                'x': PaPage.getBody().clientWidth / 2 + PaPage.getBody().scrollLeft,
                'y': PaPage.getBody().clientHeight / 2 + PaPage.getBody().scrollTop
            };
};

/* *
*	Swap real Browse button with fake one.
*
*	Note: All involved elements should be absolutely positioned.
*
*	BuGGY: Opera returns only file name instead of full path.
*
*	@access public
*	@param integer inputWidth Desired (but it`s better to be 222) width in pixels for a text input at the left of a real button
*	@param string sInputFileId ID of the input described above
*	@param string sParentInputFileId ID of the element which contains the real button
*	@param string sFakeInputFileId ID of a text input to be on the left of the fake button
*	@return void
*/
PaPage.swapBrowseButton = function (inputWidth, sInputFileId, sParentInputFileId, sFakeInputFileId)
{
    var oInputFile      = document.getElementById(sInputFileId);
    var oInputFileFake  = document.getElementById(sFakeInputFileId);

    var oButtonSize     = PaClient.getBrowseButtonSize();
    var tempInputWidth  = inputWidth - oButtonSize.width;
    var tempInputHeight = oButtonSize.height + 4;

    if (PaClient.isOpera())
    {
        tempInputWidth = tempInputWidth - 12;
    }
    else
    {
        tempInputWidth = tempInputWidth + 4;
    }

    oInputFile.style.width        = inputWidth + 'px';
    oInputFile.style.opacity      = 0;
    oInputFile.style.MozOpacity   = 0;
    oInputFile.style.KhtmlOpacity = 0;
    oInputFile.style.filter       = 'alpha(opacity: 0)';

    oInputFileFake.style.width  = tempInputWidth + 'px';

    document.getElementById(sParentInputFileId).style.clip = 'rect(0px ' + inputWidth + 'px ' + tempInputHeight + 'px ' + tempInputWidth + 'px)';

    PaEvent.addHandler('onchange', PaPage._copyFilePathToFakeInput, 'file-input', sFakeInputFileId);

    return true;
};

PaPage.highlightWord = function (node,word)
{
  /* Iterate into this nodes childNodes*/
  if (node.hasChildNodes) {
    var hi_cn;
    for (hi_cn=0;hi_cn<node.childNodes.length;hi_cn++) {
      highlightWord(node.childNodes[hi_cn],word);
    }
  }

  /* And do this node itself */
  if (node.nodeType == 3) { /* text node*/
    tempNodeVal = node.nodeValue.toLowerCase();
    tempWordVal = word.toLowerCase();
    if (tempNodeVal.indexOf(tempWordVal) != -1) {
      pn = node.parentNode;
      if (pn.className != "searchword") {
        /* word has not already been highlighted! */
        nv = node.nodeValue;
        ni = tempNodeVal.indexOf(tempWordVal);
        /* Create a load of replacement nodes*/
        before = document.createTextNode(nv.substr(0,ni));
        docWordVal = nv.substr(ni,word.length);
        after = document.createTextNode(nv.substr(ni+word.length));
        hiwordtext = document.createTextNode(docWordVal);
        hiword = document.createElement("span");
        hiword.className = "searchword";
        hiword.appendChild(hiwordtext);
        pn.insertBefore(before,node);
        pn.insertBefore(hiword,node);
        pn.insertBefore(after,node);
        pn.removeChild(node);
      }
    }
  }
};

PaPage.disableSelect = function ()
{
    var oShield = document.createElement('div');

    oShield.style.position = 'absolute';
    oShield.style.left     = 0;
    oShield.style.top      = 0;
    oShield.style.width    = oShield.style.height = '100px';
    /*oShield.id             = 'pa-select-shield';*/

    var oShield = new PaElement(oShield);

    oShield.attachToCursor(true);
};

/* *
*	Custom event handler for copying real file input path to the fake one.
*
*	@access private
*	@param object Prepared event object
*	@param object Reference to target object
*	@param object List of arguments passed
*	@return void
*/
PaPage._copyFilePathToFakeInput = function (oEvent, aArgs)
{
    document.getElementById(aArgs[0]).value = this.value;

    return true;
};

/* -- */

/*@*
*	PaSelect
*
*	------------------
*
*	Class for operating select elements.
*	Like all other element-targeted classes it fully inherits from PaElement.
*@*/

/* *
*	Class constructor
*
*	@return object original tree node stuffed with useful methods
*/
var PaSelect = function (mElement)
{
    return PaElement.call(this, mElement);
};

PaSelect.prototype = new PaElement;

/* *
*	Get selected option object (if any).
*
*	@access public
*	@param string Select elements`s ID
*	@return mixed Option object or null if nothing selected
*/
PaSelect.prototype.getSelected = function ()
{
    if (this.selectedIndex >= 0)
    {
        return this.options[this.selectedIndex]
    }

    return null;
};

PaSelect.prototype.removeOption = function (index)
{
    this.options[index] = null;
};

/* *
*	Get option by index.
*
*	@access public
*	@param string Select elements`s ID
*	@param string Option`s index
*	@return mixed Option object or null if option doesn`t exist
*/
PaSelect.prototype.getOption = function (index)
{
    return this.options[index];
};

/* *
*	Get option object by property value.
*
*	@access public
*	@param string Select elements`s ID
*	@param string Property name
*	@param mixed Property value to compare with existing one
*	@return mixed Option object or null if nothing found
*/
PaSelect.prototype.findOption = function (sProperty, mValue)
{
    for (var o = 0; o < this.length; o++)
    {
        for (var p in this.options[o])
        {
            if (p == sProperty)
            {
                if (this.options[o][p] == mValue)
                {
                    return this.options[o];
                }
                else
                {
                    break;
                }
            }
        }
    }
};

/* *
*	Make option with provided value selected.
*
*	@access public
*	@param string option`s value
*	@return boolean whether such option has been found
*/
PaSelect.prototype.selectByValue = function (sValue)
{
    for (var o = 0; o < this.options.length; o++)
    {
        if (this.options[o].innerHTML == sValue)
        {
            this.options[o].selected = true;

            return true;
        }
    }

    return false;
};

/* *
*	Make option with provided index selected.
*
*	@access public
*	@param string option`s index
*	@return boolean whether such option has been found
*/
PaSelect.prototype.selectByIndex = function (index)
{
    if (PaValidator.isDefined(this.options[index]))
    {
        this.options[index].selected = true;

        return true;
    }
    else
    {
        return false;
    }
};

PaSelect.prototype.truncate = function ()
{
    while (this.options.length)
    {
        this.options[this.options.length - 1] = null;
    }
};

PaSelect.prototype.addOption = function (sValue, sText)
{
    this.options[this.options.length] = new Option(sText, sValue);
};

PaSelect.prototype.fill = function (oOptions)
{
    this.truncate();

    for (var e in oOptions)
    {
        this.addOption(oOptions[e], e);
    }
};

/* -- */

/*@*
*	PaRadio
*
*	------------------
*
*	Class for operations with input elements type of radio.
*	Like all other element-targeted classes it fully inherits from PaElement.
*@*/

/* *
*	Class constructor
*
*	@return object original tree node stuffed with useful methods
*/
var PaRadio = function (mElement)
{
    return PaElement.call(this, mElement);
};

PaRadio.prototype = new PaElement;

/* *
*	Get selected option object (if any).
*
*	@access public
*	@param string Select elements`s ID
*	@return mixed Option object or null if nothing selected
*/
PaRadio.prototype.getSelected = function ()
{
    for (var r = 0; r < this.length; r++)
    {
        if (this[r].checked)
        {
            return this[r];
        }
    }

    return null;
};

/* -- */

/*@*
*	PaRoot
*
*	------------------
*
*@*/

var PaRoot = new Object;

PaRoot.get = function (sId)
{
    return document.getElementById(sId);
};

/* -- */

/*@*
*	PaTable
*
*	------------------
*
*	Class for operations with table elements.
*	Like all other element-targeted classes it fully inherits from PaElement.
*@*/

/* *
*	Class constructor
*
*	@return object original tree node stuffed with useful methods
*/
var PaTable = function (mElement)
{
    return PaElement.call(this, mElement);
};

PaTable.prototype = new PaElement;

/* *
*	Create class-specific element.
*
*	@return object
*/
PaTable.prototype.create = function ()
{
    return new PaTable(this.prototype.create('table'));
};

/* *
*	Get parent element of all rows. For framework use only.
*
*	@return object
*/
PaTable.prototype.getBody = function ()
{
    /*var oTableBody = this.getElementsByTagName('TBODY')[0];*/
    var oTableBody = this.tBodies;

    return oTableBody;
};

/* *
*	Get number of rows in the table
*
*	@return integer
*/
PaTable.prototype.getNumRows = function ()
{
    return this.getRows().length;
};

/* *
*	Get number of cells in the table or in the row provided
*
*	@param object optional row node
*	@return integer
*/
PaTable.prototype.getNumCells = function (oRow)
{
    var maxCells = 0;

    if (PaValidator.isDefined(oRow) && oRow.tagName.toLowerString() == 'tr')
    {
        maxCells = oRow.getElementsByTagName('TD').length + oRow.getElementsByTagName('TH').length;
    }
    else
    {
        var aRows    = this.getBody().getElementsByTagName('TR');

        for (var r in aRows)
        {
            var tempMaxCells = aRows[r].getElementsByTagName('TD').length + aRows[r].getElementsByTagName('TH').length;

            if (tempMaxCells > maxCells)
            {
                maxCells = tempMaxCells;
            }
        }
    }

    return maxCells;
};

/* *
*	Get collenction of row references
*
*	@return object
*/
PaTable.prototype.getRows = function ()
{
    /*var aRows = this.getBody().getElementsByTagName('TR');*/
    var aRows = this.rows;

    return aRows;
};

/* *
*	Get row reference
*
*	@param integer zero-based row index
*	@return object
*/
PaTable.prototype.getRow = function (index)
{
    return this.getRows()[index];
};

/* *
*	Insert row
*
*	@param integer zero-based index which new row will have
*	@return object new row
*/
/*PaTable.prototype.insertRow = function (index)
{
    [has native implementation]
};*/

/* *
*	Delete row
*
*	@param integer zero-based index of the row
*	@return void
*/
/*PaTable.prototype.deleteRow = function (index)
{
    [has native implementation]
};*/

/* *
*	Insert caption
*
*	@return object new caption
*/
PaTable.prototype.insertCaption = function ()
{
    return this.createCaption();
};

/* *
*	Delete caption
*
*	@return void
*/
/*PaTable.prototype.deleteCaption = function ()
{
    [has native implementation]
};*/

/* *
*	Get cells for the row provided
*
*	@return object cells collection
*/
PaTable.prototype.getCells = function (oRow)
{
    return oRow.getElementsByTagName('td');
};

/* *
*	Insert cell
*
*	@return object new cell
*/
PaTable.prototype.insertCell = function (oRow, index)
{
    return oRow.insertCell(index);
};

/* *
*	Delete cell
*
*	@return void
*/
PaTable.prototype.deleteCell = function (oRow, index)
{
    return oRow.deleteCell(index);
};

/* *
*	Add cells to the row and fill them explicitly with content provided
*
*	@param integer zero-based row index
*	@param array list of cell content
*	@return object mutated row
*/
PaTable.prototype.fillRow = function (mRow, aCellsContents)
{
    /* prepare row to fill */
    if (PaValidator.isElement(mRow))
    {
        var oRow = mRow;
    }
    else if (PaValidator.isInt(mRow))
    {
        var oRow = this.getRow(mRow);
    }
    else
    {
        /*throw TypeError*/
    }
    /* prepare content */
    if (PaValidator.isTag(aCellsContents, 'tr'))
    {
        var oRow = mRow;
    }
    else if (PaValidator.isInt(mRow))
    {
        var oRow = this.getRow(mRow);
    }
    else
    {
        /*throw TypeError*/
    }
    /*var oRow = this.getRow(index);

    if (!PaValidator.isDefined(oRow))
    {
        oRow = (new PaElement).create('tr');
    }

    if (PaValidator.isDefined(aCellsContents.tagName) && aCellsContents.tagName.toLowerCase() == 'tr')
    {
        var length = this.getCells(aCellsContents).length;
    }
    else
    {
        var length = aCellsContents.length;
    }

    for (var c = 0; c < length; c++)
    {
        var oCell = this.insertCell(oRow, c);
        //alert(oCell);
        oCell.innerHTML = 'Yo!';
    }*/

    return oRow;
};

/* *
*	Add rows and cells
*
*	@param array 2-dimensional, first for rows, second for cells
*	@return void
*/
PaTable.prototype.fill = function (oBodyContents)
{
    var numRows = this.getNumRows();
    /*var limit   = aTableContents.length + numRows;
    //alert(aTableContents.length);
    //for (var i = numRows, r = 0; i < limit; i++, r++)
    //{//alert(i + ' : ' + r);
        //this.fillRow(i, aTableContents[r]);
        //alert(this.getCells(aTableContents[r]).length);
    //	this.fillRow(i, this.getCells(aTableContents[r]));
    //}*/
    alert(oBodyContents);

    return undefined;
};

/* *
*	Concatenate tables
*
*	@param object table
*	@return object new table
*/
PaTable.prototype.concat = function (oTable)
{

};

/* *
*	Replace table rows with provided rows
*
*	@param integer zero-based row index
*	@param integer row counter
*	@param object with elements as replacement rows
*	@return object new table containing replaced rows
*/
PaTable.prototype.splice = function (offset, length, aReplacements)
{
    var oTableBody    = this.getBody();
    var aRows         = this.getRows();
    var mLastRow      = null;
    var initialLength = length;
    var numRows       = aRows.length;
    var oReturnTable  = document.createElement('table');

    for (var i = 0; i < numRows; i++)
    {
        if (offset <= i)
        {
            if (length > 0)
            {
                oReturnTable.appendChild(aRows[i].cloneNode(true));
                oTableBody.removeChild(aRows[i]);

                length--;
            }
            else
            {
                mLastRow = aRows[i - initialLength];

                break;
            }
        }
    }

    for (var r in aReplacements)
    {
        var oRow = document.createElement('tr');

        for (var c in aReplacements[r])
        {
            var oCellContent = document.createTextNode(aReplacements[r][c]);

            var oCell = document.createElement('td');

            oCell.appendChild(oCellContent);

            oRow.appendChild(oCell);
        }

        oTableBody.insertBefore(oRow, mLastRow);
    }

    return oReturnTable;
};

/* *
*	Copy table rows
*
*	@param integer zero-based row index
*	@param integer row counter
*	@param boolean whether to return reference to existing or to new cloned row nodes
*	@return object array of cloned nodes or row references
*/
PaTable.prototype.slice = function (offset, length, bReturnReferences)
{
    var aRows         = this.getRows();
    var numRows       = aRows.length;
    var oReturnTable  = null;
    var aReturn       = null;

    if (PaValidator.isDefined(bReturnReferences) && Boolean(bReturnReferences))
    {
        var oReturnTable = document.createElement('table');
    }
    else
    {
        var aReturn = new Array;
    }

    for (var i = 0; i < numRows; i++)
    {
        if (offset <= i)
        {
            if (length > 0)
            {
                if (PaValidator.isDefined(oReturnTable))
                {
                    oReturnTable.appendChild(aRows[i].cloneNode(true));
                }
                else
                {
                    aReturn.push(aRows[i]);
                }

                length--;
            }
            else
            {
                break;
            }
        }
    }

    return oReturnTable || aReturn;
};

PaTable.prototype.constructor = PaTable;

/* -- */

/*@*
*	PaStorage
*
*	------------------
*
*	Class for operations with temporal storage.
*	Saves nodes (not copies) and their position in childlist (with posibility to restore it easily).
*@*/

var PaStorage = function ()
{
    this.storage = document.createElement('div');
    this.storage.style.display = 'block';

    this.db = new Array;
};

PaStorage.prototype.save = function (oElement)
{
    for (var n in oElement.parentNode.childNodes)
    {
        if (oElement.parentNode.childNodes[n] == oElement)
        {
            this.db.push({'_parent' : oElement.parentNode, '_index' : n});

            break;
        }
    }

    this.storage.appendChild(oElement);

    return this.storage.childNodes.length - 1;
};

PaStorage.prototype.restore = function (storageId, newOrder)
{
    if (this.storage.childNodes[storageId].tagName != 'dummy')
    {
        var oTargetNode = this.storage.replaceChild(document.createElement('dummy'), this.storage.childNodes[storageId]);

        if (PaValidator.isInt(newOrder))
        {
            this.db[storageId]._parent.insertBefore(oTargetNode, this.db[storageId]._parent.childNodes[newOrder]);
        }
        else
        {
            this.db[storageId]._parent.insertBefore(oTargetNode, this.db[storageId]._parent.childNodes[this.db[storageId]._index]);
        }

        /*delete this.db[storageId];

        this.storage.appendChild(document.createElement('br'));*/
    }
};

/* -- */

/*@*
*	PaDate
*
*	------------------
*
*	Class for manipulations with dates.
*@*/

var PaDate = function (timestamp)
{
    if (PaValidator.isDefined(timestamp))
    {
        var oDate = new Date(timestamp);
    }
    else
    {
        var oDate = new Date();
    }

    this.date = oDate;

    var oTopThis = this;

    this.months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];

    this.days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];

    this.marks =
    {
        d : function ()
        {
            var date = oTopThis.marks.j();

            if (new String(date).length < 2)
            {
                date = '0' + date;
            }

            return date;
        },
        D : function ()
        {
            return oTopThis.marks.l().substr(0, 3);
        },
        j : function (bGmt)
        {
            if (PaValidator.isEmpty(bGmt))
            {
                return oTopThis.date.getUTCDate();
            }
            else
            {
                return oTopThis.date.getDate();
            }
        },
        l : function ()
        {
            return oTopThis.days[oTopThis.marks.w()];
        },
        S : function ()
        {
            var date = oTopThis.marks.j();

            switch (date)
            {
                case  1:
                case 21:
                case 31:
                {
                    var ordinalSuffix = 'st';
                }
                case  2:
                case 22:
                {
                    var ordinalSuffix = 'nd';
                }
                case  3:
                case 23:
                {
                    var ordinalSuffix = 'rd';
                }
                default:
                {
                    var ordinalSuffix = 'th';
                }
            }

            return ordinalSuffix;
        },
        w : function (bGmt)
        {
            if (PaValidator.isEmpty(bGmt))
            {
                return oTopThis.date.getUTCDay();
            }
            else
            {
                return oTopThis.date.getDay();
            }
        },
        z : function ()
        {
            var oNewYear       = new Date('January 1 ' + oTopThis.marks.Y() + ' 00:00:00');
            var timeDifference = oTopThis.date.getTime() - oNewYear.getTime();

            return Math.floor(timeDifference / 1000 / 60 / 60 / 24);
        },
        W : function ()
        {
            var dayOfWeek = oTopThis.marks.w();
            var dayOfYear = oTopThis.marks.z();

            var daysFromNY = 364 + oTopThis.marks.L() - dayOfYear;

            if (daysFromNY <= 2 && dayOfWeek <= (3 - daysFromNY))
            {
                return 1;
            }

            if (dayOfYear <= 2 && dayOfWeek >= 5)
            {
                return new PaDate(oTopThis.marks.Y() - 1, 11, 31).format('W');
            }

            var dayOfWeekNew = new Date(oTopThis.marks.Y(), 0, 1).getDay();

            dayOfWeekNew = dayOfWeekNew != 0 ? dayOfWeekNew - 1 : 6;

            if (dayOfWeekNew <= 3)
            {
                return (1 + Math.floor((dayOfYear + dayOfWeekNew) / 7));
            }
            else
            {
                return (1 + Math.floor((dayOfYear - (7 - dayOfWeekNew)) / 7));
            }
        },
        F : function ()
        {
            return oTopThis.months[oTopThis.marks.n()];
        },
        m : function ()
        {
            var month = oTopThis.marks.n();

            if (new String(month).length < 2)
            {
                month = '0' + month;
            }

            return month;
        },
        M : function ()
        {
            return oTopThis.marks.F().substr(0, 3);
        },
        n : function (bGmt)
        {
            if (PaValidator.isEmpty(bGmt))
            {
                return oTopThis.date.getUTCMonth();
            }
            else
            {
                return oTopThis.date.getMonth();
            }
        },
        t : function ()
        {
            var aNumDays = ['', 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
            var month    = oTopThis.marks.n();

            if (oTopThis.marks.L() == 1 && month == 2)
            {
                return 29;
            }
            else
            {
                return aNumDays[month];
            }
        },
        L : function ()
        {
            var year = oTopThis.marks.Y();

            if
            (
                year % 4 == 0
                &&
                (
                    year % 100 != 0
                    ||
                    (year % 100 == 0 && year % 400 == 0)
                )
            )
            {
                return 1;
            }
            else
            {
                return 0;
            }
        },
        Y : function (bGmt)
        {
            if (PaValidator.isEmpty(bGmt))
            {
                return oTopThis.date.getUTCFullYear();
            }
            else
            {
                return oTopThis.date.getFullYear();
            }
        },
        y : function ()
        {
            return new String(oTopThis.marks.Y()).substr(-2);
        },
        a : function ()
        {
            return oTopThis.marks.A().toLowerCase();
        },
        A : function ()
        {
            var hours = oTopThis.marks.g();

            if (hours >= 0 && hours <= 11)
            {
                var sMeridiem = 'AM';
            }
            else
            {
                var sMeridiem = 'PM';
            }

            return sMeridiem;
        },
        g : function ()
        {
            return parseInt(oTopThis.marks.h());
        },
        G : function ()
        {
            return parseInt(oTopThis.marks.H());
        },
        h : function ()
        {
            var hours = oTopThis.marks.H() - 12;

            if (hours < 0)
            {
                hours = -hours;
            }
            else if (hours == 0)
            {
                hours = 12;
            }

            return hours;
        },
        H : function (bGmt)
        {
            if (PaValidator.isEmpty(bGmt))
            {
                return oTopThis.date.getUTCHours();
            }
            else
            {
                return oTopThis.date.getHours();
            }
        },
        i : function (bGmt)
        {
            if (PaValidator.isEmpty(bGmt))
            {
                var minutes = oTopThis.date.getUTCMinutes();
            }
            else
            {
                var minutes = oTopThis.date.getMinutes();
            }

            if (new String(minutes).length < 2)
            {
                minutes = '0' + minutes;
            }

            return minutes;
        },
        s : function (bGmt)
        {
            if (PaValidator.isEmpty(bGmt))
            {
                var seconds = oTopThis.date.getUTCSeconds();
            }
            else
            {
                var seconds = oTopThis.date.getSeconds();
            }

            if (new String(seconds).length < 2)
            {
                seconds = '0' + seconds;
            }

            return seconds;
        },
        U : function ()
        {
            return oTopThis.date.valueOf() / 1000;
        }
    };
};

PaDate.prototype.format = function (sFormat, bGmt, oMonths, oDays)
{
    var aMarks = sFormat.split('');

    if (PaValidator.isDefined(oMonths))
    {
        this.months = oMonths;
    }

    if (PaValidator.isDefined(oDays))
    {
        this.days = oDays;
    }

    for (var e = 0; e < aMarks.length; e++)
    {
        if (aMarks[e - 1] == '\\')
        {
            aMarks[e - 1] = '';

            continue;
        }

        if (PaValidator.isDefined(this.marks[aMarks[e]]))
        {
            aMarks[e] = this.marks[aMarks[e]](bGmt);
        }
    }

    return aMarks.join('');
};

/* *
*	Get difference between two days.
*
*	@access public
*	@static
*	@param object Date instance
*	@param object Date instance
*	@param boolean if return difference in days
*	@return int
*/
PaDate.diff = function (oDateFirst, oDateSecond, bCalcInDays)
{
    var minuteDuration = 1000 * 60;

    if (PaValidator.isBool(bCalcInDays) && bCalcInDays === true)
    {
        oDateFirst.setHours(0);
        oDateFirst.setMinutes(0);
        oDateFirst.setSeconds(0);
        oDateSecond.setHours(0);
        oDateSecond.setMinutes(0);
        oDateSecond.setSeconds(0);
    }

    if (oDateFirst > oDateSecond)
    {
        var adjustment = (oDateFirst.getTimezoneOffset() - oDateSecond.getTimezoneOffset()) * minuteDuration;
    }
    else
    {
        var adjustment = (oDateSecond.getTimezoneOffset() - oDateFirst.getTimezoneOffset()) * minuteDuration;
    }

    var dateDifference = Math.abs(oDateSecond.getTime() - oDateFirst.getTime()) - adjustment;

    if (PaValidator.isBool(bCalcInDays) && bCalcInDays === true)
    {
        return Math.ceil(dateDifference / minuteDuration * 60 * 24);
    }
    else
    {
        return dateDifference;
    }
};

/* *
*	Check date existance.
*
*	@access public
*	@static
*	@param string date
*	@param int endianness, 2 - big endian format e.g. 2007-01-31, 1 - middle endian 01/31/2007, 0 - little endian (default) 31.01.2007
*	@return boolean
*/
PaDate.isValid = function (sDate, endianness)
{
    switch (endianness)
    {
        case 2 :
        {
            var oDate_REGEXP = /^(?:\d{2}|\d{4})[\/\-\.]\d{1,2}[\/\-\.]\d{1,2}$/;

            if (!oDate_REGEXP.test(sDate))
            {
                return false;
            }

            var aDateChunks = sDate.split(/[\/\-\.]/);

            var year  = aDateChunks[0];
            var month = aDateChunks[1];
            var day   = aDateChunks[2];

            break;
        }
        case 1 :
        {
            var oDate_REGEXP = /^\d{1,2}[\/\-\.]\d{1,2}[\/\-\.](?:\d{2}|\d{4})$/;

            if (!oDate_REGEXP.test(sDate))
            {
                return false;
            }

            var aDateChunks = sDate.split(/[\/\-\.]/);

            var month = aDateChunks[0];
            var day   = aDateChunks[1];
            var year  = aDateChunks[2];

            break;
        }
        default :
        {
            var oDate_REGEXP = /^\d{1,2}[\/\-\.]\d{1,2}[\/\-\.](?:\d{2}|\d{4})$/;

            if (!oDate_REGEXP.test(sDate))
            {
                return false;
            }

            var aDateChunks = sDate.split(/[\/\-\.]/);

            var day   = aDateChunks[0];
            var month = aDateChunks[1];
            var year  = aDateChunks[2];

            break;
        }
    }

    if (PaValidator.isDefined(oDate_REGEXP))
    {
        if (year.length == 2)
        {
            var oNow_DATE = new Date;

            var century = parseInt(oNow_DATE.getFullYear() / 100);

            year = century + '' + year;
        }

        var oTest_DATE = new Date(year, month - 1, day);

        if
        (
            oTest_DATE.getDate() == day
            &&
            oTest_DATE.getMonth() + 1 == month
            &&
            oTest_DATE.getFullYear() == year
        )
        {
            return true;
        }
    }

    return false;
};

/* *
*	Get pattern for strftime.
*
*   Limitations : on certain locales (maybe chinese :) month names represented as string
*   won`t be detected and stay as is.
*
*	@access public
*	@static
*	@return string pattern
*/
PaDate.getLocalePattern = function ()
{
    var oDummy_DATE = new Date;

    oDummy_DATE.setFullYear(2003, 1, 1);
    oDummy_DATE.setHours(19, 5, 6);

    var sTempYear = new String(oDummy_DATE.getFullYear()).replace(/0/g, '');
    var sDateTime = oDummy_DATE.toLocaleString().replace(/0/g, '');

    /***/
    var sDate         = oDummy_DATE.toLocaleDateString();
    var aDateChunks   = sDate.split(/\s+/);
    var sLiteralMonth = '';

    for (var e = 0; e < aDateChunks.length; e++)
    {
        if (!/\d/.test(aDateChunks[e]) && aDateChunks[e].length > 2)
        {
            sLiteralMonth = aDateChunks[e];
            break;
        }
    }

    if (sLiteralMonth)
    {
        sDateTime = sDateTime.replace(sLiteralMonth, '%B');
    }
    /***/

    var aDateTimeRawChunks = oDummy_DATE.toLocaleString().split(/[^0-9]/);
    var aDateTimeChunks    = sDateTime.split(/[^0-9]+/);
    var aDelimiters        = sDateTime.split(/[0-9]+/);

    if (PaClient.isIe())
    {
        aDelimiters.unshift('');
        aDelimiters.push('');
    }

    var sMask = '';

    for (var e = 0; e < aDelimiters.length; e++)
    {
        if (PaValidator.isDefined(aDateTimeChunks[e]))
        {
            var sMarker = '';

            switch (aDateTimeChunks[e])
            {
                case '1' :
                {
                    if (aDateTimeRawChunks[e].length > 1)
                    {
                        sMarker = '%d';
                    }
                    else
                    {
                        sMarker = '%e';
                    }
                    break;
                }
                case '2' :
                {
                    sMarker = '%m';
                    break;
                }
                case '3' :
                case sTempYear :
                {
                    if (aDateTimeRawChunks[e].length > 2)
                    {
                        sMarker = '%Y';
                    }
                    else
                    {
                        sMarker = '%y';
                    }
                    break;
                }
                case '7' :
                case '19' :
                {
                    if (aDateTimeChunks[e] == '7')
                    {
                        sMarker = '%I';
                    }
                    else
                    {
                        sMarker = '%H';
                    }
                    break;
                }
                case '5' :
                {
                    sMarker = '%M';
                    break;
                }
                case '6' :
                {
                    sMarker = '%S';
                    break;
                }
            }

            sMask += '' + aDelimiters[e] + '' + sMarker;
        }
    }

    return sMask;
};

PaDate.prototype.constructor = PaDate;

/* -- */

/*@*
*	PaCookie
*
*	------------------
*
*	Collection of cookie setters and getters.
*@*/

var PaCookie = new Object;

/* *
*	Set cookie.
*
*	@access public
*	@param string cookie name
*	@param mixed cookie value
*	@param object of Date class
*	@param string path
*	@param string domain
*	@param boolean whether this cookie should be visible for HTTPS pages only
*	@return mixed null if cookie is not found
*/
PaCookie.set = function (sName, mValue, oExpiration, sPath, sDomain, bSecure)
{
    document.cookie = sName + '=' + escape(mValue)
    + (oExpiration ? ';expires=' + oExpiration.toGMTString() : '')
    + (sPath ? ';path=' + sPath : '')
    + (sDomain ? ';domain=' + sDomain : '')
    + (bSecure ? ';secure' : '');
};

/* *
*	Get cookie value by name.
*
*	@access public
*	@return mixed null if cookie is not found
*/
PaCookie.get = function (sCookieName)
{
    var oCookies_ARRAY = PaCookie.getAll();

    return oCookies_ARRAY[sCookieName] ? oCookies_ARRAY[sCookieName] : null;
};

/* *
*	Get reference to the cookie object.
*
*	@access public
*	@return object
*/
PaCookie.getReference = function ()
{
    return document.cookie;
};

/* *
*	Get cookies hash.
*
*	@access public
*	@return object
*/
PaCookie.getAll = function ()
{
    var oRawCookies_ARRAY = PaCookie.getReference().split('; ');
    var oCookies_ARRAY    = new Array;

    for (var e = 0; e < oRawCookies_ARRAY.length; e++)
    {
        var oCookieChunk_ARRAY                = oRawCookies_ARRAY[e].split('=');
        oCookies_ARRAY[oCookieChunk_ARRAY[0]] = unescape(oCookieChunk_ARRAY[1]);
    }

    return oCookies_ARRAY;
};

/* *
*	Destroy cookie.
*
*	@access public
*	@param string cookie name
*	@return boolean
*/
PaCookie.destroy = function (sName)
{
    if (PaValidator.isNull(PaCookie.get(sName)))
    {
        return false;
    }
    else
    {
        var oDummy_DATE = new Date;

        oDummy_DATE.setTime(oDummy_DATE.getTime() - 1);

        PaCookie.set(sName, '', oDummy_DATE.toGMTString());
    }

    return true;
};

/* -- */

/*@*
*	PaWidgets
*
*	------------------
*
*	Considered to be a collection of widgets.
*@*/

var PaWidgets = new Object;

document._PaWidgets = new Object;

PaWidgets.clock = new Object;

document._PaWidgets.clock = new Object;

document._PaWidgets.clock.ids = new Array;

PaWidgets.clock.open = function (sId, timestamp, bGmt, aMonths, aDays)
{
    if (!PaValidator.isDefined(document._PaWidgets.clock.ids[sId]))
    {
        if (PaValidator.isDefined(timestamp))
        {
            var oDate = new Date(timestamp);

            document._PaWidgets.clock.ids[sId] = (new Date).valueOf() - timestamp;
        }
        else
        {
            var oDate = new Date;

            document._PaWidgets.clock.ids[sId] = 0;
        }
    }
    else
    {
        var oDate = new Date((new Date).valueOf() - document._PaWidgets.clock.ids[sId]);
    }

    document.getElementById(sId).innerHTML = new PaDate(oDate.valueOf()).format('H:i:s F j, Y', bGmt, aMonths, aDays);

    setTimeout('PaWidgets.clock.open("' + sId + '")', 1000);
};