// START: constants declaration
var jc_DBG = false;          // global debug flag. Turn off in production version
var jc_OK = 1;              // jMsgBox enumerated button OK.
var jc_YESNO = 2;              // jMsgBox enumerated buttons Yes/No.
var jc_YES = 3;              // jMsgBox enumerated result Yes
var jc_NO = 4;              // jMsgBox enumerated result No
var jc_CANCEL = 5;              // jMsgBox enumerated button Cancel
var jc_OKCANCEL = 6;        // jMsgBox enumerated buttons  OK / Cancel
var jc_SAVECANCEL = 7;        // jMsgBox enumerated buttons  Save / Cancel
var jc_SAVE = 8;        // jMsgBox enumerated result Save
//   END: constants declaration

/* инфо
 *
 * префиксы:
 * jp - обработчики для прототипов (прототип задается сразу после него)
 * jz - инлайн-функции
 * jc - константы и значения, вычисляемые при загрузке фреймворка
 */


var d = document;
var jajax_connections = new Array();
var jajax_conninfo = new Array();
var jajax_callbacks = new Array();
var jajax_callfuncs = new Array();
var jajax_pool = 0;


// Опознаёт браузеры и возвращает объект с ключами browser: version,
// где browser - имя браузера, version - версия браузера или 0
var jAgent = (function(){
    var vers, browser = {
        msie: 0,
        opera: 0,
        firefox: 0,
        konquerror: 0,
        chrome: 0,
        safari: 0
    },
    str = Object.prototype.toString,
    ua = (navigator.userAgent || "").toLowerCase(),
    info = /(opera|msie|firefox|chrome|safari|konqueror)(?:.*version)?[\s\/:]([\w\d\.]+)?/i.exec(ua);
    if(info && info.length===3){
        var agent = info[1], version = parseFloat(info[2]);
        if(agent==="safari"){
            vers = /version\/([\d\.]+)/.exec(ua);
            version = vers && vers[1] ? parseFloat(vers[1]) : version <= 419.3 ? 2 : 0;
        }else if(agent==="msie"){
            vers = document.documentMode;
            version = vers && vers != 5 && Math.floor(version) != vers ? vers : version;
        }else if((agent!=="opera" || !version) && str.call(window.opera)==="[object Opera]"){
            agent = "opera";
            version = window.opera.version();
        }
        browser[agent] = version;
    }
    return browser;
})();

// getElementsByClassName for IE6-8:
if(d.getElementsByClassName===undefined)
    d.getElementsByClassName = function(className) {
        var retnode = [];
        var myclass = new RegExp('\\b'+className+'\\b');
        var elem = this.getElementsByTagName('*');
        for(var i = 0; i < elem.length; i++) {
            var classes = elem[i].className;
            if (myclass.test(classes)) retnode.push(elem[i]);
        }
        return retnode;
    }

