Программный интерфейс

Показывать:
/**
 * Формы визуализации и изменения параметров объекта
 *
 * © Evgeniy Malyarov http://www.oknosoft.ru 2014-2016
 *
 * @module common
 * @submodule wnd_dat
 */


/**
 * Форма dat - шаблон окна инструментов
 */
$p.iface.dat_blank = function(_dxw, attr) {

  // TODO: реализовать undock для аккордиона

  if(!attr)
    attr = {};

  var wnd_dat = (_dxw || $p.iface.w).createWindow({
    id: dhx4.newId(),
    left: attr.left || 700,
    top: attr.top || 20,
    width: attr.width || 220,
    height: attr.height || 300,
    move: true,
    park: !attr.allow_close,
    center: !!attr.center,
    resize: true,
    caption: attr.caption || "Tools"
  });

  // если окно не помещается в области - двигаем
  var _dxw_area = {
    x: (_dxw || $p.iface.w).vp.clientWidth,
    y: (_dxw || $p.iface.w).vp.clientHeight
  }, _move;
  
  if(wnd_dat.getPosition()[0] + wnd_dat.getDimension()[0] > _dxw_area.x){
    _dxw_area.x = _dxw_area.x - wnd_dat.getDimension()[0];
    _move = true;
  }else
    _dxw_area.x = wnd_dat.getPosition()[0];
  
  if(wnd_dat.getPosition()[1] + wnd_dat.getDimension()[1] > _dxw_area.y){
    _dxw_area.y = _dxw_area.y - wnd_dat.getDimension()[1];
    _move = true;
  }else
    _dxw_area.y = wnd_dat.getPosition()[1];
    
  if(_move){
    if(_dxw_area.x<0 || _dxw_area.y<0)
      wnd_dat.maximize();
    else
      wnd_dat.setPosition(_dxw_area.x, _dxw_area.y);
  }

  _dxw = null;

  if(attr.hasOwnProperty('allow_minmax') && !attr.allow_minmax)
    wnd_dat.button('minmax').hide();

  if(attr.allow_close)
    wnd_dat.button('park').hide();
  else
    wnd_dat.button('close').hide();

  // обработчик при закрытии - анализируем модальность
  wnd_dat.attachEvent("onClose", function () {
    
    var allow_close = typeof attr.on_close == "function" ? attr.on_close(wnd_dat) : true;
    
    if(allow_close){

      // восстанавливаем модальность родительского окна
      if(attr.pwnd_modal && attr.pwnd && attr.pwnd.setModal)
        attr.pwnd.setModal(1);

      return allow_close;
    }
            
  });

  wnd_dat.setIconCss('without_icon');
  wnd_dat.cell.parentNode.children[1].classList.add('dat_gui');

  $p.iface.bind_help(wnd_dat, attr.help_path);

  wnd_dat.elmnts = {grids: {}};

  wnd_dat.wnd_options = function (options) {
    var pos = wnd_dat.getPosition(),
      sizes = wnd_dat.getDimension(),
      parked = wnd_dat.isParked();
    options.left = pos[0];
    options.top = pos[1];
    options.width = sizes[0];
    options.parked = parked;
    if(!parked)
      options.height = sizes[1];

  };

  wnd_dat.bottom_toolbar = function(oattr){

    var attr = ({
        wrapper: wnd_dat.cell,
        width: '100%',
        height: '28px',
        bottom: '0px',
        left: '0px',
        name: 'tb_bottom',
        buttons: [
          {name: 'btn_cancel', text: 'Отмена', title: 'Закрыть без сохранения', width:'60px', float: 'right'},
          {name: 'btn_ok', b: 'Ок', title: 'Применить изменения', width:'30px', float: 'right'}
        ],
        onclick: function (name) {
          return false;
        }
      })._mixin(oattr),

      tb_bottom = new OTooolBar(attr),
      sbar = wnd_dat.attachStatusBar({height: 12});
    sbar.style.zIndex = -1000;
    sbar.firstChild.style.backgroundColor = "transparent";
    sbar.firstChild.style.border = "none";
    return tb_bottom;
  };

  if(attr.modal){
    if(attr.pwnd && attr.pwnd.setModal){
      attr.pwnd_modal = attr.pwnd.isModal();
      attr.pwnd.setModal(0);
    }      
    wnd_dat.setModal(1);
  }

  return wnd_dat;
};

/**
 * обработчик выбора значения в свойствах (ссылочные типы)
 * вызывается в контексте this = pgrid
 * @param selv {*} выбранное значение
 */
