if (typeof(window.console) == 'undefined') {
    window.console = {
        log: function(){},
        debug: function(){},
        dir: function(){},
        firebug: null
    };
}

(function($) {

/** * при выставлении значения для атрибтов checked и selected это значение будет приводится к boolean
 * т.е. будет нормально работать .attr('checked', 'checked') и .attr('checked', 1) и даже .attr('checked', 'true') */
if (jQuery.attrHooks) {
    jQuery.attrHooks.checked = jQuery.attrHooks.selected = {
        set: function(elem, value, name) {
            elem[name] = !!value;
            return value;
        }
    };
}
    
jQuery.extend({
     
    jsHttpRequest: function(options)
    {
        var options = jQuery.extend({
            url:     '/',
            data:    {},
            onReady: function(){},
            cache:   true,
            target:  null,
            timeout: 30000
        }, options);
        
        if (!($.browser.msie && $.browser.version == '6.0') && options.target) {
            $(options.target).fadeTo('fast', 0.4);
        }
        
        Tools.showStatus(Tools.LOADER, null, options);
        var uid = Tools._statusUid;
        
        var escapeUrl = '';
        $.map(options.url.match(/[а-я]+|[^а-я]+/ig), function(str) {
            escapeUrl += str.match(/[а-я]+/ig) ? str.escapeCyr() : str;
        });
        
        JsHttpRequest.query(
            escapeUrl.replace(/\/\s*$/, ''),
            options.data,
            function(result, text) {
                
                if (options.target && !($.browser.msie && $.browser.version == '6.0')) {
                    $(options.target).fadeTo('fast', 1);
                }
                
                if (typeof(options.onReady) == 'function') {
                    options.onReady(result, text);
                }
                
                if (Tools._statusUid == uid) {
                    Tools.hideStatus(uid, options);
                }
            },
            !options.cache
        );
    },
    
    func: 
    {
        /**         * http://phpjs.org -> number_format         */
        numberFormat: function(number, decimals, dec_point, thousands_sep)
        {
            number = (number+'').replace(',', '').replace(' ', '');
            var n = !isFinite(+number) ? 0 : +number, 
                prec = !isFinite(+decimals) ? 0 : Math.abs(decimals),
                sep = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep,
                dec = (typeof dec_point === 'undefined') ? '.' : dec_point,
                s = '',
                toFixedFix = function (n, prec) {
                    var k = Math.pow(10, prec);
                    return '' + Math.round(n * k) / k;
                };
            // Fix for IE parseFloat(0.55).toFixed(0) = 0;
            s = (prec ? toFixedFix(n, prec) : '' + Math.round(n)).split('.');
            if (s[0].length > 3) {
                s[0] = s[0].replace(/\B(?=(?:\d{3})+(?!\d))/g, sep);
            }
            if ((s[1] || '').length < prec) {
                s[1] = s[1] || '';
                s[1] += new Array(prec - s[1].length + 1).join('0');
            }
            return s.join(dec);
        },
        
        // округление
        round: function(value, precision) 
        {
            var m = Math.pow(10, precision || 0);
            return Math.round((value || 0) * m) / m;
        }
    },
    
    alertZ: function(text, timeout)
    {
    	if (typeof(text) == 'object') {
            $.each(text, function(i, txt) {
                $.alertZ(txt, timeout);
            });
            return;
        }
        
        if (!$('#alertZ-container').length) {
            $('body').append('<div id="alertZ-container"></div>');
        }
        
        var _timeOutHide = function(msgId, timeout) {
            if (typeof($._alertZTimeOutHide) == 'undefined') $._alertZTimeOutHide = [];
            clearTimeout($._alertZTimeOutHide[msgId]);
            $._alertZTimeOutHide[msgId] = setTimeout(function() {
                $('#'+msgId+':not(.alertZ-hover)').fadeOut(1500, function() { $(this).remove(); });
            }, timeout || 10000);
        };
        
        var msgId = ('alertZ-msg-' + Math.random()).replace('.', ''); 
        $('<div class="alertZ-message" id="'+ msgId +'" style="display: none;">'+ text +'<a href="#" onclick="$(this).parent().remove(); return false;" class="alertZ-close"></a></div>')
            .prependTo('#alertZ-container')
            .fadeIn('slow')
            .mouseover(function() {
                $(this).stop().addClass('alertZ-hover').css('opacity', 0.9);
            })
            .mouseout(function() {
                $(this).removeClass('alertZ-hover');
                _timeOutHide(this.id, timeout || 10000);
            });
        
        _timeOutHide(msgId, timeout);
    },
    
    eventHandler: {
            
        _observers: {},
        _oneNotify: {},
    
        bind: function(eventName, callback)
        {
            if (typeof callback != 'function') return;
            if (!this._observers[eventName]) {
                this._observers[eventName] = [];
            }
            this._observers[eventName][callback] = callback;
            return this;
        },
        
        bindOne: function(eventName, callback)
        {
            if (typeof callback != 'function') return;
            if (!this._oneNotify[eventName]) {
                this._oneNotify[eventName] = [];
            }
            this._oneNotify[eventName][callback] = true;

            return this.bind(eventName, callback)
        },
        
        unbind: function(eventName, callback)
        {
            if (!this._observers[eventName]) return;
            
            if (callback) {
                this._observers[eventName][callback] = null;
            } else {
                this._observers[eventName] = null;
            }
        },
        
        notify: function(eventName, options) 
        {
            if (!this._observers[eventName]) return;
            for (key in this._observers[eventName]) {
                if (!this._observers[eventName][key]) continue;
                
                this._observers[eventName][key](options);
                
                if (this._oneNotify[eventName] && this._oneNotify[eventName][key]) {
                    this._oneNotify[eventName][key] = null;
                    this._observers[eventName][key] = null;
                }
            }
            return this;
        }
    }
});

jQuery.fn.extend({
    
    serializeHash: function()
    {
        var _hash  = {};
        var _array = this.serializeArray();
        $.each(_array.length ? _array : $(':input', this).serializeArray(), function(i, el) {
            var _isArray = el.name.match(/\[\]$/, '')
            
            if (_isArray)
                el.name = el.name.replace(/\[\]$/, '');
            
            if (typeof(_hash[el.name]) == 'undefined')
                return _hash[el.name] = _isArray ? [el.value] : el.value; 
            
            if (!_hash[el.name].push)
                _hash[el.name] = [_hash[el.name]];
            
            _hash[el.name].push(el.value);
        });
        return _hash;
    },
    
    red: function()
    {
        return $(this).css('border', 'red 1px solid');
    },
    
    removeSlow: function()
    {
        return $(this).animate({ opacity: 'hide', height: 'hide' }, 'slow', null, function(){ $(this).remove(); });
    },
    
    searchComplete: function(items, timeout)
    {
    	if ($._searchCompleteTimeout) clearTimeout($._searchCompleteTimeout);
    	
    	var _self = $(this);
    	
    	$._searchCompleteTimeout = setTimeout(function() 
    	{
    		var _text  = _self.val().toLowerCase();
	        var _items = _self.data('_completeItems');
	        if (!_items) {
	            _items = $(items);
	            _self.data('_completeItems', _items);
	        }
	        
	        if (!_text) return _items.show();
	
	        var _reverseText = _text.reverseKeyboardLayout();
        	var _elText;
            _items.each(function(i, el) {
            	el = $(el), _elText = el.text().toLowerCase();
                if (_elText.indexOf(_text) >= 0 || _elText.indexOf(_reverseText) >= 0) {
                	el.show();
                } else {
                	el.hide();
                }
            });
	        
    	}, timeout || 200);
        
        return this;
    },
    
    /*
     * Проверка на валидность форм
     */
    validate: function(displayErrors)
    {
        displayErrors = typeof(displayErrors) == 'undefined' || displayErrors;
        var isFocused = false;
        var result    = true;
        
        var _check = function (element, varType) {
            var success        = true;
            var notRequired    = element.attr('notrequired');
            var value          = element.val();
            var isDefaultValue = element.hasClass('input-default-value');
            
            if (notRequired && (!value || isDefaultValue)) {
                return true;
            }    
            
            switch(varType) {
                case 'email':
                    success = /^[\w.-]+@([\w-]+\.)+\w{2,6}$/i.test(value);
                    break;
                case 'string':
                case 'select':
                    success = (value != '' && value != null && element.attr('not-valid') != 'true');
                    break;
                case 'numeric':
                case 'integer':
                    success = /^\d+$/i.test(value);
                    break;
                case 'check':
                    success = element.is(':checked');
                    break;
                case 'custom':
                    success = eval(element.attr('pattern') || element.attr('regex')).test(value);
                    break;
                case 'confirm':
                    success = (value == $('#' + element.attr('for')).val());
                    break;
                case 'phone':
                    success = /^[\d- ]+$/i.test(value);
                    break;
                case 'mobilephone':
                    success = /^7\d{10}$/i.test(value);
                    break;
                case 'date':
                    success = value && /^\s*\d{1,2}\.\d{1,2}\.\d{2,4}\s*$/.test(value);
                    break;
            }
            
            if (!notRequired && isDefaultValue) {
                success = false;
            }
            
            if (!success && displayErrors) { 
                element.addClass('f-not-valide');
                if (!isFocused) element.trigger('focus'), isFocused = true;
            }
            return success;
        }
        
        $(this).removeClass('f-not-valide');
        $(this).each(function() {
            var _varType = $(this).attr('vartype');
            if (_varType && $(this)[0].offsetHeight != 0) {
                if (!_check($(this), _varType)) result = false;
            }
        });
        return result;
    }
});

$F = function(elm) {
    return $(elm).val();
}

clone = function(object) {
    return $.extend({}, object);
}

differenceOfDays = function(start, end)
{
    start = start.split('.');
    end   = end.split('.');
    start = new Date(start[2], parseInt(start[1], 10) - 1, start[0]);
    end   = new Date(end[2], parseInt(end[1], 10) - 1, end[0]);
    return Math.ceil((end - start) / (1000 * 86400));
}

$.extend(String.prototype, {
    
    /**
     * Транслит руских букв в латинские
     * @param string text
     * @return string
     */
    translit: function () {
        var text   = this;
        var ru_str = 'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюя';
        var en_str = [
        'A','B','V','G','D','E','Jo','Zh','Z','I','Y','K','L','M','N','O','P','R','S','T','U','F',
        'Kh','Ts','Ch','Sh','Shch','','Y','','E','Yu','Ya',
        'a','b','v','g','d','e','jo','zh','z','i','y','k','l','m','n','o','p','r','s','t','u','f',
        'kh','ts','ch','sh','shch','\'','y','','e','yu','ya'];

        for(var i = 0, out = [], count = text.length; i < count; i++) {
            var s = text.charAt(i), n = ru_str.indexOf(s);
            out[out.length] = (n >= 0) ? en_str[n] : s;
        }
        return out.join('');
    },
    
    // подсавляет к числу существительное в соответствующей форме. 
    // например: 3.inciting('турист', 'туриста', 'туристов') -> 3 туриста
    inciting: function(form1, form2, form5, excludeNumber)
    {
        var num    = Math.abs(parseInt(this));
        var n      = num % 100, n1 = num % 10;
        var prefix = (excludeNumber ? '' : (this + ' '));
        form5      = form5 || form2;
        
        if (n > 10 && n < 20) return prefix + form5;
        if (n1 > 1 && n1 < 5) return prefix + form2;
        if (n1 == 1) return prefix + form1;
        return prefix + form5;
    },
    
    // делает первый символ с большой буквы, остальные с маленькой. иванов -> Иванов
    capitalize: function()
    {
        return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
    },
    
    // заменяет все символы с 160 по 255-й на соответствующий entitles код. например &#231;
    unicodeToEntitles: function()
    {
        for (var i = 0, output = this, cnt = this.length; i < cnt; i++) {
            if (this.charCodeAt(i) < 160 || this.charCodeAt(i) > 255) continue;
            output = output.replace(this[i], '&#' + this.charCodeAt(i) + ';');
        }
        return output + '';
    },
    
    // переопределение escape, т.к. обычная функция вместо русских букв выдает фигню
    escapeCyr: function()
    {
        var trans = [], ret = [];
        for (var i = 0x410; i <= 0x44F; i++)
            trans[i] = i - 0x350; // А-Яа-я
        trans[0x401] = 0xA8;    // Ё
        trans[0x451] = 0xB8;    // ё
        
        // Составляем массив кодов символов, попутно переводим кириллицу
        for (var i = 0, count = this.length; i < count; i++) {
            var n = this.charCodeAt(i);
            if (typeof trans[n] != 'undefined')
                n = trans[n];
            if (n <= 0xFF)
                ret.push(n);
        }
        return escape(String.fromCharCode.apply(null, ret));
    },
    
    // переопределение unescape, т.к. обычная функция вместо русских букв выдает фигню
    unescapeCyr: function()
    {
        var trans = [], ret = [];
        for (var i = 0x410; i <= 0x44F; i++)
            trans[i] = i - 0x350; // А-Яа-я
        trans[0x401] = 0xA8;    // Ё
        trans[0x451] = 0xB8;    // ё
        
        // Составляем массив кодов символов, попутно переводим кириллицу
        for (var i = 0, count = this.length; i < count; i++) {
            var n = this.charCodeAt(i);
            if (typeof trans[n] != 'undefined')
                n = trans[n];
            if (n <= 0xFF)
                ret.push(n);
        }
        return escape(String.fromCharCode.apply(null, ret));
    },
    
    nl2br: function()
    {
    	var output = this;
    	output = output.replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1'+ '<br/>' +'$2');
    	return output + '';
    },

    /**
     * проверка того, что строка состоит только из латинских букв.
     * @return {bool}
     */
    isLat: function()
    {
    	var str = this;
    	str = str.replace(/[a-z]/gi,'');
    	if (str == '') {
    		return true;
    	} else {
    		return false
    	}
    },

    tail: function(len)
    {
    	var str = this;
    	return str.substring(str.length - len);
    },

    reverseKeyboardLayout: function()
    {
        var _ru = 'йцукенгшщзхъфывапролджэячсмитьбюбюэжхъ';
        var _en = "qwertyuiop[]asdfghjkl;'zxcvbnm,.<>\":{}";
        _ru = _ru + _ru.toUpperCase();
        _en = _en + _en.toUpperCase();
        
        var _rusChars = this.match(/[а-я]/ig);
        var _i        = 0;
        var _str      = this;
        
        if (_rusChars) {
            for (_c in _rusChars) {
                _i = _ru.indexOf(_rusChars[_c]); 
                _str = _str.replace(_rusChars[_c], _en[_i]);
            }
            return _str;
        } else {
            for (_c in this) {
                _i = _en.indexOf(this[_c]);
                _str = _str.replace(this[_c], _ru[_i]);
            }
            return _str;
        }
    }
    
});

Number.prototype.inciting = String.prototype.inciting;
window.escapeModify = function(str) { return str.escapeCyr(); }

})(jQuery);