function elm(id) {
    var _elm = typeof id==="string" ? document.getElementById(id) : (id && id.nodeType ? id : null);
    //var _hover_elm_id = null;
    if(_elm===null) return null;
    _elm.fadeIn = function(delay,callback,out) {
        var _this = this;
        if(_this.jt_fading===true) return;
        _this.jt_fading = true;
        for (i = 1; i <= 100; i++) {
          (function(percent) {
                setTimeout(function() {
                      if (out==true) percent=100-percent;
                      _this.style.opacity = percent/100;
                      _this.style.MozOpacity = percent/100;
                      _this.style.KhtmlOpacity = percent/100;
                      _this.style.zoom = 1; // for ie, set haslayout
                      _this.style.display="block";
                      if(percent==100&&out!=true) _this.jt_fading = false;
                      else if (percent==0&&out==true) _this.jt_fading = false;
                       if (percent==100&&callback!=undefined) {callback.call(_this);}
                       else if (out==true&&callback!=undefined&&percent==0) {callback.call(_this);}
                      _this.style.filter = "alpha(opacity=" + percent + ");";},percent*delay/100);
            })(i);
        }
    };

    _elm.fadeOut = function(delay,callback) {
        _elm.fadeIn(delay,callback,true);
    };
    _elm.setOpacity = function(percent) {
        this.style.opacity = percent/100;
        this.style.MozOpacity = percent/100;
        this.style.KhtmlOpacity = percent/100;
        this.style.zoom = 1; // for ie, set haslayout
        this.style.filter = "alpha(opacity=" + percent + ");";
    }
    _elm.getWidth = function(bReturnInteger) {
        var bInteger = (bReturnInteger) ? bReturnInteger : true;
        var retval = ':(';
        if(window.getComputedStyle) retval = d.defaultView.getComputedStyle(this,null).getPropertyValue('width');
        else if(this.currentStyle) retval = this.currentStyle['width'];
        if(retval=='auto') retval='100%';
        if(retval.indexOf('%')!==-1) retval = parseInt(this.offsetWidth*(retval.split('%')[0])/100) + 'px';
        var intretval = parseInt(retval)
        if(bInteger) retval = intretval;
        if(jc_DBG && intretval==0) dbg('DEBUG: Couldnt retrieve width at elm.getWidth().');
        return(retval);
    }
    _elm.getHeight = function(bReturnInteger) {
        var bInteger = (bReturnInteger) ? bReturnInteger : true;
        var retval = ':(';
        if(window.getComputedStyle) retval = d.defaultView.getComputedStyle(this,null).getPropertyValue('height');
        else if(this.currentStyle) retval = this.currentStyle['height'];
        if(retval=='auto') retval='100%';
        if(retval.indexOf('%')!==-1) retval = parseInt(this.offsetHeight*(retval.split('%')[0])/100) + 'px';
        var intretval = parseInt(retval)
        if(bInteger) retval = intretval;
        if(jc_DBG && intretval==0) dbg('DEBUG: Couldnt retrieve height at elm.getHeight().');
        return(retval);
    }
    _elm.hide = function() {this.style.display = 'none';}
    _elm.show = function() {this.style.display = 'block';}
    _elm.flash = function() {
        if(this.style.display=='none') this.style.display='block';
        else if (this.style.display=='block') this.style.display='none';
        else this.style.display='block';
    }
    _elm.getElementsByClassName = function(className) {
        var retnode = [];
        var myclass = new RegExp('\\b'+className+'\\b');
        var elem = this.getElementsByTagName('*');
        for(var i = 0; i < elem.length; i++) {
            var classes = elem[i].className;
            if (myclass.test(classes)) retnode.push(elem[i]);
        }
        return retnode;
    }

    // привязываем ховер-эффект к элементу
    _elm.hover = function(map_elm_id) {
        this.onmouseover = function() {elm(map_elm_id).show();}
        this.onmouseout = function() {elm(map_elm_id).hide();}
    }
    _elm.enableHover = function(inHoverClass) {
        var hoverClass = (inHoverClass) ? inHoverClass : 'hover';
        this.onmouseover = function() {
            this.className = this.className.replace(hoverClass, '').trim() + ' ' + hoverClass;
        }
        this.onmouseout = function() {
            this.className = this.className.replace(hoverClass, '').trim();
        }
    }
    
    _elm.makeDraggable = function() {
        var dragObject;
        var mouseOffset;
        function getWhich(e){
            return !e.which && e.button ?  (e.button & 1 ? 1 : ( e.button & 2 ? 3 : ( e.button & 4 ? 2 : 0))) : e.which;
        }
        function getPosition(e){
            var left = 0, top  = 0;
            while (e.offsetParent){
                left += e.offsetLeft;
                top += e.offsetTop;
                e = e.offsetParent;
            }
            left += e.offsetLeft;
            top  += e.offsetTop;
            return {x:left, y:top}
        }

        function getMouseOffset(target, e) {
            var docPos = getPosition(target)
            return {x:jzMouseX(e) - docPos.x, y:jzMouseY(e) - docPos.y}
        }

        function mouseUp(){
            document.body.onselectstart = document.ondragstart = document.onmouseup = document.onmousemove = null;
        }

        function mouseMove(e){
            e = e || window.event;
            dragObject.style.position = "absolute";
            dragObject.style.top = jzMouseY(e) - mouseOffset.y + "px";
            dragObject.style.left = jzMouseX(e) - mouseOffset.x + "px";
            return false;
        }
    
        function mouseDown(e) {
            e = e || window.event;
            if (getWhich(e)!=1) return;
            mouseOffset = getMouseOffset(this, e);
            document.onmousemove = mouseMove;
            document.onmouseup = mouseUp;
            document.body.onselectstart = document.ondragstart = function() {return false}
            return false;
        }
        return function(target){
            dragObject = target || this;
            this.onmousedown = mouseDown;
        }
    }();
    
    return(_elm);
}
function dbg(text) {window.alert(text);}
function dbglog(text) {if(elm('dbglog')!=undefined) elm('dbglog').value += '\n'+text;}
function jzTestKeyCode(e, testCode) {
    var evt = (e) ? e : (window.event) ? window.event : null;
    //dbglog('jzTestKeyCode('+e+','+testCode+'...');
    var keyCode = evt.keyCode ? evt.keyCode :
        evt.charCode ? evt.charCode :
        evt.which ? evt.which : void 0;
    if(testCode===0) return(keyCode);
    return((keyCode===testCode)?true:false);
}

function jzAddCSSRule(selector, rule) {
    var mysheet=d.styleSheets[0];
    if (mysheet.deleteRule){ //if Firefox
        mysheet.insertRule(selector+"{"+rule+"}", 0);
    }else if (mysheet.removeRule) { //else if IE
        mysheet.addRule(selector, rule);
    }
}

function jzMouseX(evt) {if (!evt) evt = window.event;if (evt.pageX) return evt.pageX;else if (evt.clientX)return evt.clientX + (document.documentElement.scrollLeft ?  document.documentElement.scrollLeft : document.body.scrollLeft); else return 0;}
function jzMouseY(evt) {if (!evt) evt = window.event;if (evt.pageY) return evt.pageY;else if (evt.clientY)return evt.clientY + (document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop); else return 0;}

function jTipper(holderClass, tipClass, toppad, leftpad) {
    var holderClass = (holderClass)?holderClass:'jtipper';
    var tipClass = (tipClass)?tipClass:'jtip';
    var toppad = (toppad)?toppad:0;
    var leftpad = (leftpad)?leftpad:0;
    if(jc_BROWSER==='IE') toppad=0;
    jzAddCSSRule('.'+holderClass+' .'+tipClass, 'position:absolute; left:0; top:0; white-space:nowrap; display:none;');
    var mytips = d.getElementsByClassName(holderClass);
    for(var i=0;i<mytips.length;i++) {
        if(! mytips[i].id) mytips[i].id = 'jtipper_'+i;
        var tip = elm(mytips[i].id).getElementsByClassName(tipClass)[0];
        if(! tip.id) tip.id = 'jtip_'+i;
        mytips[i].onmousemove = function(e) {
            tip = elm(this.id).getElementsByClassName(tipClass)[0];
            tip.style.display = 'block';
            tip.style.top = jzMouseY(e) - elm(tip.id).getHeight() - toppad + 'px';
            tip.style.left = jzMouseX(e) + leftpad + 'px';
        }
        mytips[i].onmouseout = function() {
            elm(this.id).getElementsByClassName(tipClass)[0].style.display='none';
        }
    }
}