$p.iface.pgrid_on_select = function(selv){

  if(selv===undefined)
    return;

  var pgrid = this.grid instanceof dhtmlXGridObject ? this.grid : this,
    source = pgrid.getUserData("", "source"),
    f = pgrid.getSelectedRowId();

  if(source.o[f] != undefined){
    if(typeof source.o[f] == "number")
      source.o[f] = $p.utils.fix_number(selv, true);
    else
      source.o[f] = selv;

  }else if(f.indexOf("fprms") > -1){
    var row = $p._find(source.o.fprms, f.split("|")[1]);
    row.value = selv;
  }

  pgrid.cells().setValue($p.utils.is_data_obj(selv) ? selv.presentation : selv || "");
  

  if(source.grid_on_change)
    source.grid_on_change.call(pgrid, f, selv);
};

/**
 * обработчик изменения значения в свойствах (примитивные типы)
 * @param pname {String} - имя измененного свойства
 * @param new_value {*} - новое значение
 * @param old_value {*} - предыдущее значение
 */
$p.iface.pgrid_on_change = function(pname, new_value, old_value){
  if(pname)
    $p.iface.pgrid_on_select.call(this, new_value);
};

/**
 * обработчик изменения флажка в свойствах (bit)
 * @param rId {String} - идентификатор строки
 * @param cInd {Number} - идентификатор колонки
 * @param state {Boolean} - состояние чекбокса
 */
$p.iface.pgrid_on_checkbox = function(rId, cInd, state){

  var pgrid = this.grid instanceof dhtmlXGridObject ? this.grid : this,
    source = pgrid.getUserData("", "source");

  if(source.o[rId] != undefined)
    source.o[rId] = state;

  if(source.grid_on_change)
    source.grid_on_change(rId, state);
};


function _clear_all(){
  $p.iface.docs.__define({
    clear_all: {
      value: function () {
        this.detachToolbar();
        this.detachStatusBar();
        this.detachObject(true);
      },
      enumerable: false
    },
    "Очистить": {
      get: function () {
        return this.clear_all;
      },
      enumerable: false
    },
    "Контейнер": {
      get: function () {
        return this.cell.querySelector(".dhx_cell_cont_layout");
      },
      enumerable: false
    }
  });
}

/**
 * Создаёт форму авторизации с обработчиками перехода к фидбэку и настройкам,
 * полем входа под гостевой ролью, полями логина и пароля и кнопкой входа
 * @method frm_auth
 * @for InterfaceObjs
 * @param attr {Object} - параметры формы
 * @param [attr.cell] {dhtmlXCellObject}
 * @return {Promise}
 */
