/**
* Формы визуализации и изменения параметров объекта
*
* © 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;
};