function jHinter(holderClass, hintClass, targetId, defaultHint) {
    var holderClass = (holderClass)?holderClass:'jhinter';
    var hintClass = (hintClass)?hintClass:'jhint';
    var defaultHint = (defaultHint)?defaultHint:'';

    elm(targetId).innerHTML=defaultHint;
    
    jzAddCSSRule('.'+holderClass+' .'+hintClass, 'display:none;');
    var myhints = d.getElementsByClassName(holderClass);
    for(var i=0;i<myhints.length;i++) {
        if(! myhints[i].id) myhints[i].id = 'jhinter_'+i;
        var tip = elm(myhints[i].id).getElementsByClassName(hintClass)[0];
        if(! tip.id) tip.id = 'jhint_'+i;
        myhints[i].onmousemove = function(e) {
            elm(targetId).innerHTML = elm(this.id).getElementsByClassName(hintClass)[0].innerHTML;
        }
        myhints[i].onmouseout = function() {
            elm(targetId).innerHTML=defaultHint;
        }
    }
}

function jzEnlargeTable(tableId) {
    var curclass = elm(tableId).className;
    if(curclass.search('jfrmwrk_table_enlarged')==-1) curclass += ((curclass.length>0)?' ':'')+'jfrmwrk_table_enlarged';
    elm(tableId).className = curclass;
}
function jzReduceTable(tableId) {
    var curclass = elm(tableId).className;
    curclass = jzTrim(curclass.replace('jfrmwrk_table_enlarged', ''));
    elm(tableId).className = curclass;
}
function jzAttachBlockToClass(targetClassName, attachmentHTML, outerContainerClassName) {
    var blocks = d.getElementsByClassName(targetClassName);
    for(var i=0;i<blocks.length;i++) {
        blocks[i].outerHTML += attachmentHTML;
        if(outerContainerClassName.length>0) blocks[i].outerHTML = '<div class="'+outerContainerClassName+'">' + blocks[i].outerHTML + '</div>';
    }
}
function jzTrim(string) {return string.replace(/^\s*/, "").replace(/\s*$/, "");}
function jpTrim() {
    var str = this.toString();
    return jzTrim(str);
}
String.prototype.trim = jpTrim;

function jzAjaxCreate(){
    var req;
    if(navigator.appName == "Microsoft Internet Explorer"){
        req = new ActiveXObject("Microsoft.XMLHTTP");
    }else{
        req = new XMLHttpRequest();
    }
    return req;
}
function jzAjaxRefresh(conn_id) {
var state,data;
    state = jajax_connections[conn_id].readyState;
    if(state == 4) {
        data = jajax_connections[conn_id].responseText;
        if(jajax_callbacks[conn_id] !== undefined) {elm(jajax_callbacks[conn_id]).innerHTML = data;}
        if(jajax_callfuncs[conn_id] !== undefined) {jajax_callfuncs[conn_id]();}
    }
}
var jAjaxes = new Array();
function jAjax() {
    //var method = 'post';
    //var query_data = '';
    //var ajax = jzAjaxCreate();
    jAjaxes[jajax_pool] = this;
    var conn_id;
    var callbackFunc = null;
    this.setCallbackFunc = function(func) {
        //jajax_callfuncs[conn_id] = func;
        this.callbackFunc = func;
    }
    this.Open = function(method, url, query, callbackid, charset) {
        this.conn_id = jajax_pool;
        var myid = this.conn_id;
        charset = (charset)?charset:'utf-8';
        jajax_conninfo[this.conn_id] = method + ':' + url + ':' + query;
        jajax_callbacks[this.conn_id] = callbackid;
        jajax_connections[this.conn_id] = jzAjaxCreate();
        jajax_connections[this.conn_id].open(method, url, true);
        jajax_connections[this.conn_id].onreadystatechange = function() {jzAjaxRefresh(myid);}
        jajax_connections[this.conn_id].setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset="+charset);
        jajax_connections[this.conn_id].send(query);
        if(this.callbackFunc !== null) jajax_callfuncs[this.conn_id] = this.callbackFunc;
        //alert(this.conn_id);
        jajax_pool++;
    }
    this.Close = function() {
        jajax_conninfo[this.conn_id] = null;
        jajax_connections[this.conn_id] = null;
        jajax_callbacks[this.conn_id] = null;
        jajax_callfuncs[this.conn_id] = null;
    }
    this.getConnId = function() {return this.conn_id;}
    this.alertConnId = function() {window.alert(this.conn_id);}
    this.enumConnections = function() {
        var enumarr = new Array();
        var myenum = '';
        for(conn in jajax_conninfo)
            if(jajax_conninfo[conn]!=null) enumarr.push(''+conn+': '+jajax_conninfo[conn]);
        myenum = enumarr.join('\n');
        window.alert(myenum);
    }
}

var jcomboboxes = new Array();
var jcomboboxes_pool = 0;
function jSetComboVars(comboid, variant, hidden_id) {
    if(comboid==0) return;
    elm('jcomboinput_'+comboid).value = variant;
    elm('jcombohidden_'+comboid).value = hidden_id;
    //elm('var_id').value = var_id;
    jcomboboxes[comboid].curvar=0;
    //jSearch(true);
    //elm('jcombovariants_'+comboid).innerHTML = '';
    jcomboboxes[comboid].ComboHideVars(comboid);
}