$p.iface.frm_auth = function (attr, resolve, reject) {

  if(!attr)
    attr = {};

  var _cell, _frm, w, were_errors;

  if(attr.modal_dialog){
    if(!attr.options)
      attr.options = {
        name: "frm_auth",
        caption: "Вход на сервер",
        width: 360,
        height: 300,
        center: true,
        allow_close: true,
        allow_minmax: true,
        modal: true
      };
    _cell = $p.iface.dat_blank(attr._dxw, attr.options);
    _cell.attachEvent("onClose",function(win){
      if(were_errors){
        if(reject)
          reject(err);
      }else if(resolve)
        resolve();
      return true;
    });
    _frm = _cell.attachForm();

  }else{
    _cell = attr.cell || $p.iface.docs;
    _frm = $p.iface.auth = _cell.attachForm();
    $p.msg.show_msg($p.msg.init_login, _cell);
  }


  function do_auth(login, password){
    
    $p.ajax.username = login;
    $p.ajax.password = $p.aes.Ctr.encrypt(password);

    if(login){

      if($p.wsql.get_user_param("user_name") != login)
        $p.wsql.set_user_param("user_name", login);          // сохраняем имя пользователя в базе

      //$p.eve.log_in(attr.onstep)
      $p.wsql.pouch.log_in(login, password)
        .then(function () {

          if($p.wsql.get_user_param("enable_save_pwd")){
            if($p.aes.Ctr.decrypt($p.wsql.get_user_param("user_pwd")) != password)
              $p.wsql.set_user_param("user_pwd", $p.aes.Ctr.encrypt(password));   // сохраняем имя пользователя в базе
            
          }else if($p.wsql.get_user_param("user_pwd") != "")
            $p.wsql.set_user_param("user_pwd", "");

          $p.eve.logged_in = true;
          if(attr.modal_dialog)
            _cell.close();
          else if(resolve)
            resolve();

        })
        .catch(function (err) {
          were_errors = true;
          _frm.onerror(err);
        })
        .then(function () {
          if($p.iface.sync)
            $p.iface.sync.close();
          if(_cell && _cell.progressOff){
            _cell.progressOff();
            if(!were_errors && attr.hide_header)
              _cell.hideHeader();
          }
          if($p.iface.cell_tree && !were_errors)
            $p.iface.cell_tree.expand();
        });

    } else
      this.validate();
  }

  // обработчик кнопки "войти" формы авторизации
  function auth_click(name){

    were_errors = false;
    this.resetValidateCss();

    if(this.getCheckedValue("type") == "guest"){

      var login = this.getItemValue("guest"),
        password = "";
      if($p.job_prm.guests && $p.job_prm.guests.length){
        $p.job_prm.guests.some(function (g) {
          if(g.username == login){
            password = $p.aes.Ctr.decrypt(g.password);
            return true;
          }
        });
      }
      do_auth.call(this, login, password);

    }else if(this.getCheckedValue("type") == "auth"){
      do_auth.call(this, this.getItemValue("login"), this.getItemValue("password"));

    }
  }

  // загружаем структуру формы
  _frm.loadStruct($p.injected_data["form_auth.xml"], function(){

    var selected;

    // если указан список гостевых пользователей
    if($p.job_prm.guests && $p.job_prm.guests.length){

      var guests = $p.job_prm.guests.map(function (g) {
          var v = {
            text: g.username,
            value: g.username
          };
          if($p.wsql.get_user_param("user_name") == g.username){
            v.selected = true;
            selected = g.username;
          }
          return v;
        });

      if(!selected){
        guests[0].selected = true;
        selected = guests[0].value;
      }

      _frm.reloadOptions("guest", guests);
    }

    // после готовности формы читаем пользователя из локальной датабазы
    if($p.wsql.get_user_param("user_name") && $p.wsql.get_user_param("user_name") != selected){
      _frm.setItemValue("login", $p.wsql.get_user_param("user_name"));
      _frm.setItemValue("type", "auth");

      if($p.wsql.get_user_param("enable_save_pwd") && $p.wsql.get_user_param("user_pwd")){
        _frm.setItemValue("password", $p.aes.Ctr.decrypt($p.wsql.get_user_param("user_pwd")));
      }
    }

    // позиционируем форму по центру
    if(!attr.modal_dialog){
      if((w = ((_cell.getWidth ? _cell.getWidth() : _cell.cell.offsetWidth) - 500)/2) >= 10)
        _frm.cont.style.paddingLeft = w.toFixed() + "px";
      else
        _frm.cont.style.paddingLeft = "20px";
    }

    setTimeout(function () {

      dhx4.callEvent("on_draw_auth", [_frm]);

      if(($p.wsql.get_user_param("autologin") || attr.try_auto) && (selected || ($p.wsql.get_user_param("user_name") && $p.wsql.get_user_param("user_pwd"))))
        auth_click.call(_frm);

    });
  });

  // назначаем обработчик нажатия на кнопку
  _frm.attachEvent("onButtonClick", auth_click);

  _frm.attachEvent("onKeyDown",function(inp, ev, name, value){
    if(ev.keyCode == 13){
      if(name == "password" || this.getCheckedValue("type") == "guest"){
        auth_click.call(this);
      }
    }
  });


  _frm.onerror = function (err) {

    $p.ajax.authorized = false;

    var emsg = err.message.toLowerCase();

    if(emsg.indexOf("auth") != -1) {
      $p.msg.show_msg({
        title: $p.msg.main_title + $p.version,
        type: "alert-error",
        text: $p.msg.error_auth
      });
      _frm.setItemValue("password", "");
      _frm.validate();

    }else if(emsg.indexOf("gateway") != -1 || emsg.indexOf("net") != -1) {
      $p.msg.show_msg({
        title: $p.msg.main_title + $p.version,
        type: "alert-error",
        text: $p.msg.error_network
      });
    }
  }



};


/**
 * Служебная функция для открытия окна настроек из гиперссылки
 * @param e
 * @return {Boolean}
 */
$p.iface.open_settings = function (e) {
  var evt = (e || (typeof event != "undefined" ? event : undefined));
  if(evt)
    evt.preventDefault();

  var hprm = $p.job_prm.parse_url();
  $p.iface.set_hash(hprm.obj, hprm.ref, hprm.frm, "settings");

  return $p.iface.cancel_bubble(evt);
};