/**
 * Плагин для работы с JSON, содержит 2 функции
 * 
 * string = $.toJSON(object);
 * object = $.parseJSON(string, [safeMode]);
 */
(function($){var m={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},s={'array':function(x){var a=['['],b,f,i,l=x.length,v;for(i=0;i<l;i+=1){v=x[i];f=s[typeof v];if(f){v=f(v);if(typeof v=='string'){if(b){a[a.length]=','}a[a.length]=v;b=true}}}a[a.length]=']';return a.join('')},'boolean':function(x){return String(x)},'null':function(x){return"null"},'number':function(x){return isFinite(x)?String(x):'null'},'object':function(x){if(x){if(x instanceof Array){return s.array(x)}var a=['{'],b,f,i,v;for(i in x){v=x[i];f=s[typeof v];if(f){v=f(v);if(typeof v=='string'){if(b){a[a.length]=','}a.push(s.string(i),':',v);b=true}}}a[a.length]='}';return a.join('')}return'null'},'string':function(x){if(/["\\\x00-\x1f]/.test(x)){x=x.replace(/([\x00-\x1f\\"])/g,function(a,b){var c=m[b];if(c){return c}c=b.charCodeAt();return'\\u00'+Math.floor(c/16).toString(16)+(c%16).toString(16)})}return'"'+x+'"'}};$.toJSON=function(v){var f=isNaN(v)?s[typeof v]:s['number'];if(f)return f(v)};$.parseJSON=function(v,safe){if(safe===undefined)safe=$.parseJSON.safe;if(safe&&!/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.test(v))return undefined;return eval('('+v+')')};$.parseJSON.safe=false})(jQuery);