function jCombobox() {
    var curvar=0;
    var hovered = false;
    var myId;
    var ajaxParserUrl;

    jcomboboxes_pool++;
    myId = jcomboboxes_pool;
    
    this.ComboHideVars = function(comboid) {
        elm('jcombovariants_'+comboid).innerHTML = '';
    }
    this.jSeekVariant = function(oldvar, newvar) {
        //if(newvar>0 && elm('jcombovar_'+myId+'_'+newvar)==undefined) return;
        if(newvar>0 && elm('jcombovar_'+myId+'_'+newvar)==undefined) newvar=0;
        if(oldvar>0) elm('jcombovar_'+myId+'_'+oldvar).className='';
        if(newvar>0) elm('jcombovar_'+myId+'_'+newvar).className='jcomboselected';
        curvar=newvar;
        dbglog(''+myId+': '+oldvar+' to '+newvar+' --> '+curvar);
    }
    this.jSearch = function(e) {
        //if(jTestKeyCode(e,8)) { jSeekVariant(curvar, 0); }
        if(jzTestKeyCode(e,40)) {jcomboboxes[myId].jSeekVariant(curvar, curvar+1);return;} //проверка на достижение конца списка?
        if(jzTestKeyCode(e,38) && curvar>0) {jcomboboxes[myId].jSeekVariant(curvar, curvar-1);return;}
        if(jzTestKeyCode(e,13) && curvar>0) {elm('jcombovar_'+myId+'_'+curvar).onclick();elm('jcombovariants_'+myId).innerHTML = '';return;}
        curvar=0;
        elm('jcombohidden_'+myId).value=0;
        jcomboboxes[myId].ComboHideVars(myId);
        if(elm('jcomboinput_'+myId).value=='') return;
        var ajax = new jAjax();
        ajax.Open('post', ajaxParserUrl, 'search='+elm('jcomboinput_'+myId).value+'&comboid='+myId, 'jcombovariants_'+myId);
    }

    this.Create = function(targetElmId, ajaxUrl, submitParamName, submitParserUrl, intComboWidth, addClassName) {
        jcomboboxes[myId] = this;
        var comboWidth = (intComboWidth) ? intComboWidth : 500;
        ajaxParserUrl = ajaxUrl;

        var combobox = d.createElement('div'); // внешний контейнер для всего комбобокса
        combobox.setAttribute('id', 'jcombobox_'+myId);
        combobox.className = 'jcombobox';
        combobox.style.position = 'relative';
        //combobox.innerHTML = 'New combobox is growing here...<br />';
        if(targetElmId) elm(targetElmId).appendChild(combobox); else d.body.appendChild(combobox);

        var comboinput = d.createElement('input'); // строка для ввода данных пользователя
        comboinput.setAttribute('id', 'jcomboinput_'+myId);
        comboinput.setAttribute('type','text');
        comboinput.className = 'jcomboinput';
        comboinput.style.width = ''+comboWidth+'px';
        comboinput.onkeyup = this.jSearch;
        comboinput.onfocus = this.jSearch;
        comboinput.onblur = function() {if(!hovered) elm('jcombovariants_'+myId).innerHTML = '';}
        elm('jcombobox_'+myId).appendChild(comboinput);

        var combovariants = d.createElement('div'); // див для выпадающих вариантов
        combovariants.setAttribute('id', 'jcombovariants_'+myId);
        combovariants.className = 'jcombovariants';
        combovariants.style.position = 'absolute';
        combovariants.style.top = ''+parseInt(elm('jcomboinput_'+myId).offsetTop + elm('jcomboinput_'+myId).offsetHeight - 1) + 'px';
        combovariants.style.width = ''+parseInt(elm('jcomboinput_'+myId).offsetWidth) + 'px';
        combovariants.onmouseover = function() {hovered = true;}
        combovariants.onmouseout = function() {hovered = false;}
        elm('jcombobox_'+myId).appendChild(combovariants);
        // фикс ширины для нормальных браузеров
        if(window.getComputedStyle) elm('jcombovariants_'+myId).style.width = parseInt(elm('jcomboinput_'+myId).offsetWidth) - 2*parseInt(d.defaultView.getComputedStyle(elm('jcombovariants_'+myId),null).getPropertyValue('border-left-width')) + 'px';
        // фикс ширины для ИЕ
        else if(d.getElementById('jcombovariants_'+myId).currentStyle)
            elm('jcombovariants_'+myId).style.width = parseInt(elm('jcomboinput_'+myId).offsetWidth) - 2*parseInt(d.getElementById('jcombovariants_'+myId).currentStyle['borderLeftWidth']);
            //document.getElementById('searchme').currentStyle['borderLeftWidth']

        var combohidden = d.createElement('input'); // скрытое поле для передачи значения дальше
        combohidden.setAttribute('id', 'jcombohidden_'+myId);
        combohidden.setAttribute('type', 'hidden');
        elm('jcombobox_'+myId).appendChild(combohidden);

        var combobutton = d.createElement('input'); // кнопка для окончательного сабмита
        combobutton.setAttribute('id', 'jcombobutton_'+myId);
        combobutton.setAttribute('type','button');
        combobutton.setAttribute('value','готово');
        combobutton.onclick = function() {
            window.alert('В конце концов ты выбрал: \n' + elm('jcomboinput_'+myId).value + ' (id='+elm('jcombohidden_'+myId).value+')\n\nДальше должна быть отправка этого значения на сервер зачем-нибудь.');
        }
        elm('jcombobox_'+myId).appendChild(combobutton);


    }
}

var jsliders = new Array();
var jsliders_pool = 0;