/**
 * Переключает вид формы между списком, календаарём и отчетами
 * @method swith_view
 * @for InterfaceObjs
 * @param name {String} - имя представления
 */
$p.iface.swith_view = function(name){

  var state,
    iface = $p.iface,

    /**
     * Переключает состав элементов дерева
     * @param view
     */
    swith_tree = function(name){

      function compare_text(a, b) {
        if (a.text > b.text) return 1;
        if (a.text < b.text) return -1;
      }

      if(!iface.tree){

        var hprm = $p.job_prm.parse_url();
        if(hprm.obj) {
          var parts = hprm.obj.split('.');
          if(parts.length > 1){

            var mgr = $p.md.mgr_by_class_name(hprm.obj);

            if(typeof iface.docs.close === "function" )
              iface.docs.close();

            if(mgr)
              mgr.form_list(iface.docs, {});
          }
        }
        return;

      }else if(iface.tree._view == name || ["rep", "cal"].indexOf(name) != -1)
        return;

      iface.tree.deleteChildItems(0);
      if(name == "oper"){
        var meta_tree = {id:0, item:[
          {id:"oper_cat", text: $p.msg.meta_cat, open: true, item:[]},
          {id:"oper_doc", text: $p.msg.meta_doc, item:[]},
          {id:"oper_cch", text: $p.msg.meta_cch, item:[]},
          {id:"oper_cacc", text: $p.msg.meta_cacc, item:[]},
          {id:"oper_tsk", text: $p.msg.meta_tsk, item:[]}
        ]}, mdn, md,

        // бежим по справочникам
          tlist = meta_tree.item[0].item;
        for(mdn in $p.cat){
          if(typeof $p.cat[mdn] == "function")
            continue;
          md = $p.cat[mdn].metadata();
          if(md.hide)
            continue;
          tlist.push({id: "oper.cat." + mdn, text: md.synonym || md.name, tooltip: md.illustration || md.list_presentation});
        }
        tlist.sort(compare_text);

        // бежим по документам
        tlist = meta_tree.item[1].item;
        for(mdn in $p.doc){
          if(typeof $p.doc[mdn] == "function")
            continue;
          md = $p.doc[mdn].metadata();
          if(md.hide)
            continue;
          tlist.push({id: "oper.doc." + mdn, text: md.synonym || md.name, tooltip: md.illustration || md.list_presentation});
        }
        tlist.sort(compare_text);

        // бежим по планам видов характеристик
        tlist = meta_tree.item[2].item;
        for(mdn in $p.cch){
          if(typeof $p.cch[mdn] == "function")
            continue;
          md = $p.cch[mdn].metadata();
          if(md.hide)
            continue;
          tlist.push({id: "oper.cch." + mdn, text: md.synonym || md.name, tooltip: md.illustration || md.list_presentation});
        }
        tlist.sort(compare_text);

        // бежим по планам счетов
        tlist = meta_tree.item[3].item;
        for(mdn in $p.cacc){
          if(typeof $p.cacc[mdn] == "function")
            continue;
          md = $p.cacc[mdn].metadata();
          if(md.hide)
            continue;
          tlist.push({id: "oper.cacc." + mdn, text: md.synonym || md.name, tooltip: md.illustration || md.list_presentation});
        }
        tlist.sort(compare_text);

        // бежим по задачам
        tlist = meta_tree.item[4].item;
        for(mdn in $p.tsk){
          if(typeof $p.tsk[mdn] == "function")
            continue;
          md = $p.tsk[mdn].metadata();
          if(md.hide)
            continue;
          tlist.push({id: "oper.tsk." + mdn, text: md.synonym || md.name, tooltip: md.illustration || md.list_presentation});
        }
        tlist.sort(compare_text);

        iface.tree.parse(meta_tree, function(){
          var hprm = $p.job_prm.parse_url();
          if(hprm.obj){
            iface.tree.selectItem(hprm.view+"."+hprm.obj, true);
          }
        }, "json");

      }else{
        iface.tree.loadXML(iface.tree.tree_filteres, function(){

        });

      }

      iface.tree._view = name;
    };

  if(name.indexOf(iface.docs.getViewName())==0)
    return iface.docs.getViewName();

  state = iface.docs.showView(name);
  if (state == true) {
    // first call, init corresponding components
    // календарь
    if(name=="cal" && !window.dhtmlXScheduler){
      $p.load_script("dist/dhtmlxscheduler.min.js", "script", function(){
        //scheduler.config.xml_date="%Y-%m-%d %H:%i";
        scheduler.config.first_hour = 8;
        scheduler.config.last_hour = 22;
        iface.docs.scheduler = iface.docs.attachScheduler(new Date("2015-11-20"), "week", "scheduler_here");
        iface.docs.scheduler.attachEvent("onBeforeViewChange", function(old_mode, old_date, mode, date){
          if(mode == "timeline"){
            $p.msg.show_not_implemented();
            return false;
          }
          return true;
        });
      });

      $p.load_script("dist/dhtmlxscheduler.css", "link");

      //}else if(name=="rep"){
      //  // подключаемый отчет
      //
      //}else if(name=="oper"){
      //  // в дереве - список метаданных, в окне - список текущего метаданного
      //

    }
  }

  swith_tree(name);

  if(name == "def")
    iface.main.showStatusBar();
  else
    iface.main.hideStatusBar();
};


