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

Показывать:
  1. /**
  2. * Формы визуализации и изменения параметров объекта
  3. *
  4. * © Evgeniy Malyarov http://www.oknosoft.ru 2014-2016
  5. *
  6. * @module common
  7. * @submodule wnd_dat
  8. */
  9.  
  10.  
  11. /**
  12. * Форма dat - шаблон окна инструментов
  13. */
  14. $p.iface.dat_blank = function(_dxw, attr) {
  15.  
  16. // TODO: реализовать undock для аккордиона
  17.  
  18. if(!attr)
  19. attr = {};
  20.  
  21. var wnd_dat = (_dxw || $p.iface.w).createWindow({
  22. id: dhx4.newId(),
  23. left: attr.left || 700,
  24. top: attr.top || 20,
  25. width: attr.width || 220,
  26. height: attr.height || 300,
  27. move: true,
  28. park: !attr.allow_close,
  29. center: !!attr.center,
  30. resize: true,
  31. caption: attr.caption || "Tools"
  32. });
  33.  
  34. // если окно не помещается в области - двигаем
  35. var _dxw_area = {
  36. x: (_dxw || $p.iface.w).vp.clientWidth,
  37. y: (_dxw || $p.iface.w).vp.clientHeight
  38. }, _move;
  39. if(wnd_dat.getPosition()[0] + wnd_dat.getDimension()[0] > _dxw_area.x){
  40. _dxw_area.x = _dxw_area.x - wnd_dat.getDimension()[0];
  41. _move = true;
  42. }else
  43. _dxw_area.x = wnd_dat.getPosition()[0];
  44. if(wnd_dat.getPosition()[1] + wnd_dat.getDimension()[1] > _dxw_area.y){
  45. _dxw_area.y = _dxw_area.y - wnd_dat.getDimension()[1];
  46. _move = true;
  47. }else
  48. _dxw_area.y = wnd_dat.getPosition()[1];
  49. if(_move){
  50. if(_dxw_area.x<0 || _dxw_area.y<0)
  51. wnd_dat.maximize();
  52. else
  53. wnd_dat.setPosition(_dxw_area.x, _dxw_area.y);
  54. }
  55.  
  56. _dxw = null;
  57.  
  58. if(attr.hasOwnProperty('allow_minmax') && !attr.allow_minmax)
  59. wnd_dat.button('minmax').hide();
  60.  
  61. if(attr.allow_close)
  62. wnd_dat.button('park').hide();
  63. else
  64. wnd_dat.button('close').hide();
  65.  
  66. // обработчик при закрытии - анализируем модальность
  67. wnd_dat.attachEvent("onClose", function () {
  68. var allow_close = typeof attr.on_close == "function" ? attr.on_close(wnd_dat) : true;
  69. if(allow_close){
  70.  
  71. // восстанавливаем модальность родительского окна
  72. if(attr.pwnd_modal && attr.pwnd && attr.pwnd.setModal)
  73. attr.pwnd.setModal(1);
  74.  
  75. return allow_close;
  76. }
  77. });
  78.  
  79. wnd_dat.setIconCss('without_icon');
  80. wnd_dat.cell.parentNode.children[1].classList.add('dat_gui');
  81.  
  82. $p.iface.bind_help(wnd_dat, attr.help_path);
  83.  
  84. wnd_dat.elmnts = {grids: {}};
  85.  
  86. wnd_dat.wnd_options = function (options) {
  87. var pos = wnd_dat.getPosition(),
  88. sizes = wnd_dat.getDimension(),
  89. parked = wnd_dat.isParked();
  90. options.left = pos[0];
  91. options.top = pos[1];
  92. options.width = sizes[0];
  93. options.parked = parked;
  94. if(!parked)
  95. options.height = sizes[1];
  96.  
  97. };
  98.  
  99. wnd_dat.bottom_toolbar = function(oattr){
  100.  
  101. var attr = ({
  102. wrapper: wnd_dat.cell,
  103. width: '100%',
  104. height: '28px',
  105. bottom: '0px',
  106. left: '0px',
  107. name: 'tb_bottom',
  108. buttons: [
  109. {name: 'btn_cancel', text: 'Отмена', title: 'Закрыть без сохранения', width:'60px', float: 'right'},
  110. {name: 'btn_ok', b: 'Ок', title: 'Применить изменения', width:'30px', float: 'right'}
  111. ],
  112. onclick: function (name) {
  113. return false;
  114. }
  115. })._mixin(oattr),
  116.  
  117. tb_bottom = new OTooolBar(attr),
  118. sbar = wnd_dat.attachStatusBar({height: 12});
  119. sbar.style.zIndex = -1000;
  120. sbar.firstChild.style.backgroundColor = "transparent";
  121. sbar.firstChild.style.border = "none";
  122. return tb_bottom;
  123. };
  124.  
  125. if(attr.modal){
  126. if(attr.pwnd && attr.pwnd.setModal){
  127. attr.pwnd_modal = attr.pwnd.isModal();
  128. attr.pwnd.setModal(0);
  129. }
  130. wnd_dat.setModal(1);
  131. }
  132.  
  133. return wnd_dat;
  134. };
  135.  
  136. /**
  137. * обработчик выбора значения в свойствах (ссылочные типы)
  138. * вызывается в контексте this = pgrid
  139. * @param selv {*} выбранное значение
  140. */
  141. $p.iface.pgrid_on_select = function(selv){
  142.  
  143. if(selv===undefined)
  144. return;
  145.  
  146. var pgrid = this.grid instanceof dhtmlXGridObject ? this.grid : this,
  147. source = pgrid.getUserData("", "source"),
  148. f = pgrid.getSelectedRowId();
  149.  
  150. if(source.o[f] != undefined){
  151. if(typeof source.o[f] == "number")
  152. source.o[f] = $p.utils.fix_number(selv, true);
  153. else
  154. source.o[f] = selv;
  155.  
  156. }else if(f.indexOf("fprms") > -1){
  157. var row = $p._find(source.o.fprms, f.split("|")[1]);
  158. row.value = selv;
  159. }
  160.  
  161. pgrid.cells().setValue($p.utils.is_data_obj(selv) ? selv.presentation : selv || "");
  162.  
  163. if(source.grid_on_change)
  164. source.grid_on_change.call(pgrid, f, selv);
  165. };
  166.  
  167. /**
  168. * обработчик изменения значения в свойствах (примитивные типы)
  169. * @param pname {String} - имя измененного свойства
  170. * @param new_value {*} - новое значение
  171. * @param old_value {*} - предыдущее значение
  172. */
  173. $p.iface.pgrid_on_change = function(pname, new_value, old_value){
  174. if(pname)
  175. $p.iface.pgrid_on_select.call(this, new_value);
  176. };
  177.  
  178. /**
  179. * обработчик изменения флажка в свойствах (bit)
  180. * @param rId {String} - идентификатор строки
  181. * @param cInd {Number} - идентификатор колонки
  182. * @param state {Boolean} - состояние чекбокса
  183. */
  184. $p.iface.pgrid_on_checkbox = function(rId, cInd, state){
  185.  
  186. var pgrid = this.grid instanceof dhtmlXGridObject ? this.grid : this,
  187. source = pgrid.getUserData("", "source");
  188.  
  189. if(source.o[rId] != undefined)
  190. source.o[rId] = state;
  191.  
  192. if(source.grid_on_change)
  193. source.grid_on_change(rId, state);
  194. };
  195.  
  196.  
  197. function _clear_all(){
  198. $p.iface.docs.__define({
  199. clear_all: {
  200. value: function () {
  201. this.detachToolbar();
  202. this.detachStatusBar();
  203. this.detachObject(true);
  204. },
  205. enumerable: false
  206. },
  207. "Очистить": {
  208. get: function () {
  209. return this.clear_all;
  210. },
  211. enumerable: false
  212. },
  213. "Контейнер": {
  214. get: function () {
  215. return this.cell.querySelector(".dhx_cell_cont_layout");
  216. },
  217. enumerable: false
  218. }
  219. });
  220. }
  221.  
  222. /**
  223. * Создаёт форму авторизации с обработчиками перехода к фидбэку и настройкам,
  224. * полем входа под гостевой ролью, полями логина и пароля и кнопкой входа
  225. * @method frm_auth
  226. * @for InterfaceObjs
  227. * @param attr {Object} - параметры формы
  228. * @param [attr.cell] {dhtmlXCellObject}
  229. * @return {Promise}
  230. */
  231. $p.iface.frm_auth = function (attr, resolve, reject) {
  232.  
  233. if(!attr)
  234. attr = {};
  235.  
  236. var _cell, _frm, w, were_errors;
  237.  
  238. if(attr.modal_dialog){
  239. if(!attr.options)
  240. attr.options = {
  241. name: "frm_auth",
  242. caption: "Вход на сервер",
  243. width: 360,
  244. height: 300,
  245. center: true,
  246. allow_close: true,
  247. allow_minmax: true,
  248. modal: true
  249. };
  250. _cell = $p.iface.dat_blank(attr._dxw, attr.options);
  251. _cell.attachEvent("onClose",function(win){
  252. if(were_errors){
  253. if(reject)
  254. reject(err);
  255. }else if(resolve)
  256. resolve();
  257. return true;
  258. });
  259. _frm = _cell.attachForm();
  260.  
  261. }else{
  262. _cell = attr.cell || $p.iface.docs;
  263. _frm = $p.iface.auth = _cell.attachForm();
  264. $p.msg.show_msg($p.msg.init_login, _cell);
  265. }
  266.  
  267.  
  268. function do_auth(login, password){
  269. $p.ajax.username = login;
  270. $p.ajax.password = $p.aes.Ctr.encrypt(password);
  271.  
  272. if(login){
  273.  
  274. if($p.wsql.get_user_param("user_name") != login)
  275. $p.wsql.set_user_param("user_name", login); // сохраняем имя пользователя в базе
  276.  
  277. //$p.eve.log_in(attr.onstep)
  278. $p.wsql.pouch.log_in(login, password)
  279. .then(function () {
  280.  
  281. if($p.wsql.get_user_param("enable_save_pwd")){
  282. if($p.aes.Ctr.decrypt($p.wsql.get_user_param("user_pwd")) != password)
  283. $p.wsql.set_user_param("user_pwd", $p.aes.Ctr.encrypt(password)); // сохраняем имя пользователя в базе
  284. }else if($p.wsql.get_user_param("user_pwd") != "")
  285. $p.wsql.set_user_param("user_pwd", "");
  286.  
  287. $p.eve.logged_in = true;
  288. if(attr.modal_dialog)
  289. _cell.close();
  290. else if(resolve)
  291. resolve();
  292.  
  293. })
  294. .catch(function (err) {
  295. were_errors = true;
  296. _frm.onerror(err);
  297. })
  298. .then(function () {
  299. if($p.iface.sync)
  300. $p.iface.sync.close();
  301. if(_cell && _cell.progressOff){
  302. _cell.progressOff();
  303. if(!were_errors && attr.hide_header)
  304. _cell.hideHeader();
  305. }
  306. if($p.iface.cell_tree && !were_errors)
  307. $p.iface.cell_tree.expand();
  308. });
  309.  
  310. } else
  311. this.validate();
  312. }
  313.  
  314. // обработчик кнопки "войти" формы авторизации
  315. function auth_click(name){
  316.  
  317. were_errors = false;
  318. this.resetValidateCss();
  319.  
  320. if(this.getCheckedValue("type") == "guest"){
  321.  
  322. var login = this.getItemValue("guest"),
  323. password = "";
  324. if($p.job_prm.guests && $p.job_prm.guests.length){
  325. $p.job_prm.guests.some(function (g) {
  326. if(g.username == login){
  327. password = $p.aes.Ctr.decrypt(g.password);
  328. return true;
  329. }
  330. });
  331. }
  332. do_auth.call(this, login, password);
  333.  
  334. }else if(this.getCheckedValue("type") == "auth"){
  335. do_auth.call(this, this.getItemValue("login"), this.getItemValue("password"));
  336.  
  337. }
  338. }
  339.  
  340. // загружаем структуру формы
  341. _frm.loadStruct($p.injected_data["form_auth.xml"], function(){
  342.  
  343. var selected;
  344.  
  345. // если указан список гостевых пользователей
  346. if($p.job_prm.guests && $p.job_prm.guests.length){
  347.  
  348. var guests = $p.job_prm.guests.map(function (g) {
  349. var v = {
  350. text: g.username,
  351. value: g.username
  352. };
  353. if($p.wsql.get_user_param("user_name") == g.username){
  354. v.selected = true;
  355. selected = g.username;
  356. }
  357. return v;
  358. });
  359.  
  360. if(!selected){
  361. guests[0].selected = true;
  362. selected = guests[0].value;
  363. }
  364.  
  365. _frm.reloadOptions("guest", guests);
  366. }
  367.  
  368. // после готовности формы читаем пользователя из локальной датабазы
  369. if($p.wsql.get_user_param("user_name") && $p.wsql.get_user_param("user_name") != selected){
  370. _frm.setItemValue("login", $p.wsql.get_user_param("user_name"));
  371. _frm.setItemValue("type", "auth");
  372.  
  373. if($p.wsql.get_user_param("enable_save_pwd") && $p.wsql.get_user_param("user_pwd")){
  374. _frm.setItemValue("password", $p.aes.Ctr.decrypt($p.wsql.get_user_param("user_pwd")));
  375. }
  376. }
  377.  
  378. // позиционируем форму по центру
  379. if(!attr.modal_dialog){
  380. if((w = ((_cell.getWidth ? _cell.getWidth() : _cell.cell.offsetWidth) - 500)/2) >= 10)
  381. _frm.cont.style.paddingLeft = w.toFixed() + "px";
  382. else
  383. _frm.cont.style.paddingLeft = "20px";
  384. }
  385.  
  386. setTimeout(function () {
  387.  
  388. dhx4.callEvent("on_draw_auth", [_frm]);
  389.  
  390. 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"))))
  391. auth_click.call(_frm);
  392.  
  393. });
  394. });
  395.  
  396. // назначаем обработчик нажатия на кнопку
  397. _frm.attachEvent("onButtonClick", auth_click);
  398.  
  399. _frm.attachEvent("onKeyDown",function(inp, ev, name, value){
  400. if(ev.keyCode == 13){
  401. if(name == "password" || this.getCheckedValue("type") == "guest"){
  402. auth_click.call(this);
  403. }
  404. }
  405. });
  406.  
  407.  
  408. _frm.onerror = function (err) {
  409.  
  410. $p.ajax.authorized = false;
  411.  
  412. var emsg = err.message.toLowerCase();
  413.  
  414. if(emsg.indexOf("auth") != -1) {
  415. $p.msg.show_msg({
  416. title: $p.msg.main_title + $p.version,
  417. type: "alert-error",
  418. text: $p.msg.error_auth
  419. });
  420. _frm.setItemValue("password", "");
  421. _frm.validate();
  422.  
  423. }else if(emsg.indexOf("gateway") != -1 || emsg.indexOf("net") != -1) {
  424. $p.msg.show_msg({
  425. title: $p.msg.main_title + $p.version,
  426. type: "alert-error",
  427. text: $p.msg.error_network
  428. });
  429. }
  430. }
  431.  
  432.  
  433.  
  434. };
  435.  
  436.  
  437. /**
  438. * Служебная функция для открытия окна настроек из гиперссылки
  439. * @param e
  440. * @return {Boolean}
  441. */
  442. $p.iface.open_settings = function (e) {
  443. var evt = (e || (typeof event != "undefined" ? event : undefined));
  444. if(evt)
  445. evt.preventDefault();
  446.  
  447. var hprm = $p.job_prm.parse_url();
  448. $p.iface.set_hash(hprm.obj, hprm.ref, hprm.frm, "settings");
  449.  
  450. return $p.iface.cancel_bubble(evt);
  451. };
  452.  
  453. /**
  454. * Переключает вид формы между списком, календаарём и отчетами
  455. * @method swith_view
  456. * @for InterfaceObjs
  457. * @param name {String} - имя представления
  458. */
  459. $p.iface.swith_view = function(name){
  460.  
  461. var state,
  462. iface = $p.iface,
  463.  
  464. /**
  465. * Переключает состав элементов дерева
  466. * @param view
  467. */
  468. swith_tree = function(name){
  469.  
  470. function compare_text(a, b) {
  471. if (a.text > b.text) return 1;
  472. if (a.text < b.text) return -1;
  473. }
  474.  
  475. if(!iface.tree){
  476.  
  477. var hprm = $p.job_prm.parse_url();
  478. if(hprm.obj) {
  479. var parts = hprm.obj.split('.');
  480. if(parts.length > 1){
  481.  
  482. var mgr = $p.md.mgr_by_class_name(hprm.obj);
  483.  
  484. if(typeof iface.docs.close === "function" )
  485. iface.docs.close();
  486.  
  487. if(mgr)
  488. mgr.form_list(iface.docs, {});
  489. }
  490. }
  491. return;
  492.  
  493. }else if(iface.tree._view == name || ["rep", "cal"].indexOf(name) != -1)
  494. return;
  495.  
  496. iface.tree.deleteChildItems(0);
  497. if(name == "oper"){
  498. var meta_tree = {id:0, item:[
  499. {id:"oper_cat", text: $p.msg.meta_cat, open: true, item:[]},
  500. {id:"oper_doc", text: $p.msg.meta_doc, item:[]},
  501. {id:"oper_cch", text: $p.msg.meta_cch, item:[]},
  502. {id:"oper_cacc", text: $p.msg.meta_cacc, item:[]},
  503. {id:"oper_tsk", text: $p.msg.meta_tsk, item:[]}
  504. ]}, mdn, md,
  505.  
  506. // бежим по справочникам
  507. tlist = meta_tree.item[0].item;
  508. for(mdn in $p.cat){
  509. if(typeof $p.cat[mdn] == "function")
  510. continue;
  511. md = $p.cat[mdn].metadata();
  512. if(md.hide)
  513. continue;
  514. tlist.push({id: "oper.cat." + mdn, text: md.synonym || md.name, tooltip: md.illustration || md.list_presentation});
  515. }
  516. tlist.sort(compare_text);
  517.  
  518. // бежим по документам
  519. tlist = meta_tree.item[1].item;
  520. for(mdn in $p.doc){
  521. if(typeof $p.doc[mdn] == "function")
  522. continue;
  523. md = $p.doc[mdn].metadata();
  524. if(md.hide)
  525. continue;
  526. tlist.push({id: "oper.doc." + mdn, text: md.synonym || md.name, tooltip: md.illustration || md.list_presentation});
  527. }
  528. tlist.sort(compare_text);
  529.  
  530. // бежим по планам видов характеристик
  531. tlist = meta_tree.item[2].item;
  532. for(mdn in $p.cch){
  533. if(typeof $p.cch[mdn] == "function")
  534. continue;
  535. md = $p.cch[mdn].metadata();
  536. if(md.hide)
  537. continue;
  538. tlist.push({id: "oper.cch." + mdn, text: md.synonym || md.name, tooltip: md.illustration || md.list_presentation});
  539. }
  540. tlist.sort(compare_text);
  541.  
  542. // бежим по планам счетов
  543. tlist = meta_tree.item[3].item;
  544. for(mdn in $p.cacc){
  545. if(typeof $p.cacc[mdn] == "function")
  546. continue;
  547. md = $p.cacc[mdn].metadata();
  548. if(md.hide)
  549. continue;
  550. tlist.push({id: "oper.cacc." + mdn, text: md.synonym || md.name, tooltip: md.illustration || md.list_presentation});
  551. }
  552. tlist.sort(compare_text);
  553.  
  554. // бежим по задачам
  555. tlist = meta_tree.item[4].item;
  556. for(mdn in $p.tsk){
  557. if(typeof $p.tsk[mdn] == "function")
  558. continue;
  559. md = $p.tsk[mdn].metadata();
  560. if(md.hide)
  561. continue;
  562. tlist.push({id: "oper.tsk." + mdn, text: md.synonym || md.name, tooltip: md.illustration || md.list_presentation});
  563. }
  564. tlist.sort(compare_text);
  565.  
  566. iface.tree.parse(meta_tree, function(){
  567. var hprm = $p.job_prm.parse_url();
  568. if(hprm.obj){
  569. iface.tree.selectItem(hprm.view+"."+hprm.obj, true);
  570. }
  571. }, "json");
  572.  
  573. }else{
  574. iface.tree.loadXML(iface.tree.tree_filteres, function(){
  575.  
  576. });
  577.  
  578. }
  579.  
  580. iface.tree._view = name;
  581. };
  582.  
  583. if(name.indexOf(iface.docs.getViewName())==0)
  584. return iface.docs.getViewName();
  585.  
  586. state = iface.docs.showView(name);
  587. if (state == true) {
  588. // first call, init corresponding components
  589. // календарь
  590. if(name=="cal" && !window.dhtmlXScheduler){
  591. $p.load_script("dist/dhtmlxscheduler.min.js", "script", function(){
  592. //scheduler.config.xml_date="%Y-%m-%d %H:%i";
  593. scheduler.config.first_hour = 8;
  594. scheduler.config.last_hour = 22;
  595. iface.docs.scheduler = iface.docs.attachScheduler(new Date("2015-11-20"), "week", "scheduler_here");
  596. iface.docs.scheduler.attachEvent("onBeforeViewChange", function(old_mode, old_date, mode, date){
  597. if(mode == "timeline"){
  598. $p.msg.show_not_implemented();
  599. return false;
  600. }
  601. return true;
  602. });
  603. });
  604.  
  605. $p.load_script("dist/dhtmlxscheduler.css", "link");
  606.  
  607. //}else if(name=="rep"){
  608. // // подключаемый отчет
  609. //
  610. //}else if(name=="oper"){
  611. // // в дереве - список метаданных, в окне - список текущего метаданного
  612. //
  613.  
  614. }
  615. }
  616.  
  617. swith_tree(name);
  618.  
  619. if(name == "def")
  620. iface.main.showStatusBar();
  621. else
  622. iface.main.hideStatusBar();
  623. };
  624.  
  625.  
  626. /**
  627. * ### Визуальный компонент OTooolBar
  628. * Панель инструментов рисовалки и альтернативная панель инструментов прочих форм
  629. * - Гибкое управление размером, положением и выравниванием как самой панели, так и отдельных кнопок
  630. * - Кнопки и группы кнопок, иконы и текст
  631. * - Всплывающие подсказки с произвольным html
  632. *
  633. * @class OTooolBar
  634. * @param attr {Object} - параметры создаваемой панели - родитель, положение, размер и ориентация
  635. * @constructor
  636. * @menuorder 54
  637. * @tooltip Командная панель
  638. */
  639. function OTooolBar(attr){
  640. var _this = this,
  641. div = document.createElement('div'),
  642. offset, popup_focused, sub_focused, btn_focused;
  643.  
  644. if(!attr.image_path)
  645. attr.image_path = dhtmlx.image_path;
  646.  
  647. if(attr.hasOwnProperty("class_name"))
  648. div.className = attr.class_name;
  649. else
  650. div.className = 'md_otooolbar';
  651.  
  652. _this.cell = div;
  653.  
  654. _this.buttons = {};
  655.  
  656. function bselect(select){
  657. for(var i=0; i<div.children.length; i++){
  658. div.children[i].classList.remove('selected');
  659. }
  660. if(select && !this.classList.contains('selected'))
  661. this.classList.add('selected');
  662. }
  663.  
  664. function popup_hide(){
  665. popup_focused = false;
  666. setTimeout(function () {
  667. if(!popup_focused)
  668. $p.iface.popup.hide();
  669. }, 300);
  670. }
  671.  
  672. function btn_click(){
  673. if(attr.onclick)
  674. attr.onclick.call(_this, this.name.replace(attr.name + '_', ''), attr.name);
  675. }
  676.  
  677. /**
  678. * Добавляет кнопку на панель инструментов
  679. * @method add
  680. * @param battr {Object} - атрибуты создаваемой кнопки
  681. */
  682. this.add = function(battr){
  683.  
  684. var bdiv = $p.iface.add_button(div, attr, battr);
  685.  
  686. bdiv.onclick = btn_click;
  687.  
  688. bdiv.onmouseover = function(){
  689. if(battr.title && !battr.sub){
  690. popup_focused = true;
  691.  
  692. $p.iface.popup.clear();
  693. $p.iface.popup.attachHTML(battr.title);
  694. $p.iface.popup.show(dhx4.absLeft(bdiv), dhx4.absTop(bdiv), bdiv.offsetWidth, bdiv.offsetHeight);
  695.  
  696. $p.iface.popup.p.onmouseover = function(){
  697. popup_focused = true;
  698. };
  699.  
  700. $p.iface.popup.p.onmouseout = popup_hide;
  701.  
  702. if(attr.on_popup)
  703. attr.on_popup($p.iface.popup, bdiv);
  704. }
  705. };
  706.  
  707. bdiv.onmouseout = popup_hide;
  708.  
  709. _this.buttons[battr.name] = bdiv;
  710.  
  711. if(battr.sub){
  712.  
  713. function remove_sub(parent){
  714. if(!parent)
  715. parent = bdiv;
  716. if(parent.subdiv && !sub_focused && !btn_focused){
  717. while(parent.subdiv.firstChild)
  718. parent.subdiv.removeChild(parent.subdiv.firstChild);
  719. parent.subdiv.parentNode.removeChild(parent.subdiv);
  720. parent.subdiv = null;
  721. }
  722. }
  723.  
  724. bdiv.onmouseover = function(){
  725.  
  726. // нужно погасить сабдивы соседей
  727. for(var i=0; i<bdiv.parentNode.children.length; i++){
  728. if(bdiv.parentNode.children[i] != bdiv && bdiv.parentNode.children[i].subdiv){
  729. remove_sub(bdiv.parentNode.children[i]);
  730. break;
  731. }
  732. }
  733.  
  734. btn_focused = true;
  735.  
  736. if(!this.subdiv){
  737. this.subdiv = document.createElement('div');
  738. this.subdiv.className = 'md_otooolbar';
  739. offset = $p.iface.get_offset(bdiv);
  740. if(battr.sub.align == 'right')
  741. this.subdiv.style.left = (offset.left + bdiv.offsetWidth - (parseInt(battr.sub.width.replace(/\D+/g,"")) || 56)) + 'px';
  742. else
  743. this.subdiv.style.left = offset.left + 'px';
  744. this.subdiv.style.top = (offset.top + div.offsetHeight) + 'px';
  745. this.subdiv.style.height = battr.sub.height || '198px';
  746. this.subdiv.style.width = battr.sub.width || '56px';
  747. for(var i in battr.sub.buttons){
  748. var bsub = $p.iface.add_button(this.subdiv, attr, battr.sub.buttons[i]);
  749. bsub.onclick = btn_click;
  750. }
  751. attr.wrapper.appendChild(this.subdiv);
  752.  
  753. this.subdiv.onmouseover = function () {
  754. sub_focused = true;
  755. };
  756.  
  757. this.subdiv.onmouseout = function () {
  758. sub_focused = false;
  759. setTimeout(remove_sub, 500);
  760. };
  761.  
  762. if(battr.title)
  763. $p.iface.popup.show(dhx4.absLeft(this.subdiv), dhx4.absTop(this.subdiv), this.subdiv.offsetWidth, this.subdiv.offsetHeight);
  764. }
  765.  
  766. };
  767.  
  768. bdiv.onmouseout = function(){
  769. btn_focused = false;
  770. setTimeout(remove_sub, 500);
  771. }
  772. }
  773. };
  774.  
  775. /**
  776. * Выделяет кнопку по событию mouseover и снимает выделение с остальных кнопок
  777. * @method select
  778. * @param name {String} - имя текущей кнопки
  779. */
  780. this.select = function(name){
  781. for(var i=0; i<div.children.length; i++){
  782. var btn = div.children[i];
  783. if(btn.name == attr.name + '_' + name){
  784. bselect.call(btn, true);
  785. return;
  786. }
  787. }
  788. };
  789.  
  790. /**
  791. * Возвращает имя выделенной кнопки
  792. */
  793. this.get_selected = function () {
  794. for(var i=0; i<div.children.length; i++){
  795. var btn = div.children[i];
  796. if(btn.classList.contains('selected'))
  797. return btn.name;
  798. }
  799. };
  800.  
  801. /**
  802. * Деструктор объекта
  803. * @method unload
  804. */
  805. this.unload = function(){
  806. while(div.firstChild)
  807. div.removeChild(div.firstChild);
  808. attr.wrapper.removeChild(div);
  809. };
  810.  
  811.  
  812. attr.wrapper.appendChild(div);
  813. div.style.width = attr.width || '28px';
  814. div.style.height = attr.height || '150px';
  815. div.style.position = 'absolute';
  816.  
  817. if(attr.top) div.style.top = attr.top;
  818. if(attr.left) div.style.left = attr.left;
  819. if(attr.bottom) div.style.bottom = attr.bottom;
  820. if(attr.right) div.style.right = attr.right;
  821. if(attr.paddingRight) div.style.paddingRight = attr.paddingRight;
  822. if(attr.paddingLeft) div.style.paddingLeft = attr.paddingLeft;
  823.  
  824. if(attr.buttons)
  825. attr.buttons.forEach(function(battr){
  826. _this.add(battr);
  827. });
  828.  
  829. };
  830. $p.iface.OTooolBar = OTooolBar;
  831.  
  832. /**
  833. * Добавляет кнопку на панель инструментов
  834. * @method add_button
  835. * @for InterfaceObjs
  836. * @param parent {Element}
  837. * @param attr {Object}
  838. * @param battr {Object}
  839. * @returns {Element}
  840. */
  841. $p.iface.add_button = function(parent, attr, battr) {
  842. var bdiv = document.createElement('div'), html = '';
  843. bdiv.name = (attr ? attr.name + '_' : '') + battr.name;
  844. parent.appendChild(bdiv);
  845.  
  846. // если имя начинается с sep_ - это разделитель
  847. bdiv.className = (battr.name.indexOf("sep_") == 0) ? 'md_otooolbar_sep' : 'md_otooolbar_button';
  848. if(battr.hasOwnProperty("class_name"))
  849. bdiv.classList.add(battr.class_name);
  850.  
  851. if(battr.img)
  852. html = '<img src="' + (attr ? attr.image_path : '') + battr.img + '">';
  853. if(battr.b)
  854. html +='<b style="vertical-align: super;"> ' + battr.b + '</b>';
  855. else if(battr.text)
  856. html +='<span style="vertical-align: super;"> ' + battr.text + '</span>';
  857. else if(battr.css)
  858. bdiv.classList.add(battr.css);
  859. bdiv.innerHTML = html;
  860.  
  861. if(battr.float) bdiv.style.float = battr.float;
  862. if(battr.clear) bdiv.style.clear = battr.clear;
  863. if(battr.width) bdiv.style.width = battr.width;
  864. if(battr.paddingRight) bdiv.style.paddingRight = battr.paddingRight;
  865. if(battr.paddingLeft) bdiv.style.paddingLeft = battr.paddingLeft;
  866.  
  867. if(battr.tooltip)
  868. bdiv.title = battr.tooltip;
  869.  
  870. return bdiv;
  871. };
  872.