function jSlider() {
    var myId;
    var mySlidesHTML = new Array();
    var mySlidesLabels = new Array();
    var mySlidesCount = 0;
    var bShowSlideMeter = false;
    var myCaption = '';
    var mySeekPointer;
    var myCallbackNext = null;
    var myAdditionalNav = '';

    jsliders_pool++;
    myId = jsliders_pool;
    jsliders[myId] = this;

    if(myId===1) {
        // наколдуем css-правило jslider_prehidden
        jzAddCSSRule('.jslider_prehidden','display:none');
        
    }

    this.SlideMeter = function(bAdd) {bShowSlideMeter = bAdd;}
    this.SetCaption = function(caption) {myCaption = caption;}
    this.AddSlide = function(slideLabel, slideHTML) {
        mySlidesLabels[mySlidesCount] = slideLabel;
        mySlidesHTML[mySlidesCount] = slideHTML;
        mySlidesCount++;
    }
    this.AddSlideFromDiv = function(slideLabel, sourceDivElm) {
        if(parseInt(sourceDivElm)===sourceDivElm) sourceDivElm = 'jslidersource'+sourceDivElm;
        mySlidesLabels[mySlidesCount] = slideLabel;
        mySlidesHTML[mySlidesCount] = elm(sourceDivElm).innerHTML;
        mySlidesCount++;
        // MAYBE: может быть делать sourceDivElm - невидимым?
    }
    this.Render = function(targetElm) {
        var slidesHTML = '';
        mySeekPointer = 0;
        for(i=0;i<mySlidesCount;i++) {
            slidesHTML += '<div id="jslider_'+myId+'_slide_'+i+'" class="jslider_slide" style="display:none;">';
                slidesHTML += '<h2 class="jslider_slide_label">'+mySlidesLabels[i]+'</h2>';
                slidesHTML += '<div class="jslider_slide_content">'+mySlidesHTML[i]+'</div>';
                // slidesHTML += '<hr class="jslider_hr" />';
            slidesHTML += '</div>';
        }
        if(i===0) slidesHTML = 'You have to add slides first...';
        var renderHTML = '';
        renderHTML += '<div class="jslider" id="jslider_'+myId+'">';
        renderHTML += '<h1 class="jslider_caption">'+myCaption+'</h1>';
        renderHTML += slidesHTML;
        renderHTML += '<div class="jslider_navigation">';
        renderHTML += '<span class="jslider_navleft"><a id="jslider_'+myId+'_navleft">Назад</a></span>';
        renderHTML += ' <span id="jslider_'+myId+'_meter" class="jslider_meter">&nbsp;</span> ';
        if(myAdditionalNav.length>0) renderHTML += myAdditionalNav;
        renderHTML += '<span class="jslider_navright"><a id="jslider_'+myId+'_navright">Вперед</a></span>';
        renderHTML += '</div>';

        elm(targetElm).innerHTML = renderHTML;
        elm(targetElm).style.display = 'block';

        jsliders[myId].RefreshNavigation();
    }
    this.MoveLeft = function() {
        var curelm = elm('jslider_'+myId+'_slide_'+mySeekPointer);
        curelm.fadeOut(500, function() {
            curelm.style.display = 'none';
            mySeekPointer--;
            jsliders[myId].RefreshNavigation();
            //window.alert('pointer='+mySeekPointer+' myId='+myId);
        });
    }
    this.MoveRight = function() {
        var curelm = elm('jslider_'+myId+'_slide_'+mySeekPointer);
        var bGood = true;
        if(myCallbackNext !== null) bGood = myCallbackNext();
        if(!bGood) return;
        curelm.fadeOut(500, function() {
            curelm.style.display = 'none';
            mySeekPointer++;
            jsliders[myId].RefreshNavigation();
            
            //window.alert('pointer='+mySeekPointer+' myId='+myId);
        });
    }
    this.MoveTo = function(gotoSlide) {
        var curelm = elm('jslider_'+myId+'_slide_'+mySeekPointer);
        curelm.fadeOut(500, function() {
            curelm.style.display = 'none';
            mySeekPointer = gotoSlide-1;
            jsliders[myId].RefreshNavigation();
            //window.alert('pointer='+mySeekPointer+' myId='+myId);
        });
    }
    this.addNav = function(navHTML) {myAdditionalNav += navHTML;}
    this.setCallbackNext = function(func) {
        myCallbackNext = func;
    }
    this.curSlide = function() {return(mySeekPointer);}
    this.RefreshNavigation = function() {
        if(mySeekPointer<0) mySeekPointer = 0;
        if(mySeekPointer>0) {
            elm('jslider_'+myId+'_navleft').className = 'jslider_nav_enabled';
            elm('jslider_'+myId+'_navleft').onclick = function() {jsliders[myId].MoveLeft();}
        } else {
            elm('jslider_'+myId+'_navleft').className = 'jslider_nav_disabled';
            elm('jslider_'+myId+'_navleft').onclick = function() {return null;}
        }
        if(mySeekPointer<mySlidesCount-1) {
            elm('jslider_'+myId+'_navright').className = 'jslider_nav_enabled';
            elm('jslider_'+myId+'_navright').onclick = function() {jsliders[myId].MoveRight();}
        } else {
            elm('jslider_'+myId+'_navright').className = 'jslider_nav_disabled';
            elm('jslider_'+myId+'_navright').onclick = function() {return null;}
        }
        var curelm = elm('jslider_'+myId+'_slide_'+mySeekPointer);
        curelm.setOpacity(0);
        curelm.style.display = 'block';
        curelm.fadeIn(500);
        if(bShowSlideMeter) elm('jslider_'+myId+'_meter').innerHTML = ''+parseInt(mySeekPointer+1)+' из '+mySlidesCount;
    }
}

var jmsgboxes = new Array();
var jmsgboxes_pool = 0;