/**
 * ### Визуальный компонент OTooolBar
 * Панель инструментов рисовалки и альтернативная панель инструментов прочих форм
 * - Гибкое управление размером, положением и выравниванием как самой панели, так и отдельных кнопок
 * - Кнопки и группы кнопок, иконы и текст
 * - Всплывающие подсказки с произвольным html
 *
 * @class OTooolBar
 * @param attr {Object} - параметры создаваемой панели - родитель, положение, размер и ориентация
 * @constructor
 * @menuorder 54
 * @tooltip Командная панель
 */
function OTooolBar(attr){
  var _this = this,
    div = document.createElement('div'),
    offset, popup_focused, sub_focused, btn_focused;

  if(!attr.image_path)
    attr.image_path = dhtmlx.image_path;

  if(attr.hasOwnProperty("class_name"))
    div.className = attr.class_name;
  else
    div.className = 'md_otooolbar';

  _this.cell = div;

  _this.buttons = {};

  function bselect(select){
    for(var i=0; i<div.children.length; i++){
      div.children[i].classList.remove('selected');
    }
    if(select && !this.classList.contains('selected'))
      this.classList.add('selected');
  }

  function popup_hide(){
    popup_focused = false;
    setTimeout(function () {
      if(!popup_focused)
        $p.iface.popup.hide();
    }, 300);
  }

  function btn_click(){
    if(attr.onclick)
      attr.onclick.call(_this, this.name.replace(attr.name + '_', ''), attr.name);
  }

  /**
   * Добавляет кнопку на панель инструментов
   * @method add
   * @param battr {Object} - атрибуты создаваемой кнопки
   */
  this.add = function(battr){

    var bdiv = $p.iface.add_button(div, attr, battr);

    bdiv.onclick = btn_click;

    bdiv.onmouseover = function(){
      if(battr.title && !battr.sub){
        popup_focused = true;

        $p.iface.popup.clear();
        $p.iface.popup.attachHTML(battr.title);
        $p.iface.popup.show(dhx4.absLeft(bdiv), dhx4.absTop(bdiv), bdiv.offsetWidth, bdiv.offsetHeight);

        $p.iface.popup.p.onmouseover = function(){
          popup_focused = true;
        };

        $p.iface.popup.p.onmouseout = popup_hide;

        if(attr.on_popup)
          attr.on_popup($p.iface.popup, bdiv);
      }
    };

    bdiv.onmouseout = popup_hide;

    _this.buttons[battr.name] = bdiv;

    if(battr.sub){

      function remove_sub(parent){
        if(!parent)
          parent = bdiv;
        if(parent.subdiv && !sub_focused && !btn_focused){
          while(parent.subdiv.firstChild)
            parent.subdiv.removeChild(parent.subdiv.firstChild);
          parent.subdiv.parentNode.removeChild(parent.subdiv);
          parent.subdiv = null;
        }
      }

      bdiv.onmouseover = function(){

        // нужно погасить сабдивы соседей
        for(var i=0; i<bdiv.parentNode.children.length; i++){
          if(bdiv.parentNode.children[i] != bdiv && bdiv.parentNode.children[i].subdiv){
            remove_sub(bdiv.parentNode.children[i]);
            break;
          }
        }

        btn_focused = true;

        if(!this.subdiv){
          this.subdiv = document.createElement('div');
          this.subdiv.className = 'md_otooolbar';
          offset = $p.iface.get_offset(bdiv);
          if(battr.sub.align == 'right')
            this.subdiv.style.left = (offset.left + bdiv.offsetWidth - (parseInt(battr.sub.width.replace(/\D+/g,"")) || 56)) + 'px';
          else
            this.subdiv.style.left = offset.left + 'px';
          this.subdiv.style.top = (offset.top + div.offsetHeight) + 'px';
          this.subdiv.style.height = battr.sub.height || '198px';
          this.subdiv.style.width = battr.sub.width || '56px';
          for(var i in battr.sub.buttons){
            var bsub = $p.iface.add_button(this.subdiv, attr, battr.sub.buttons[i]);
            bsub.onclick = btn_click;
          }
          attr.wrapper.appendChild(this.subdiv);

          this.subdiv.onmouseover = function () {
            sub_focused = true;
          };

          this.subdiv.onmouseout = function () {
            sub_focused = false;
            setTimeout(remove_sub, 500);
          };

          if(battr.title)
            $p.iface.popup.show(dhx4.absLeft(this.subdiv), dhx4.absTop(this.subdiv), this.subdiv.offsetWidth, this.subdiv.offsetHeight);
        }

      };

      bdiv.onmouseout = function(){
        btn_focused = false;
        setTimeout(remove_sub, 500);
      }
    }
  };

  /**
   * Выделяет кнопку по событию mouseover и снимает выделение с остальных кнопок
   * @method select
   * @param name {String} - имя текущей кнопки
   */
  this.select = function(name){
    for(var i=0; i<div.children.length; i++){
      var btn = div.children[i];
      if(btn.name == attr.name + '_' + name){
        bselect.call(btn, true);
        return;
      }
    }
  };

  /**
   * Возвращает имя выделенной кнопки
   */
  this.get_selected = function () {
    for(var i=0; i<div.children.length; i++){
      var btn = div.children[i];
      if(btn.classList.contains('selected'))
        return btn.name;
    }
  };

  /**
   * Деструктор объекта
   * @method unload
   */
  this.unload = function(){
    while(div.firstChild)
      div.removeChild(div.firstChild);
    attr.wrapper.removeChild(div);
  };


  attr.wrapper.appendChild(div);
  div.style.width = attr.width || '28px';
  div.style.height = attr.height || '150px';
  div.style.position = 'absolute';

  if(attr.top) div.style.top = attr.top;
  if(attr.left) div.style.left = attr.left;
  if(attr.bottom) div.style.bottom = attr.bottom;
  if(attr.right) div.style.right = attr.right;
  if(attr.paddingRight) div.style.paddingRight = attr.paddingRight;
  if(attr.paddingLeft) div.style.paddingLeft = attr.paddingLeft;

  if(attr.buttons)
    attr.buttons.forEach(function(battr){
      _this.add(battr);
    });

};
$p.iface.OTooolBar = OTooolBar;

