Ext.BLANK_IMAGE_URL  = '/public/images/default/s.gif';

// opera ajax bug fix
Ext.SSL_SECURE_URL = 'about:dummy.html';

/*$$$ = function (b, m) {
    m = typeof m == 'undefined' ? 'Y29udGFjdEBteS1kaWFiZXQuY29t' : m;
    b.innerHTML = Base64.decode(m);
}   */

$$$$ = function (h, m) {
    h = typeof h == 'string' ? document.getElementById(h) : h;
    if (typeof h.innerHTML != 'undefined' && typeof h.href != 'undefined') {
        m = typeof m == 'undefined' ? 'Y29udGFjdEBteS1kaWFiZXQuY29t' : m;
        m = Base64.decode(m);
        h.href = 'mailto: ' + m;
        h.innerHTML = m;
    }
}

var Core = function(){
    var msgCt;

    function createBox(t, s){
        return ['<div class="msg">',
                '<div class="x-box-tl"><div class="x-box-tr"><div class="x-box-tc"></div></div></div>',
                '<div class="x-box-ml"><div class="x-box-mr"><div class="x-box-mc"><h3>', t, '</h3>', s, '</div></div></div>',
                '<div class="x-box-bl"><div class="x-box-br"><div class="x-box-bc"></div></div></div>',
                '</div>'].join('');
    }
    return {
        msg : function(title, format){
            if(!msgCt){
                msgCt = Ext.DomHelper.insertFirst(document.body, {id:'msg-div'}, true);
            }
            msgCt.alignTo(document, 't-t');
            var s = format;
//            var s = String.format.apply(String, Array.prototype.slice.call(arguments, 1));
            var m = Ext.DomHelper.append(msgCt, {html:createBox(title, s)}, true);
            m.slideIn('t').pause(1).ghost("t", {remove:true});
        }
    };
}();

Core.translate = {
    langpack : {}
};
Core.translate.get = function (key, list, lang, type){
    if (typeof lang == 'undefined' || lang == null) {
        if (!this.language) {
            return key;
        }

        lang = this.language;
    }

    if (typeof list == 'undefined' || list == null) {
        if (!this.list) {
            return key;
        }

        list = this.list;
    }

    if (typeof type == 'undefined' || type == null) {
        if (!this.type) {
            return key;
        }

        type = this.type;
    }

    return this.langpack[lang] && this.langpack[lang][type] && this.langpack[lang][type][list] && this.langpack[lang][type][list][key] ? this.langpack[lang][type][list][key] : key;
}