function jMsgBox() {
    var width, height, label, content, callback,
    // Элемент окна в котором будет отображаться содержимое
    win = document.createElement("div"),
    // Работает ли браузер в режиме совместимости
    isQuirks = document.compatMode!=="CSS1Compat",
    // В каком элементе следует искать данные о размерах/позиции
    body = document[isQuirks ? "body" : "documentElement"],
    // Получаем подложку, которая была добавлена ранее
    wrapper = document.getElementById("jmsgboxwrapper"),
    // К какому элементу будут привязаны данные
    parent = this.nodeType ? this : {},
    bnames = {};
    
    // Если подложка ещё не была создана, создаём её
    if(!wrapper){
        wrapper = document.createElement("div");
        wrapper.id = wrapper.className = "jmsgboxwrapper";
        wrapper.style.cssText = "position:absolute;left:0;top:0;width:100%;display:none";
        document.documentElement.appendChild(wrapper);
    }
    
    // Очищаем wrapper перед показом нового содержимого
    wrapper.innerHTML = "";
    
    // Определение надписей на кнопках управления окном
    bnames[jc_OK] = "OK";
    bnames[jc_YES] = "Да";
    bnames[jc_NO] = "Нет";
    bnames[jc_CANCEL] = "Отмена";
    bnames[jc_SAVE] = "Сохранить";
    
    // Определяет контент плавающего окна (узел или тест содержимого)
    parent.setContent = function(data){
        content = document.createElement("div");
        if(typeof data === "string"){
            content.innerHTML = data;
        }else if(data && data.nodeType){
            content.appendChild(data);
        }
        content.className = "jmsgbox_content";
        return content;
    }
    // Определяет подпись окна с содержимым
    parent.setLabel = function(l) {label = l}
    // Определяет ширину окна с содержимым
    parent.setWidth = function(w) {width = w}
    // Определяет функцию ответа пользователю о действии
    parent.setCallback = function(c) {callback = c}
    // Закрывает окно с содержимым и отправляет ответ пользователю
    parent.close = function(result) {
        if(callback) callback(result);
        wrapper.innerHTML = "";
        wrapper.style.display = "none";
        return content;
    }
    // Отображает окно с содержимым для пользователя
    parent.render = function(btn, draggable, usekeys){
        var x = 0, button, buttons,
        // Создаём требуемые для работы элементы окна
        hdr = document.createElement("h3"),
        nav = document.createElement("div"),
        // Производим расчёты размеров и положение окна/документа
        winWidth = window.innerWidth || body.clientWidth || 0,
        winHeight = window.innerHeight || body.clientHeight || 0,
        scrollTop = window.pageYOffset || body.scrollTop || 0,
        docHeight = body.offsetHeight || 0;

        // Окно по умолчанию будет возможно перемещать по экрану
        draggable = draggable===false ? false : true;
        
        // Навсякий случай скрываем подложку, а окно делаем невидимым (см. ниже)
        win.style.visibility = "hidden";
        wrapper.style.display = "none";
        
        // Создаем кнопки в окне, в зависимости от пожелания пользователя
        switch(btn) {
            case jc_YESNO:buttons = [jc_NO, jc_YES];break;
            case jc_OKCANCEL:buttons = [jc_CANCEL, jc_OK];break;
            case jc_SAVECANCEL:buttons = [jc_CANCEL, jc_SAVE];break;
            case jc_CANCEL:buttons = [jc_CANCEL];break;
            default:buttons = [jc_OK];
        }
        
        for(;x<buttons.length;x++){
            button = nav.appendChild(document.createElement("a"));
            button.name = buttons[x];
            button.style.cursor = "pointer";
            button.innerHTML = bnames[buttons[x]];
            button.onclick = function(){parent.close(Number(this.name))}
        }
        
        // Обработка горячих клавиш для управления окном
        // IE хранит свойство события onkeydown в узле document, остальные в объекте window
        ("onkeydown" in document ? document : window).onkeydown = usekeys ? function(e){
            switch((e || window.event).keyCode){
                case 27:parent.close(buttons[0]);break;
                case 13:parent.close(buttons[1] || buttons[0]);break;
                default:return true;
            }
            this.onkeydown = null;
            return false;
        } : null;
        
        // Наполняем окно сгенерированным содержимым
        if(!content) parent.setContent();
        
        nav.className = "jmsgbox_nav";
        
        hdr.innerHTML = label;
        hdr.className = "jmsgbox_header";

        win.className = "jmsgbox_container";
        win.appendChild(hdr);
        win.appendChild(content);
        win.appendChild(nav);
        
        wrapper.appendChild(win);
        
        // Даём окну возможность быть перемещённым
        if(draggable){
            elm(hdr).makeDraggable(win);
            hdr.style.cursor = "pointer";
            hdr.onmouseout = function(){hdr.className = "jmsgbox_header"}
            hdr.onmouseover = function(){hdr.className = "jmsgbox_header jmsgbox_header_light"}
        }
        
        // Если высота окна больше, чем размер документа, подложка должна быть размера окна
        wrapper.style.height = (winHeight > docHeight ? winHeight : docHeight)+"px";
        
        // Чтобы получить верные расчёты, подложку показываем, в нём окно с visibility=hidden
        wrapper.style.display = "block";
        
        // Если блок заполнен inline элементами - он будет сжат
        win.style.overflow = content.style.overflow = "inherit";

        // Firefox 2 Правильно определяет ширину только у inline элементов
        content.style.display = "table-cell";
        win.style.width = (width = width || content.offsetWidth)+"px";
        height = win.offsetHeight;
        content.style.display = "block";
        
        win.style.zIndex = "100";
        win.style.position = "absolute";
        win.style.left = winWidth/2 - width/2 + "px";
        win.style.top = winHeight/2 - height/2 + scrollTop + "px";
        
        // Снимаем видимость в самом конце, чтобы получить верные расчёты выше
        win.style.visibility = "inherit";
        return content;
    }
    // Отдаёт пользователю объект из которого будут вызываться функции работы с окном
    return parent;
}


// Возвращает объект XMLHttpRequest или undefined
var jGetXHR = window.ActiveXObject ? function(){
    try{
        return new ActiveXObject("Microsoft.XMLHTTP");
    }catch(e){ }
} : function(){
    try{
        return new XMLHttpRequest();
    }catch(e){ }
}

// Замена стандартному typeof "null", "undefined", "array", "object", "string", "boolean", "number", "regexp"
var jGetType = function(data){
    var l, t, types = {}, toStr = Object.prototype.toString, expr = /\[object ([^\]]+)/,
        tps = "Array|Object|String|Boolean|Number|RegExp".split("|");
    for(l = tps.length; t = tps[--l];) types["[object "+t+"]"] = t.toLowerCase();
    return function(data){
        return data==null ? String(data) : types[toStr.call(data)] || "object";
    }
}()