/**
 * Добавляет кнопку на панель инструментов
 * @method add_button
 * @for InterfaceObjs
 * @param parent {Element}
 * @param attr {Object}
 * @param battr {Object}
 * @returns {Element}
 */
$p.iface.add_button = function(parent, attr, battr) {
  var bdiv = document.createElement('div'), html = '';
  bdiv.name = (attr ? attr.name + '_' : '') + battr.name;
  parent.appendChild(bdiv);

  // если имя начинается с sep_ - это разделитель
  bdiv.className = (battr.name.indexOf("sep_") == 0) ? 'md_otooolbar_sep' : 'md_otooolbar_button';
  if(battr.hasOwnProperty("class_name"))
    bdiv.classList.add(battr.class_name);

  if(battr.img)
    html = '<img src="' + (attr ? attr.image_path : '') + battr.img + '">';
  if(battr.b)
    html +='<b style="vertical-align: super;"> ' + battr.b + '</b>';
  else if(battr.text)
    html +='<span style="vertical-align: super;"> ' + battr.text + '</span>';
  else if(battr.css)
    bdiv.classList.add(battr.css);
  bdiv.innerHTML = html;

  if(battr.float) bdiv.style.float = battr.float;
  if(battr.clear) bdiv.style.clear = battr.clear;
  if(battr.width) bdiv.style.width = battr.width;
  if(battr.paddingRight) bdiv.style.paddingRight = battr.paddingRight;
  if(battr.paddingLeft) bdiv.style.paddingLeft = battr.paddingLeft;

  if(battr.tooltip)
    bdiv.title = battr.tooltip;

  return bdiv;
};