Ext.apply(Ext.form.VTypes, {
    password:  function(v) {
        return /^.*(?=.{6,80})(?=.*\d)(?=.*[a-z]).*$/.test(v);
    },
    passwordText: Core.translate.get('invalidPasswordFormat'),

    initpassword : function(val, field) {
        if (field.initialPasswordField) {
            var pwd = Ext.getCmp(field.initialPasswordField);
            return (val == pwd.getValue());
        }
        return true;
    },
    initpasswordText : 'Passwords do not match',

    phoneNumber : function(val, field) {
        var pattern = /^[\d \(\)#\-\+]+$/;
        if (!pattern.test(val))
        {
            var str = val.replace(/[^\d ()#-+]+/, '');
            field.setValue(str);
        }
        return true;
    },

    urlUpd : function(val, field) {
        var pattern = /^([a-z]+?:\/\/)?([\w!~*'()-]+\.)*([\da-z][\da-z-]{0,61})?[\da-z]\.[a-z]{2,6}((\/?)|(\/[\w!~*'().;?:@&=+$,%#-]+)+\/?)$/;
        return pattern.test(val);
    }
});/**/

Core.sliderValue = 150;

Core.date = new Date();

Core.removeAnchor = function()
{
    var url = window.location.href.split('#');
    if (url && url[0]) {
        return url[0];
    } else {
        return null;
    }
}

Core.removeUrlParameter = function(parameterName) {
    var url = Core.removeAnchor();
    var pattern = new RegExp('/' + parameterName + '/[^/]*(/|$)');
    return url.replace(pattern, '');
}

Core.deleteFromArray = function(arr, value)
{
    for(var i = 0; i < arr.length; i++)
    {
        if(arr[i] == value) arr.splice(i, 1);
    }

    return arr;
}

Core.inArray = function(arr, value)
{
    for (var i = 0; i < arr.length; i++) {
        if (arr[i] == value) {
            return true;
        }
    }

    return false;
}

Core.evalResponse = function(response, options)
{
    eval(response.responseText);
};

Core.alertResponse = function(response, options)
{
    alert(response.responseText);
};


Core.failureResponse = function()
{
    Ext.MessageBox.alert('Error', 'Can not process request');
};

Core.evalRequest = function(processor, page, action, params)
{
    var data = {page: page, action: action, data: Base64.encode(Ext.encode(params))};
    Ext.Ajax.request({
    url: processor,
    success: Core.evalResponse,
    failure: Core.failureResponse,
    method: 'POST',
    params: data
    });
};

Core.alertRequest = function(processor, page, action, params)
{
    var data = {page: page, action: action, data: Base64.encode(Ext.encode(params))};
    Ext.Ajax.request({
    url: processor,
    success: Core.alertResponse,
    failure: Core.failureResponse,
    method: 'POST',
    params: data
    });
};

Core.maskRedirect = function(url, msg, translate)
{
    translate = typeof translate != 'undefined' ? translate : false;
    msg = typeof msg != 'undefined' ? (translate ? Core.translate.get(msg, 'all') : msg) : Core.translate.get('loadingMsg', 'all');

    Core.mask.show(msg);
    window.location.href = url;
}

Core.request = function(processor, page, action, params, callback, autoAbortInput)
{
    var data = {page: page, action: action, data: Base64.encode(Ext.encode(params))};
    Ext.Ajax.request({
    url: processor,
    callback: callback,
    method: 'POST',
    params: data,
	autoAbort: autoAbortInput
    });
};

Core.defaultFormRequestFailure = function(form, action) {
    switch (action.failureType) {
        case Ext.form.Action.CONNECT_FAILURE:
            Core.msg(Core.translate.get('reqFailure', 'all'), Core.translate.get('reqAjaxFailure', 'all'));
            break;
        default:
            Core.msg(Core.translate.get('reqFailure', 'all'), action.result.msg);
    }
    if (action.result && action.result.errors) {
        Core.handleErrors(action.result.errors);
    }
}

Core.formRequest = function(formPanel, processor, page, action, options) {
    if (formPanel.getForm().isValid()) {

        var submitOptions = {
            url : processor,
            waitMsg: Core.translate.get('formSubmitMessage'),
            waitTitle: Core.translate.get('formSubmitTitle')
        };

        var clientValidation = typeof options['clientValidation'] != 'undefined' ? options['clientValidation'] : true;
        submitOptions.clientValidation = clientValidation;

        var params = typeof options['params'] != 'undefined' ? options['params'] : null;
        submitOptions.params = {
            page: page,
            action: action,
            data: Base64.encode(Ext.encode(params))
        };

        if (typeof options['success'] == 'function') {
            submitOptions.success = options['success'];
        }

        if (typeof options['failure'] == 'function') {
            submitOptions.failure = options['failure'];
        } else {
            submitOptions.failure = Core.defaultFormRequestFailure;
        }

        formPanel.getForm().submit(submitOptions);
    } else {
        Core.msg(Core.translate.get('reqFailure', 'all'), Core.translate.get('reqFormInvalidated', 'all'));
    }
}

Core.formatDate = function(value)
{
    return value ? value.dateFormat('d-M-Y') : '';
}

Core.formatPrice = function(v)
{
    if(v == 'null' || !v) return '';

    return v;
}

Core.formatPriceDot = function(v)
{
    if(v == 'null' || !v) return '';

    return Ext.util.Format.number(v, '0.0');
}

Core.formatNumber = function(v)
{
    if (v == 'null' || !v) {
        return '';
    }
    v = v - 0;
    v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : String(v));
    var ps = v.split('.');
    var whole = ps[0];
    var r = /(\d+)(\d{3})/;
    while (r.test(whole)) {
        whole = whole.replace(r, '$1' + ',' + '$2');
    }
    v = whole + '.' + ps[1];
    return v;
}

/**
 * Returns non-assoc array combining assoc array (source) with its index.
 * {'id' : 1, 'title' : 'Title'}, ['id', 'title'] => [1, 'Title']
 */
Core.hashToArray = function(source, indexSequence)
{
    var result = [];
    for (var i = 0; i < indexSequence.length; i++) {
        result[i] = source[indexSequence[i]];
    }
    return result;
}

/**
 * Iterates through source assoc array set and applies to each element Core.hashToArray.
 * [{'id' : 1, 'title' : 'Title'}, {'id' : 2, 'title' : 'Title2'}], ['id', 'title'] => [[1, 'Title'], [2, 'Title2']]
 */
Core.hashSetToArraySet = function(source, indexSequence)
{
    var result = [];
    for (var i = 0; i < source.length; i++) {
        result[i] = Core.hashToArray(source[i], indexSequence);
    }
    return result;
}

/**
 * Requests server for given page, action and params.
 * On success data received from the server will be loaded to component with id equal to target (target either must a component itself) data store.
 * If parameter dataTransformMask is defined, data will be transformed using Core.hashSetToArraySet before load.
 */
Core.loadRemoteData = function(page, action, params, target, dataTransformMask) {
    Core.request('/event/', page, action, params, function(o, s, r) {
        if (s) {
            var response = Ext.decode(r.responseText);
            if (response.success) {
                Core.msg(Core.translate.get('reqSuccess', 'all'), response.msg);
                if (response.data) {
                    var d = typeof dataTransformMask != 'undefined' ? Core.hashSetToArraySet(response.data, dataTransformMask) : response.data,
                        t = typeof target == 'string' ? Ext.getCmp(target) : target;

                    t.getStore().loadData(d);
                }
            }
            else {
                Core.msg(Core.translate.get('reqFailure', 'all'), response.msg);
            }
        }
        else {
            Core.msg(Core.translate.get('reqFailure', 'all'), Core.translate.get('reqAjaxFailure', 'all'));
        }
    });
}

Core.performAction = function(page, action, params) {
    Core.request('/event/', page, action, params, function(o, s, r) {
        if (s) {
            var response = Ext.decode(r.responseText);
            if (response.success) {
                Core.msg(Core.translate.get('reqSuccess'), response.msg);
            }
            else {
                Core.msg(Core.translate.get('reqFailure'), response.msg);
            }
        }
        else {
            Core.msg(Core.translate.get('reqFailure'), Core.translate.get('reqAjaxFailure'));
        }
    });
}

Core.performActionWithCallback = function(page, action, params) {
    Core.request('/event/', page, action, params, function(o, s, r) {
        if (s) {
            var response = Ext.decode(r.responseText);
            if (response.success) {
                Core.msg(Core.translate.get('reqSuccess'), response.msg);
                if (typeof Core.successCallback == 'function') {
                    Core.successCallback();
                }
            }
            else {
                Core.msg(Core.translate.get('reqFailure'), response.msg);
                if (typeof Core.failureCallback == 'function') {
                    Core.failureCallback();
                }
            }
        }
        else {
            Core.msg(Core.translate.get('reqFailure'), Core.translate.get('reqAjaxFailure'));
        }
    });
}

Core.handleErrors = function(errors, form) {
    var message = '';
    for (var i in errors) {
        if (typeof errors[i] == 'string') {
            message += '* ' + errors[i] + '<br />';
        } else {
            // "i" is an element name, so it seems we can find and element, which this error array is referred to.
            // perhaps we will have to extend Ext.form.field class to display these errors (let's say a red-backgrounded box beneath the field).
            for (var l in errors[i]) {
                if (typeof errors[i][l] == 'string') {
                    message += '* ' + errors[i][l] + '<br />';
                }
            }
        }
    }

    Ext.Msg.alert(Core.translate.get('errorList'), message);
}

Core.handleClientErrors = function(form) {
    var fields = form.findByType('field'),
        message = '';
    if (fields && fields.length) {
        for (var i = 0; i < fields.length; i++) {
            if (!fields[i].isValid()) {
                message = '* ' + fields[i] + ' is not valid<br />';
            }
        }
    }

    if (message) {
        Ext.Msg.alert(Core.translate.get('errorList'), message);
    }
}

Core.getAllRecords = function(grid)
{
    var count = grid.getStore().getCount();
    var data = new Array();
    for(i = 0; i < count; i++)
    {
        data[i] = grid.getStore().getAt(i).data;
    }
    return data;
}

Ext.namespace("Ext.ux");
Ext.ux.comboBoxRenderer = function(combo) {
  return function(value) {
    var idx = combo.store.find(combo.valueField, value);
    var rec = combo.store.getAt(idx);
    if(rec){
        return htmlspecialchars(rec.get(combo.displayField));
    }else{
        return '';
    }
  };
}

Ext.ux.multiSelectRenderer = function(multiSelect) {
    return function(value) {
        var selectedValues = value.split(multiSelect.valueSeparator);
        var display = '';
        for (var i = 0; typeof (selectedValues[i]) != 'undefined'; i++) {
            if (!selectedValues[i]) {
                continue;
            }
            var id = multiSelect.store.find(multiSelect.valueField, selectedValues[i]);
            var rec = multiSelect.store.getAt(id);
            if (rec) {
                display += (rec.get(multiSelect.displayField) + multiSelect.textSeparator);
            }
        }
        if (display) {
            display = display.substr(0, display.length - multiSelect.textSeparator.length);
        }
        return display;
    };
}

Core.checkRowsSeparator = function(str) {
    if(str.indexOf('\r\n') != -1) {  // IE
        return '\r\n';
    } else {                           // Mozilla
        return '\n';
    }
}

/**
 * Split string to array by checkRowsSeparator's separator
 *
 * @param str str  -- a string to split
 *
 * @return array
 *
 * @author Alex Kondrashov <alexander.kondrashov@toasterbridge.com>
 * @since 2009/3/31
 */
Core.textareaSplitString = function(str) {
    var separator = Core.checkRowsSeparator(str);
    return str.split(separator);
}

/**
 * Textarea rows limiter
 *
 * @param str str  -- a string to limit
 * @param num limit  -- max rows count
 *
 * @return str
 *
 * @author Alex Kondrashov <alexander.kondrashov@toasterbridge.com>
 * @since 2009/3/31
 */
Core.rowsLimit = function(str, limit) {
    if(limit == null) limit = 6;
    var separator = Core.checkRowsSeparator(str);
    var Arr = str.split(separator);
    var newArr = Arr.splice(0, limit);
    return newArr.join(separator);
}

/**
 * Textarea rows corrector
 *
 * @param id fieldId  -- textarea field id
 * @param str msgHead  -- header of message
 * @param str msgBody  -- body of message
 *
 * @return str
 *
 * @author Alex Kondrashov <alexander.kondrashov@toasterbridge.com>
 * @since 2009/3/31
 */
Core.textareaRowLengthCorrect = function(fieldId, limit, msgHead, msgBody) {
    var thisField = Ext.getCmp(fieldId);
    var thisFieldValue = thisField.getValue();
    var newValue = Core.rowsLimit(thisFieldValue, limit);
    if (thisFieldValue != newValue) {
        thisField.setValue(newValue);
        Core.msg(msgHead, msgBody);
    }
}

/**
 * Divides string with only bool values into the array of parametres
 *
 * @param str str  -- a string like "open=1;closed=0;"
 *
 * @return array  -- "array(open = 1, closed = 0)"
 *
 * @author Alex Kondrashov <alexander.kondrashov@toasterbridge.com>
 * @since 2009/3/31
 */
Core.splitBoolParams = function(str) {
    var splittedStr = str.split(';');
    var resultArr = new Array();
    var tempArr = new Array();
    for(i = 0; i < splittedStr.length - 1; i++) {
        tempArr = splittedStr[i].split('=');
        resultArr[tempArr[0]] = tempArr[1];
    }
    return resultArr;
}

/**
 * Search in array of fields not valid values.
 * Also highlights not valid fields
 *
 * @example areFieldsValid('fieldId1', 'fieldId2');
 *
 * @param string (any quantity of field ids)
 * @return bool
 *
 * @author Alex Kondrashov <alexander.kondrashov@toasterbridge.com>
 * @since 2009/4/6
 */
Core.areFieldsValid = function() {
    var areAllFieldsValid = true;
    for (var i=0; i<arguments.length; i++) {
        if(Ext.getCmp(arguments[i]).isValid() == false) {
            areAllFieldsValid = false;
            Ext.getCmp(arguments[i]).markInvalid();
        }
    }
    return areAllFieldsValid;
}

/**
 * Shows all JavaScript object properties
 *
 * @param obj
 * @param str
 * @return void
 *
 * @author Alex Kondrashov <alexander.kondrashov@toasterbridge.com>
 * @since 2009/4/6
 */
Core.showObj = function(obj, maxLevel)
{
	if (!Core.thisTimeout) {
		Core.thisTimeout = window.setTimeout(Core.showObj, 100, obj, maxLevel);
		return false;
	}

	if (!maxLevel) {
		maxLevel = 4;
	}

	var newWin = window.open('JS_Object_Debug', 'Object_debug' + new Date(), 'width=800,height=600,toolbar=no,menubar=no,scrollbars=yes');

	newWin.document.write('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">');
	newWin.document.write('<html>');
	newWin.document.write('<head>');
	newWin.document.write('<style type="text/css">* {cursor: default !important;}</style>');
	newWin.document.write('<meta http-equiv="Content-Type" content="text/html; charset=utf-8">');
	newWin.document.write('<title>JS Object Debug from ' + Date() + '</title>');
	newWin.document.write('</head>');
	newWin.document.write('<body style="background-color: #7E7E7E; font: normal 12px verdana; color: white">');

	newWin.document.write('</body>');
	newWin.document.write('</html>');

	newWin.document.body.style.color = 'white';
	newWin.document.body.style.backgroundColor = '#7E7E7E';
	newWin.document.body.style.fontSize = '76%';
	newWin.document.body.style.padding = '0';
	newWin.document.body.style.margin = '0';

	var content = Core.showObjRecursive(obj, maxLevel, newWin, 1);
	content.style.margin = '8px';

	newWin.document.body.appendChild(content);
}

Core.showObjRecursive = function(obj, maxLevel, newWin, level, pi) {
	var rezultNode = document.createElement('div');
	rezultNode.style.font = '1em/2em Verdana';
	if (!pi) pi = '';

	for (var i in obj) {
		if (obj instanceof Array && typeof(obj[i]) != 'function') {
			var regExp = /^\d+$/;
			if (regExp.test(i)) {
				var parentIdentificator = (i) ? pi + "[" + i + "]" : '';
			} else {
				var parentIdentificator = (i) ? pi + "['" + i + "']" : '';
			}
		} else {
			var parentIdentificator = (i) ? pi + '.' + i : '';
		}

		var div = document.createElement('div');

		var def = document.createElement('strong');
		if (obj instanceof Array && typeof(obj[i]) != 'function') {
			def.appendChild(document.createTextNode('[' + i + ']'));
		} else if (typeof(obj[i]) == 'function') {
			def.appendChild(document.createTextNode(i + '()'));
		} else {
			def.appendChild(document.createTextNode(i));
		}
		div.appendChild(def);

		var objType = document.createElement('italic');
		if (obj[i] instanceof Array) {
			objType.appendChild(document.createTextNode(' (array) => '));
		} else if (obj[i] instanceof RegExp) {
			objType.appendChild(document.createTextNode(' (regular expression) => '));
		} else if (obj[i] && obj[i].tagName) {
			objType.appendChild(document.createTextNode(' (' + obj[i] + ') => '));
		} else {
			objType.appendChild(document.createTextNode(' (' + typeof(obj[i]) + ') => '));
		}
		div.appendChild(objType);

		var content = obj[i] + '';

		var objInstanse = document.createElement('div');
		objInstanse.style.margin = '10px 0 10px 20px';
		objInstanse.style.border = '1px solid #8E8E8E'
		objInstanse.style.outline = '1px solid #676767';
		objInstanse.style.padding = '5px';

		var piSpan = document.createElement('div');
		piSpan.style.color = 'yellow';
		piSpan.style.marginBottom = '5px';
		if (typeof(obj[i]) == 'function') {
			piSpan.appendChild(document.createTextNode(parentIdentificator + '()'));
		} else {
			piSpan.appendChild(document.createTextNode(parentIdentificator));
		}
		objInstanse.appendChild(piSpan);

		if ((content == '[object Object]' && level < maxLevel) || obj[i] instanceof Array || (obj[i] && obj[i].tagName) || typeof(obj[i]) == 'function') {
			var button = document.createElement('button');
			button.style.height = '20px';
			button.style.width = '20px';
			button.style.textAlign = 'center';
			button.style.verticalAlign = 'middle';

			button.onclick = function() {
				if (this.parentNode.lastChild.style.display == 'none') {
					this.parentNode.lastChild.style.display = '';
				} else {
					this.parentNode.lastChild.style.display = 'none';
				}
				if (this.innerHTML == '+') {
					this.innerHTML = '-';
				} else {
					this.innerHTML = '+';
				}
			}

			button.appendChild(document.createTextNode('+'));
			div.appendChild(button);

			objInstanse.style.display = 'none';
			objInstanse.style.font = '1em/1.5em Verdana';
		}

		if (obj[i] instanceof Array) {
			objInstanse.appendChild(Core.showObjRecursive(obj[i], maxLevel, newWin, level + 1, parentIdentificator));
		} else if (obj[i] instanceof RegExp) {
			objInstanse = document.createElement('span');
			objInstanse.style.color = '#99bbff';
			objInstanse.appendChild(document.createTextNode(content));
		} else if (content == '[object Object]' && level < maxLevel) {
			objInstanse.appendChild(Core.showObjRecursive(obj[i], maxLevel, newWin, level + 1, parentIdentificator));
		} else if (typeof(obj[i]) == 'string') {
			objInstanse = document.createElement('span');
			objInstanse.appendChild(document.createTextNode("'" + content + "'"));
		} else if (content == '[object Object]' && level == maxLevel) {
			objInstanse = document.createElement('span');
			objInstanse.appendChild(document.createTextNode(content));
		} else if (typeof(obj[i]) == 'boolean' || typeof(obj[i]) == 'number' || !obj[i]) {
			objInstanse = document.createElement('span');
			objInstanse.style.color = '#ff9999';
			objInstanse.appendChild(document.createTextNode(content));
		} else if (typeof(obj[i]) == 'function') {
			var el = createElement('pre', {}, content);
			style(el, {'font': '1em/1.5em Verdana'});
			objInstanse.appendChild(el);
		} else {
			if (obj[i] && obj[i].tagName) {
				var tempDiv = document.createElement('div');
				tempDiv.appendChild(obj[i]);
				objInstanse.appendChild(document.createTextNode(tempDiv.innerHTML));
			} else {
				objInstanse.appendChild(document.createTextNode(content));
			}
		}

		div.appendChild(objInstanse);

		objType.style.color = '#C2D959';
		objType.style.fontStyle = 'italic';
		def.style.marginLeft = '5px';
		piSpan.style.marginLeft = '5px';

		rezultNode.appendChild(div);
	}

	return rezultNode;
}

/**
 * Shorthand of document.getElementById
 *
 * @string e -- id of element
 *
 * @return DOM Element
 */
function $(e) {
    if(typeof e == 'string')
        return document.getElementById(e);
    return e;
}

/**
 * Add new event to element
 *
 * @DOM Element obj -- DOM Element
 * @DOM string      -- event type ('click', 'load', ...)
 * @function fn     -- function
 *
 * @return bool -- true or false
 */
function addEvent(obj, evType, fn) {
    if(obj.addEventListener) {
        obj.addEventListener(evType, fn, false);
        return true;
    } else if(obj.attachEvent) {
        return obj.attachEvent("on" + evType, fn);
    }
    return false;
}

/**
 * Fires dom event of element
 */
function fireEvent(element, event) {
    if (document.createEventObject) {
        // dispatch for IE
        var evt = document.createEventObject();
        return element.fireEvent('on' + event, evt)
    } else {
        // dispatch for firefox + others
        var evt = document.createEvent("HTMLEvents");
        evt.initEvent(event, true, true); // event type,bubbling,cancelable
        return !element.dispatchEvent(evt);
    }
}

/**
 * @function `createElement` create and return a new element
 *
 * @string name     -- html name of new element
 * @array attrs     -- array of attributes
 * @DOM Element doc -- DOM element witch will contained new element
 * @t               -- append text
 *
 * @return DOM Element
 */
function createElement(name, attrs, text, doc) {
    var doc = doc ? doc : document;
    var elm = doc.createElement(name);
    if (text != null) elm.appendChild(doc.createTextNode(text));
    if (attrs) {
        for(attr in attrs) {
            elm.setAttribute(attr, attrs[attr]);
        }
    }
    return elm;
}

/**
 * @function `getChilds` return array of child nodes
 *
 * @DOM Element el  -- DOM element
 * @string tagName -- Tags name
 *
 * @return array of DOM Elements
 */
function getChilds(el, tagName) {
    var nodes = [];
    for(var i = 0; i < el.childNodes.length; i++)
        if(el.childNodes[i].nodeName.toLowerCase() == tagName)
            nodes.push(el.childNodes[i]);
    return nodes;
}

/**
 * set styles for element
 *
 * @DOM Element el  -- DOM element
 * @string styles -- array of styles, like `{color: 'red', margin: '0'}`
 *
 * @return void
 */
function style(el, styles) {
    for (var i in styles) {
        el.style[i] = styles[i];
    }
}

/**
 * return childs array
 *
 * @param DOM Element e -- Element to search
 * @param string tag -- tag name of child element
 *
 * @return array of DOM Elements
 */
function getChildByTagName(el, tag) {
    var regExp = new RegExp('^' + tag + '$', 'i');
    var childs = el.childNodes;
    var rezult = [];
    for (var i in childs) {
        if (childs[i].tagName && regExp.test(childs[i].tagName)) {
            rezult.push(childs[i]);
        }
    }
    return rezult;
}

/**
 * Core Mask Object
 * Core.mask.setMsg() -- LoadMask message
 * Core.mask.show()   -- shows LoadMask window
 * Core.mask.hide()   -- hide LoasMask window
 */
Core.mask = {};
Core.mask.load = function() {
    Core.mask.obj = new Ext.LoadMask(Ext.getBody(), {msg:'Loading...'});
}
Core.mask.setMsg = function(msg) {
    if (!Core.mask.obj) {
        Core.mask.load();
    }
    Core.mask.obj.msg = (msg) ? msg : 'Loading...';
}
Core.mask.show = function(msg) {
    if (msg || !Core.mask.obj) {
        Core.mask.setMsg(msg);
    }
    Core.mask.obj.show();
}
Core.mask.hide = function() {
	if (Core.mask.obj != undefined) {
    	Core.mask.obj.hide();
	}
}

/**
 * Returns Element object by class name
 *
 * @param string className  --  Class name
 * @param string node  --  Nodes name for faster searching. Search in Document if null
 * @param Element tag  --  Tag name for faster searching. It is '*' if null
 *
 * @return array of Element objects
 */
function getElementsByClass(className, node, tag) {
    var rezult = [];
    if (node == null) node = document;
    if (tag == null) tag = '*';
    var elements = node.getElementsByTagName(tag);
    var pattern = new RegExp(className);
    for (var i in elements) {
		if (elements[i] != undefined) {
        	if (pattern.test(elements[i].className)) rezult.push(elements[i]);
		}
    }
    return rezult;
}

/**
 * Returns mouse X and Y coords in all brausers
 *
 * @param event e  --  mouse event object
 *
 * @return array
 */
function mousePageXY(e)
{
  var x = 0, y = 0;

  if (!e) e = window.event;

  if (e.pageX || e.pageY)
  {
    x = e.pageX;
    y = e.pageY;
  }
  else if (e.clientX || e.clientY)
  {
    x = e.clientX + (document.documentElement.scrollLeft || document.body.scrollLeft) - document.documentElement.clientLeft;
    y = e.clientY + (document.documentElement.scrollTop || document.body.scrollTop) - document.documentElement.clientTop;
  }

  return {"x":x, "y":y};
}

Core.changeCellColor = function(elem, checked)//col, checked)
{
	if(checked) {
		elem.style.color = '#f00';
	}else{
		elem.style.color = '#000';
	}
	/*var gridPanel = Ext.getCmp('gridPanel');
	gridPanel.getStore().findBy(function(record, id) {
			if(record.data.id) {
				if(checked) {
					gridPanel.getView().getCell(record.data.lineNumber - 1, col).className += ' holdRow';
				}else{
					var elem = gridPanel.getView().getCell(record.data.lineNumber - 1, col);
					elem.className = elem.className.replace(' holdRow', '');
				}
			}
		});*/
}

Core.minelem = function(v)
{
    var m = v[0];
    for (var i=0; i < v.length; i++)
    {
        if (v[i] < m) {
            m = v[i];
        }
    }
    return m;
}

var timer = {b: 0, e: 0, d: 0, a: {}};
timer.start = function() {
    this.b = new Date();
}
timer.app = function(t) {
    this.a[t] = new Date();
}
timer.end = function() {
    this.e = new Date();
    this.d = this.e - this.b;
    var a = this.a;
    var str = '';
    for (var i in a) {
        var t = a[i] - this.b;
        str += i + ': ' + t + '\n';
    }
    alert(str + 'end: ' + this.d);
}

/* Base 64 */

var Base64 = {

//		 private property
		_keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",

//		 public method for encoding
		encode : function (input) {
			var output = "";
			var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
			var i = 0;

			input = Base64._utf8_encode(input);

			while (i < input.length) {

				chr1 = input.charCodeAt(i++);
				chr2 = input.charCodeAt(i++);
				chr3 = input.charCodeAt(i++);

				enc1 = chr1 >> 2;
				enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
				enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
				enc4 = chr3 & 63;

				if (isNaN(chr2)) {
					enc3 = enc4 = 64;
				} else if (isNaN(chr3)) {
					enc4 = 64;
				}

				output = output +
				this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
				this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);

			}

			return output;
		},

//		 public method for decoding
		decode : function (input) {
			var output = "";
			var chr1, chr2, chr3;
			var enc1, enc2, enc3, enc4;
			var i = 0;

			input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

			while (i < input.length) {

				enc1 = this._keyStr.indexOf(input.charAt(i++));
				enc2 = this._keyStr.indexOf(input.charAt(i++));
				enc3 = this._keyStr.indexOf(input.charAt(i++));
				enc4 = this._keyStr.indexOf(input.charAt(i++));

				chr1 = (enc1 << 2) | (enc2 >> 4);
				chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
				chr3 = ((enc3 & 3) << 6) | enc4;

				output = output + String.fromCharCode(chr1);

				if (enc3 != 64) {
					output = output + String.fromCharCode(chr2);
				}
				if (enc4 != 64) {
					output = output + String.fromCharCode(chr3);
				}

			}

			output = Base64._utf8_decode(output);

			return output;

		},

//		 private method for UTF-8 encoding
		_utf8_encode : function (string) {
			string = string.replace(/\r\n/g,"\n");
			var utftext = "";

			for (var n = 0; n < string.length; n++) {

				var c = string.charCodeAt(n);

				if (c < 128) {
					utftext += String.fromCharCode(c);
				}
				else if((c > 127) && (c < 2048)) {
					utftext += String.fromCharCode((c >> 6) | 192);
					utftext += String.fromCharCode((c & 63) | 128);
				}
				else {
					utftext += String.fromCharCode((c >> 12) | 224);
					utftext += String.fromCharCode(((c >> 6) & 63) | 128);
					utftext += String.fromCharCode((c & 63) | 128);
				}

			}

			return utftext;
		},

//		 private method for UTF-8 decoding
		_utf8_decode : function (utftext) {
			var string = "";
			var i = 0;
			var c = c1 = c2 = 0;

			while ( i < utftext.length ) {

				c = utftext.charCodeAt(i);

				if (c < 128) {
					string += String.fromCharCode(c);
					i++;
				}
				else if((c > 191) && (c < 224)) {
					c2 = utftext.charCodeAt(i+1);
					string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
					i += 2;
				}
				else {
					c2 = utftext.charCodeAt(i+1);
					c3 = utftext.charCodeAt(i+2);
					string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
					i += 3;
				}

			}

			return string;
		}

	}


/**
 * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
 * @param {String} value The string to encode
 * @return {String} The encoded text
 */
function htmlspecialchars(value)
{
   return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
}

/**
 * Converts newline characters to the HTML tag &lt;br/>
 * @param {String} The string value to format.
 * @return {String} The string with embedded &lt;br/> tags in place of newlines.
 */
function nl2br(v) {
	return v === undefined || v === null ? '' : v.replace(/\n/g, '<br/>');
}

function textareaSpecialchars(v) {
	return '<pre>' + nl2br(htmlspecialchars(v)) + '</pre>';
}

/**
 * Converts email group lists into array
 * Shows Ext.MessageBox.alert if any error found
 *
 * @param string emails -- emails to check
 * @param string msg    -- name of errors
 *
 * @return array
 */
Core.checkEmailGroup = function(emails, msg) {
    newEmails = emails.split('\n');
    emails = [];
    var regExp = /^\s+|\s+$/g;
    for (var i = 0; i < newEmails.length; i++) {
        var email = newEmails[i].replace(regExp, '');
        if (email != '') {
            emails.push(email);
        }
    }
    var regExp = /^(.*)\<(.*)\>$/;
    var validEmails = [];
    var email = [];
    for (var j = 0; j < emails.length; j++) {
        email = regExp.exec(emails[j]);
		if (email) {
        	validEmails.push({'address': email[2], 'name': email[1]});
		} else if (Ext.form.VTypes.email(emails[j])) {
        	validEmails.push({'address': emails[j], 'name': ''});
		} else if (emails[j] != '') {
			Ext.MessageBox.alert(htmlspecialchars(msg) + ' Error', '<span class="span-error-accent">' + htmlspecialchars(emails[j]) + '</span> is not correct email address!');
			return false;
		}
    }
    return validEmails;
}

/**
 * This function removes dirty class and resets form's original values
 *
 * @param Ext.form form -- a form to clean
 * @return void
 */
Core.cleanDirty = function(form) {
	form.getForm().items.each(function (B) {
		B.originalValue = B.getValue();
		B.removeClass('dirty-text-field');
	});
}

/**
 * This is an update for the Grid's templates. It allows to select grids in IE.
 */
Ext.grid.GridView.prototype.initTemplates = function () {
    var C = this.templates || {};
    if (!C.master) {
        C.master = new (Ext.Template)("<div class=\"x-grid3\" hidefocus=\"true\">", "<div class=\"x-grid3-viewport\">", "<div class=\"x-grid3-header\"><div class=\"x-grid3-header-inner\"><div class=\"x-grid3-header-offset\">{header:htmlEncode}</div></div><div class=\"x-clear\"></div></div>", "<div class=\"x-grid3-scroller\"><div class=\"x-grid3-body\">{body}</div><a href=\"#\" class=\"x-grid3-focus\" tabIndex=\"-1\"></a></div>", "</div>", "<div class=\"x-grid3-resize-marker\">&#160;</div>", "<div class=\"x-grid3-resize-proxy\">&#160;</div>", "</div>");
    }
    if (!C.header) {
        C.header = new (Ext.Template)("<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" style=\"{tstyle}\">", "<thead><tr class=\"x-grid3-hd-row\">{cells}</tr></thead>", "</table>");
    }
    if (!C.hcell) {
        C.hcell = new (Ext.Template)("<td class=\"x-grid3-hd x-grid3-cell x-grid3-td-{id}\" style=\"{style}\"><div {tooltip} {attr} class=\"x-grid3-hd-inner x-grid3-hd-{id}\" style=\"{istyle}\">", this.grid.enableHdMenu ? "<a class=\"x-grid3-hd-btn\" href=\"#\"></a>" : "", "{value}<img class=\"x-grid3-sort-icon\" src=\"", Ext.BLANK_IMAGE_URL, "\" />", "</div></td>");
    }
    if (!C.body) {
        C.body = new (Ext.Template)("{rows}");
    }
    if (!C.row) {
        C.row = new (Ext.Template)("<div class=\"x-grid3-row {alt}\" style=\"{tstyle}\"><table class=\"x-grid3-row-table\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" style=\"{tstyle}\">", "<tbody><tr>{cells}</tr>", (this.enableRowBody ? "<tr class=\"x-grid3-row-body-tr\" style=\"{bodyStyle}\"><td colspan=\"{cols}\" class=\"x-grid3-body-cell\" tabIndex=\"0\" hidefocus=\"on\"><div class=\"x-grid3-row-body\">{body}</div></td></tr>" : ""), "</tbody></table></div>");
    }
    if (!C.cell) {
        C.cell = new (Ext.Template)("<td class=\"x-grid3-col x-grid3-cell x-grid3-td-{id} {css}\" style=\"{style}\" tabIndex=\"0\" {cellAttr}>", "<div class=\"x-grid3-cell-inner x-grid3-col-{id}\" {attr}>{value}</div>", "</td>");
    }
    for (var A in C) {
        var B = C[A];
        if (B && typeof B.compile == "function" && !B.compiled) {
            B.disableFormats = true;
            B.compile();
        }
    }
    this.templates = C;
    this.tdClass = "x-grid3-cell";
    this.cellSelector = "td.x-grid3-cell";
    this.hdCls = "x-grid3-hd";
    this.rowSelector = "div.x-grid3-row";
    this.colRe = new RegExp("x-grid3-td-([^\\s]+)", "");
}


/**
 * Popup window object
 */
Core.Popup = new Object();
Core.Popup.obj = false;
Core.Popup.opened = false;
Core.Popup.show = function(processor, page, action, params, width, height, title) {
	if (Core.Popup.obj.el && !Core.Popup.obj.hidden) {
		Core.Popup.obj.el.mask('Loading...', 'x-mask-loading');
	}

	if (Core.Popup.obj) {
		if (Core.Popup.obj.hidden) {
			Core.Popup.createObj(width, height, title);
		}
	} else {
		Core.Popup.createObj(width, height, title);
	}

	Core.request(processor, page, action, params, function(r, s, o){
		if (s) {
			var r = Ext.decode(o.responseText);
			if (!r.errors) {
				Core.Popup.update(r.fields, r.title);
			} else {
				var errors = '';
				for (var i = 0; i < r.errors.length; i++) {
					errors += (i + 1 < r.errors.length) ? r.errors[i] + '<br />' : r.errors[i];
				}
				if (Core.Popup.obj.el) {
					Core.Popup.obj.el.unmask();
					Core.Popup.obj.hide();
				}
				Ext.MessageBox.alert(r.errors.length > 1 ? 'Errors' : 'Error', errors);
			}
		} else {
			Core.Popup.obj.el.unmask();
			Core.Popup.obj.hide();
			Core.msg(Core.translate.get('reqFailure', 'all'), Core.translate.get('reqAjaxFailure', 'all'));
		}
	}, true);

}

Core.Popup.getHtml = function(fields) {
	var html = '<table width="100%" cellspacing="0" border="0" cellpadding="3">';
	for(var i in fields) {
	    if(fields[i]) {
	        fields[i] = fields[i].replace("\n", '<br>');
	    } else {
	        fields[i] = '';
	    }
   		html += '<tr><td class="myPopupText" width="120" align="left"><b>' + i + ':</b></td><td class="myPopupText">' + fields[i] + '</td></tr>';
	}
	html += '</table>';
	return html;
}

Core.Popup.createObj = function(width, height, title) {//fields, title) {

	if (Core.Popup.obj) {
		Core.Popup.obj.destroy();
	}

	var wwidth=(window.innerWidth)?window.innerWidth:
	    ((document.all)?document.body.offsetWidth:null);

	Core.Popup.obj = new Ext.Window({
		title: title,
		width: width,
		minHeight: 200,
		autoHeight: true,
		modal: false,
		layout: 'fit',
		plain: true,
		autoEl: {id:'popupWindow', html: ''},
		'bodyStyle': 'padding:5px;',
		buttonAlign: 'center',
		html: '',
		x: wwidth - (width+50),
		y: 50
	});

	Core.Popup.obj.on('beforeclose',
		function() {
			Core.Popup.obj.hide();
			Core.Popup.opened = false;
		}
	);

	Core.Popup.obj.show();
	Core.Popup.opened = true;
	Core.Popup.obj.el.mask('Loading...', 'x-mask-loading');
}

Core.Popup.update = function(data, title) {

	Core.Popup.obj.setTitle(title);

	/*var form = Core.Popup.obj.items.itemAt(0).getForm();
	var keys = form.items.keys;
	for (var B = 0, A = keys.length; B < A; B++) {
		form.items.items[B].setValue(data[keys[B]]);
	}*/
	var html = Core.Popup.getHtml(data);
	Core.Popup.obj.body.update(html);

	Core.Popup.obj.el.unmask();
	if (Core.Popup.obj.hidden) {
		Core.Popup.obj.show();
	}
}

/**
 * JustText is a div form element.
 */
Ext.form.JustText = Ext.extend(Ext.form.TextField,{
	preventScrollbars: false,
	onRender: function(B,A){
		if(!this.el){
			this.defaultAutoCreate={
				tag: "div",
				style: "display: inline-block;",
				autocomplete: "off"
			}
		}
		Ext.form.JustText.superclass.onRender.call(this,B,A);
		this.el.addClass('x-form-justText-el');
	},
	setValue: function(A){
		this.el.dom.innerHTML = A;
	},
	validateValue: function(){
		return true;
	}
});

Ext.reg('justtext', Ext.form.JustText);

/**
 * Replace disabled by readonly in all fields
 */
Ext.Component.prototype.onDisable = function () {
    this.getActionEl().addClass(this.disabledClass);
    this.el.dom.setAttribute("readonly", "readonly");
}
Ext.Component.prototype.onEnable = function () {
    this.getActionEl().removeClass(this.disabledClass);
    this.el.dom.removeAttribute("readonly");
}

Ext.data.Connection.prototype.handleResponse = function (A) {
    this.transId = false;
    var B = A.argument.options;
    A.argument = B ? B.argument : null;
    this.fireEvent("requestcomplete", this, A, B);

	var loginBoxTest = /^<!-- Loginbox -->/;
	if (loginBoxTest.test(A.responseText)) {
		Core.mask.hide();
		Core.showLoginPopup();
		if (Core.AjaxAuth.isAuth) {
			Core.AjaxAuth.isAuth = false;
		} else {
        	Core.msg('Failure', 'Login incorrect');
		}
        return false;
	} else if (!Core.AjaxAuth.isAuth) {
		Core.mask.hide();
		Core.AjaxAuth.isAuth = true;
		Ext.Ajax.request(Core.AjaxAuth.E);
		Core.showLoginPopupObject.hide();
        Core.msg('Success', 'Successfully authorized');
		return false;
	}

    Ext.callback(B.success, B.scope, [A, B]);
    Ext.callback(B.callback, B.scope, [B, true, A]);
}

Core.showLoginPopupObject = false;
Core.showLoginPopup = function() {

    var form = new Ext.form.FormPanel({
		id: 'loginForm',
        baseCls: 'x-plain',
        url: '/',
        defaultType: 'textfield',
        labelWidth: 90,
        items: [
			{
				fieldLabel: 'Login',
				name: 'login',
				id: 'login',
				itemCls: 'left',
				anchor: '100%',
				allowBlank: false
			}
			,{
				fieldLabel: 'Security Code',
				name: 'securityCode',
				id: 'securityCode',
				itemCls: 'left',
				anchor: '100%',
				allowBlank: false,
				autoCreate: {
			        tag: 'input',
			        type: 'password',
			        autocomplete: 'off'
			    }
			}
			,{
				fieldLabel: 'Password',
				name: 'password',
				id: 'password',
				itemCls: 'left',
				anchor: '100%',
				allowBlank: false,
                autoCreate: {
                    tag: 'input',
                    type: 'password',
                    autocomplete: 'off'
                }
			}
		]
    });

    if (Core.showLoginPopupObject) {
        Core.showLoginPopupObject.destroy();
    }

    Core.showLoginPopupObject = new Ext.Window({
        title: 'Session Expired',
        width: 300,
        height: 166,
        minWidth: 300,
        minHeight: 166,
        modal: true,
        layout: 'fit',
        plain: true,
        bodyStyle:'padding:10px;',
        buttonAlign:'center',
        items: form,
        keys: [{
            key: [10, 13],
            fn: function() {
                if (Ext.getCmp('loginForm').getForm().isValid()) {
                    Core.mask.show(Core.translate.get('loadingMsg', 'all'));
                    Ext.getCmp('loginForm').getForm().submit();
                } else {
                    Core.msg(Core.translate.get('reqFailure', 'all'), Core.translate.get('reqFormInvalidated', 'all'));
                }
            }
        }],
        buttons: [{
            'text': 'Enter',
            'id': 'btnSubmitLogin',
			'type': 'submit',
            'handler': function(){
                if (Ext.getCmp('loginForm').getForm().isValid()) {
                    Core.mask.show(Core.translate.get('loadingMsg', 'all'));
                    Ext.getCmp('loginForm').getForm().submit();
                } else {
                    Core.msg(Core.translate.get('reqFailure', 'all'), Core.translate.get('reqFormInvalidated', 'all'));
                }
            }
        }]
    });
    form.getForm().reset();
    Core.showLoginPopupObject.on('beforeclose',
        function() {
            return false;
        }
    )
    Core.showLoginPopupObject.on('show',
        function() {
            Ext.getCmp('login').focus();
        }
    )
    Core.showLoginPopupObject.show();
}