// Кодирует объект в строку Query String с n вложений
var jQueryEncode = function(data, name){
    var type = jGetType(data), result = [];
    name = name || "";
    if(type==="string" || type==="boolean"){
        return name+(name && "=")+encodeURIComponent(data);
    }else if(type==="number" && isFinite(data) && !isNaN(data)){
        return name+(name && "=")+data;
    }else if(type==="object"){
        for(var x in data)
            result[result.length] = jQueryEncode(data[x], name ? name+"["+x+"]" : x);
        return result.join("&");
    }else if(type==="array"){
        name += "[]";
        for(var x=0; x<data.length; x++)
            result[result.length] = jQueryEncode(data[x], name);
        return result.join("&");
    }
    return null;
}

// Проверяет XML на валидность. Проверяет по узлу или документу. возвращает документ XML или NULL
var jCheckXML = function(xml){
    if(!xml) return null;
    xml = xml.ownerDocument || xml;
    xml = xml.XMLDocument || xml;
    var doc = xml.documentElement, name = doc && doc.nodeName;
    return name && name!=="parsererror" && name!=="HTML" && doc.firstChild ? xml : null;
}

// Преобразует данные из строки в документ XML
var jParseXML = function(xml){
    var parser;
    try{
       if(window.DOMParser) {
          parser = new DOMParser();
          parser = parser.parseFromString(xml, "text/xml");
       }else if(window.ActiveXObject){
          parser = new ActiveXObject("Microsoft.XMLDOM");
          parser.async = "false";
          parser.loadXML(xml);
       }
    }catch(e){ }
    return parser || null;
}

// Проверяет JSON на валидность. Возвращает boolean
var jCheckJSON = function(){
    var chars = /^[\],:{}\s]*$/,
    braces = /(?:^|:|,)(?:\s*\[)+/g,
    escape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
    tokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
    return function(json){
        return !!json && jGetType(json)==="string" && chars.test(json.replace(escape,"@").replace(tokens,"]").replace(braces,""));
    }
}()

// Преобразует данные из строки в объект JSON
var jParseJSON = function(json){
    return jCheckJSON(json) ? (window.JSON && JSON.parse ? JSON.parse(json) : (new Function( "return " + json ))()) : null;
}

// Посылает асинхронный запрос. Возвращает boolean, успешно ли начался запрос
// 
// data: объект или строка с данными, которые будут присоединины к запросу
// headers: объект с передаваемыми заголовками, например {"X-Data": "json"}
// options: {
//    url: адрес - получатель запроса
//    method: метод, которым будет отправлен запрос
//    type: тип данных, который должен вернуть сервер xml, html, text, script, json
//    timeout: Сколько ждать до принудительного обрыва запроса в секундах
//    callback(status,data): Функция, в которую будет направлен ответ при любом исходе
//    onerror(data): Функция, в которую будет направлен ответ при ошибке
//    onsuccess(data): Функция, в которую будет направлен ответ при положительном исходе
// }
function jSimpleAjax (options, data, headers){

   var types = {
       html: ["text/html"],
       text: ["text/plain"],
       xml: ["application/xml", "text/xml"],
       script: ["application/javascript", "text/javascript", "application/ecmascript", "application/x-ecmascript"],
       json: ["application/json", "text/javascript", "application/javascript", "application/ecmascript", "application/x-ecmascript"]
   }

   var
      xhr,
      timeout,
      headers = headers || {},
      url = (options.url || location.href).replace(/#.*/, ""),
      method = (options.method || "GET").toUpperCase(),
      dataType = (options.type || "text").toLowerCase(),
      hasBody = /POST|PUT|DELETE|TRACE/.test(method),
      query = typeof data==="string" ? data : jQueryEncode(data);
   
   var getData = function(){
      var xml = xhr.responseXML, text = xhr.responseText || "";
      if(dataType==="xml" && !xml){
          return jParseXML(text);
      }else if(dataType==="json"){
          return jParseJSON(text);
      }else if(dataType!=="xml"){
          return text;
      }
      return xml || null;
   }
   
   var clearData = function(){
       xhr.onreadystatechange = null;
       clearTimeout(timeout);
       xhr = options.callback = options.onsuccess = options.onerror = null;
   }
   
   var requestError = function(status){
       if(options.onerror) options.onerror(status);
       if(options.callback) options.callback(false,status);
   }
   
   if(query && !hasBody){
       url += (/\?/.test(url) ? "&" : "?")+query;
   }
   
   if(!(xhr = jGetXHR())) return requestError();
   try{xhr.open(method, url, true)}catch(e){return requestError()}
   
   headers["X-Requested-With"] = "XMLHttpRequest";
   
   if(query && hasBody && !headers["Content-Type"]){
       headers["Content-Type"] = "application/x-www-form-urlencoded";
   }
   
   if(dataType==="xml" && types[dataType]){
       if(xhr.overrideMimeType){
           try{ xhr.overrideMimeType(types[dataType][0]) }catch(e){ }
       }
       headers["Accept"] = types[dataType].join(", ")+", */*; q=0.01";
   }
   
   for(var x in headers) xhr.setRequestHeader(x, headers[x]);
   
   xhr.onreadystatechange = function(){
       if(xhr.readyState===4){
           var stat = xhr.status;
           if((stat>199 && stat<300) || stat===304 || stat===0 || stat===1223){
               var data = getData();
               if(options.onsuccess) options.onsuccess(data);
               if(options.callback) options.callback(true,data);
           }else{
               requestError(stat);
           }
           clearData();
       }else if(xhr.readyState===0){
           requestError();
           clearData();
       }
   }

   try{ xhr.send(hasBody ? query || null : null) }catch(e){ return requestError() }
   
   if(options.timeout && options.timeout>0){
       timeout = setTimeout(function(){
           xhr.onreadystatechange = null;
           try{
              xhr.abort();
           }catch(e){ }
           requestError();
           xhr = options.callback = options.onsuccess = options.onerror = null;
       },options.timeout*1000);
   }
   
   return true;
}

function jzNoEnter(e) {
    var key;
    if(window.event) key = window.event.keyCode; //IE
    else key = e.which; //firefox
    return (key != 13);
}

function jzDecHex(d) {return d.toString(16);}
function jzHexDec(h) {return parseInt(h,16);}
function jzColorDecHex(d) {
    var h = jzDecHex(d);
    if(h.length===1) h = '0'+h;
    return h;
}
function jzColorRGB2Hex(r,g,b) {return '#'+jzColorDecHex(r)+jzColorDecHex(g)+jzColorDecHex(b);}
function jzColorRandom() {
    var r = Math.round(Math.random()*255);
    var g = Math.round(Math.random()*255);
    var b = Math.round(Math.random()*255);
    return(jzColorRGB2Hex(r, g, b));
}
function jzSender(e) {return (e && e.target) || (window.event && window.event.srcElement);}
// Узнаем, какой браузер к нам пришел
var jc_BROWSER = "Unknown";
var jc_BROWSER_IE_VERSION = "not IE";
if (window.opera) jc_BROWSER = "Opera";
else if (navigator.userAgent) {
    if (navigator.userAgent.indexOf("MSIE") != -1) {
        jc_BROWSER = "IE";
        if (navigator.userAgent.indexOf("MSIE 8.0") != -1) jc_BROWSER_IE_VERSION = 8;
        if (navigator.userAgent.indexOf("MSIE 7.0") != -1) jc_BROWSER_IE_VERSION = 7;
        if (navigator.userAgent.indexOf("MSIE 6.0") != -1) jc_BROWSER_IE_VERSION = 6;
        if (navigator.userAgent.indexOf("MSIE 5.")  != -1) jc_BROWSER_IE_VERSION = 5;
    }
    else if (navigator.userAgent.indexOf("Firefox") != -1) jc_BROWSER = "Firefox";
}

// вычисляем или изменяем текущую отображаемую часть страницы (смещение по прокрутке)
function jzScrollTop(_scrollto) {
        var scrollto = (_scrollto) ? _scrollto : 0;

        var iebody = (document.compatMode && document.compatMode != "BackCompat") ? document.documentElement : document.body;
        //var dsocleft=document.all? iebody.scrollLeft : pageXOffset;
        var curscroll = document.all? iebody.scrollTop : pageYOffset;
        if(scrollto>0) {
            if(jc_BROWSER=='IE') d.documentElement.scrollTop = scrollto;
            else d.body.scrollTop = scrollto;
            return(0);
        }
        else return(curscroll);
}

// функция увеличивает размер картинки до настоящего по наведению мышкой. Должна вызываться не раньше onload.
function jImgRolloverZoom(_classname, _smallsize_param) {
    var zoom_factor = (_smallsize_param)?_smallsize_param:0.5;
    var classname = (_classname)?_classname:'imgautosized';
    var imgs = d.getElementsByClassName(classname);
    //var orig_size, small_size;
    for(var i=0; i<imgs.length; i++) {
        imgs[i].orig_size = {width:imgs[i].width, height:imgs[i].height};
        imgs[i].small_size = {width:Math.round(imgs[i].orig_size['width']*zoom_factor), height:Math.round(imgs[i].orig_size['height']*zoom_factor)};
        imgs[i].jlocked = false;
        imgs[i].onmouseover = function() {if(!this.jlocked){this.style.width=this.orig_size['width']+'px';this.style.height = this.orig_size['height']+'px';}}
        imgs[i].onmouseout = function() {if(!this.jlocked) {this.style.width=this.small_size['width']+'px';this.style.height = this.small_size['height']+'px';}}
        imgs[i].onclick = function() {this.jlocked = (this.jlocked)? false:true;}
        imgs[i].onmouseout();
    }
}

// Эта функция отслеживает, когда дерево DOM было загружено
var jsAddDOMLoadEvent = function(){
    var
    handlers = [], isReady = false, isTop = false,
    isComplete = function(){if(document.readyState==="complete") ready(1)},
    doScroll = function(){
        if (isReady) return;
	try {document.documentElement.doScroll("left")}catch(e){setTimeout(doScroll, 1);return}
	ready();
    },
    ready = function(a){
        if(!document.body) return setTimeout(ready, 1);
        if(isReady || handlers.length===0) return;
        isReady = true;
        for(var x=0;x<handlers.length;x++) handlers[x]();
    }
    isComplete();
    if(document.addEventListener) {
        window.addEventListener("load", ready, false);
        document.addEventListener("DOMContentLoaded", ready, false);
    }else if(document.attachEvent){
        document.attachEvent( "onreadystatechange", isComplete);
        window.attachEvent("onload", ready);
        try{isTop = window.frameElement==null} catch(e) {}
        if ( document.documentElement.doScroll && isTop) {doScroll()}
    }
    return function(handler){
        !isReady ? handlers[handlers.length] = handler : handler();
    }
}()

// эта функция должна быть последней во фреймворке.
function jzAddOnloadEvent(func, putBefore) { // won't work if called before <BODY onload="..."> specified:(
    var bPutBefore = (putBefore) ? putBefore : false;
    if(func==undefined) {
        if(jc_DBG) dbg('undefined function on input to jzAddOnloadEvent'); //else dbg(func);
        return;
    }
    var oldonload = function() {return(null);}
    if(!(window.onload === null || window.onload===undefined)) { // в chrome/ie - по умолчанию null, в firefox - undefined
        oldonload = window.onload;
    }
    window.onload = (bPutBefore)? function() {func();oldonload();} : function() {oldonload();func();}
}
