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

Показывать:
  1. /**
  2. * Расширение типов ячеек dhtmlXGrid
  3. *
  4. * © Evgeniy Malyarov http://www.oknosoft.ru 2014-2016
  5. *
  6. * Экспортирует конструкторы:
  7. * * **eXcell_ref** - поля ввода значений ссылочных типов
  8. * * **eXcell_refc** - комбобокс ссылочных типов (перечисления и короткие справочники)
  9. *
  10. * @module wdg_dhtmlx
  11. * @requires common
  12. */
  13.  
  14. // Прототип кустомных ячеек для грида
  15. var eXcell_proto = new eXcell();
  16.  
  17. /**
  18. * Обработчик клавиш {tab}, {enter} и {F4} в поле ввода
  19. */
  20. eXcell_proto.input_keydown = function(e, t){
  21.  
  22. function obj_on_select(v){
  23. if(t.source.on_select)
  24. t.source.on_select.call(t.source, v);
  25. }
  26.  
  27. if(e.keyCode === 8 || e.keyCode === 46){ // по {del} и {bs} очищаем значение
  28. t.setValue("");
  29. t.grid.editStop();
  30. if(t.source.on_select)
  31. t.source.on_select.call(t.source, "");
  32.  
  33. }else if(e.keyCode === 9 || e.keyCode === 13)
  34. t.grid.editStop(); // по {tab} и {enter} заканчиваем редактирование
  35.  
  36. else if(e.keyCode === 115)
  37. t.cell.firstChild.childNodes[1].onclick(e); // по {F4} открываем редактор
  38.  
  39. else if(e.keyCode === 113){ // по {F2} открываем форму объекта
  40. if(t.source.tabular_section){
  41. t.mgr = _md.value_mgr(t.source.row, t.source.col, t.source.row._metadata.fields[t.source.col].type);
  42. if(t.mgr){
  43. var tv = t.source.row[t.source.col];
  44. t.mgr.form_obj(t.source.wnd, {
  45. o: tv,
  46. on_select: obj_on_select
  47. });
  48. }
  49.  
  50. }else if(t.fpath.length==1){
  51. t.mgr = _md.value_mgr(t.source.o._obj, t.fpath[0], t.source.o._metadata.fields[t.fpath[0]].type);
  52. if(t.mgr){
  53. var tv = t.source.o[t.fpath[0]];
  54. t.mgr.form_obj(t.source.wnd, {
  55. o: tv,
  56. on_select: obj_on_select
  57. });
  58. }
  59. }
  60. }
  61.  
  62. return $p.iface.cancel_bubble(e);
  63. };
  64.  
  65. /**
  66. * Конструктор поля ввода со списком OCombo
  67. * @param cell
  68. */
  69. function eXcell_ocombo(cell){
  70.  
  71. if (!cell)
  72. return;
  73.  
  74. var t = this;
  75.  
  76. t.cell = cell;
  77. t.grid = cell.parentNode.grid;
  78.  
  79. /**
  80. * устанавливает текст в ячейке. например, this.setCValue("<input type='button' value='"+val+"'>",val);
  81. */
  82. t.setValue=function(val){
  83. t.setCValue(val instanceof DataObj ? val.presentation : (val || ""));
  84. };
  85.  
  86. /**
  87. * получает значение ячейки из табличной части или поля объекта или допполя допобъекта, а не из грида
  88. */
  89. t.getValue=function(){
  90. return t.grid.get_cell_value();
  91.  
  92. };
  93.  
  94. /**
  95. * Обрабатывает событие перехода к следующему полю (окончание редактирования)
  96. */
  97. t.shiftNext = function () {
  98. t.grid.editStop();
  99. };
  100.  
  101. /**
  102. * Cоздаёт элементы управления редактора и назначает им обработчики
  103. */
  104. t.edit=function(){
  105.  
  106. if(t.combo)
  107. return;
  108.  
  109. t.val = t.getValue(); //save current value
  110. t.cell.innerHTML = "";
  111. t.combo = new OCombo({
  112. parent: t.cell
  113. }._mixin(t.grid.get_cell_field()));
  114. t.combo.getInput().focus();
  115. };
  116.  
  117. /**
  118. * вызывается при отключении редактора
  119. * @return {boolean} - если "истина", значит объект был изменён
  120. */
  121. t.detach=function(){
  122. if(t.combo){
  123.  
  124. if(t.combo.getComboText){
  125. t.setValue(t.combo.getComboText()); // текст в элементе управления
  126. if(!t.combo.getSelectedValue())
  127. t.combo.callEvent("onChange");
  128. var res = !$p.utils.is_equal(t.val, t.getValue());// compares the new and the old values
  129. t.combo.unload();
  130. return res;
  131.  
  132. } else if(t.combo.unload){
  133. t.combo.unload();
  134. }
  135. }
  136. return true;
  137. }
  138. }
  139. eXcell_ocombo.prototype = eXcell_proto;
  140. window.eXcell_ocombo = eXcell_ocombo;
  141.  
  142. /**
  143. * Конструктор поля ввода значений ссылочных типов для грида
  144. * @param cell
  145. */
  146. window.eXcell_ref = eXcell_ocombo;
  147.  
  148. /**
  149. * Конструктор комбобокса кешируемых ссылочных типов для грида
  150. */
  151. window.eXcell_refc = eXcell_ocombo;
  152.  
  153. /**
  154. * Конструктор поля пароля
  155. */
  156. function eXcell_pwd(cell){ //the eXcell name is defined here
  157.  
  158. var fnedit;
  159. if (cell){ //the default pattern, just copy it
  160. this.cell = cell;
  161. this.grid = cell.parentNode.grid;
  162. eXcell_ed.call(this); //uses methods of the "ed" type
  163. fnedit = this.edit;
  164. this.edit = function(){
  165. fnedit.call(this);
  166. this.obj.type="password";
  167. };
  168. this.setValue=function(){
  169. this.setCValue("*********");
  170. };
  171. this.getValue=function(){
  172. return this.grid.get_cell_value();
  173.  
  174. };
  175. this.detach=function(){
  176. if(this.grid.get_cell_field){
  177. var cf = this.grid.get_cell_field();
  178. cf.obj[cf.field] = this.obj.value;
  179. }
  180. this.setValue();
  181. fnedit = null;
  182. return this.val != this.getValue();
  183. }
  184. }
  185. }
  186. eXcell_pwd.prototype = eXcell_proto;
  187. window.eXcell_pwd = eXcell_pwd;
  188.  
  189.  
  190. dhtmlXCalendarObject.prototype._dateToStr = function(val, format) {
  191. if(val instanceof Date && val.getFullYear() < 1000)
  192. return "";
  193. else
  194. return window.dhx4.date2str(val, format||this._dateFormat, this._dateStrings());
  195. };
  196.  
  197. eXcell_dhxCalendar.prototype.edit = function() {
  198.  
  199. var arPos = this.grid.getPosition(this.cell);
  200. this.grid._grid_calendarA._show(false, false);
  201. this.grid._grid_calendarA.setPosition(arPos[0],arPos[1]+this.cell.offsetHeight);
  202. this.grid._grid_calendarA._last_operation_calendar = false;
  203.  
  204.  
  205. this.grid.callEvent("onCalendarShow", [this.grid._grid_calendarA, this.cell.parentNode.idd, this.cell._cellIndex]);
  206. this.cell._cediton = true;
  207. this.val = this.cell.val;
  208. if(this.val instanceof Date && this.val.getFullYear() < 1000)
  209. this.val = new Date();
  210. this._val = this.cell.innerHTML;
  211. var t = this.grid._grid_calendarA.draw;
  212. this.grid._grid_calendarA.draw = function(){};
  213. this.grid._grid_calendarA.setDateFormat((this.grid._dtmask||"%d.%m.%Y"));
  214. this.grid._grid_calendarA.setDate(this.val||(new Date()));
  215. this.grid._grid_calendarA.draw = t;
  216.  
  217. };
  218.  
  219. /**
  220. * fix ajax
  221. */
  222. (function(){
  223.  
  224. function fix_auth(t, method, url, async){
  225. if(url.indexOf("odata/standard.odata") != -1 || url.indexOf("/hs/rest") != -1){
  226. var username, password;
  227. if($p.ajax.authorized){
  228. username = $p.ajax.username;
  229. password = $p.aes.Ctr.decrypt($p.ajax.password);
  230. }else{
  231. if($p.job_prm.guest_name){
  232. username = $p.job_prm.guest_name;
  233. password = $p.aes.Ctr.decrypt($p.job_prm.guest_pwd);
  234. }else{
  235. username = $p.wsql.get_user_param("user_name");
  236. password = $p.aes.Ctr.decrypt($p.wsql.get_user_param("user_pwd"));
  237. }
  238. }
  239. t.open(method, url, async, username, password);
  240. t.withCredentials = true;
  241. t.setRequestHeader("Authorization", "Basic " +
  242. btoa(unescape(encodeURIComponent(username + ":" + password))));
  243. }else
  244. t.open(method, url, async);
  245. }
  246.  
  247. dhx4.ajax._call = function(method, url, postData, async, onLoad, longParams, headers) {
  248.  
  249. var t = (window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"));
  250. var isQt = (navigator.userAgent.match(/AppleWebKit/) != null && navigator.userAgent.match(/Qt/) != null && navigator.userAgent.match(/Safari/) != null);
  251.  
  252. if (async == true) {
  253. t.onreadystatechange = function() {
  254. if ((t.readyState == 4) || (isQt == true && t.readyState == 3)) { // what for long response and status 404?
  255. if (t.status != 200 || t.responseText == "")
  256. if (!dhx4.callEvent("onAjaxError", [{xmlDoc:t, filePath:url, async:async}])) return;
  257.  
  258. window.setTimeout(function(){
  259. if (typeof(onLoad) == "function") {
  260. onLoad.apply(window, [{xmlDoc:t, filePath:url, async:async}]); // dhtmlx-compat, response.xmlDoc.responseXML/responseText
  261. }
  262. if (longParams != null) {
  263. if (typeof(longParams.postData) != "undefined") {
  264. dhx4.ajax.postLong(longParams.url, longParams.postData, onLoad);
  265. } else {
  266. dhx4.ajax.getLong(longParams.url, onLoad);
  267. }
  268. }
  269. onLoad = null;
  270. t = null;
  271. },1);
  272. }
  273. }
  274. }
  275.  
  276. if (method == "GET") {
  277. url += this._dhxr(url);
  278. }
  279.  
  280. t.open(method, url, async);
  281.  
  282. // если обращение по rest или irest, добавляем авторизацию
  283. fix_auth(t, method, url, async);
  284.  
  285. if (headers != null) {
  286. for (var key in headers) t.setRequestHeader(key, headers[key]);
  287. } else if (method == "POST" || method == "PUT" || method == "DELETE") {
  288. t.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
  289. } else if (method == "GET") {
  290. postData = null;
  291. }
  292.  
  293. t.setRequestHeader("X-Requested-With", "XMLHttpRequest");
  294.  
  295. t.send(postData);
  296.  
  297. if (async != true) {
  298. if ((t.readyState == 4) || (isQt == true && t.readyState == 3)) {
  299. if (t.status != 200 || t.responseText == "") dhx4.callEvent("onAjaxError", [{xmlDoc:t, filePath:url, async:async}]);
  300. }
  301. }
  302.  
  303. return {xmlDoc:t, filePath:url, async:async}; // dhtmlx-compat, response.xmlDoc.responseXML/responseText
  304.  
  305. };
  306.  
  307. dhtmlx.ajax.prototype.send = function(url,params,call){
  308. var x=this.getXHR();
  309. if (typeof call == "function")
  310. call = [call];
  311. //add extra params to the url
  312. if (typeof params == "object"){
  313. var t=[];
  314. for (var a in params){
  315. var value = params[a];
  316. if (value === null || value === dhtmlx.undefined)
  317. value = "";
  318. t.push(a+"="+encodeURIComponent(value));// utf-8 escaping
  319. }
  320. params=t.join("&");
  321. }
  322. if (params && !this.post){
  323. url=url+(url.indexOf("?")!=-1 ? "&" : "?")+params;
  324. params=null;
  325. }
  326.  
  327. //x.open(this.post?"POST":"GET",url,!this._sync);
  328. fix_auth(x, this.post?"POST":"GET",url,!this._sync);
  329.  
  330. if (this.post)
  331. x.setRequestHeader('Content-type','application/x-www-form-urlencoded');
  332.  
  333. //async mode, define loading callback
  334. //if (!this._sync){
  335. var self=this;
  336. x.onreadystatechange= function(){
  337. if (!x.readyState || x.readyState == 4){
  338. //dhtmlx.log_full_time("data_loading"); //log rendering time
  339. if (call && self)
  340. for (var i=0; i < call.length; i++) //there can be multiple callbacks
  341. if (call[i])
  342. call[i].call((self.master||self),x.responseText,x.responseXML,x);
  343. self.master=null;
  344. call=self=null; //anti-leak
  345. }
  346. };
  347. //}
  348.  
  349. x.send(params||null);
  350. return x; //return XHR, which can be used in case of sync. mode
  351. }
  352.  
  353. })();
  354.  
  355. /**
  356. * Проверяет, видна ли ячейка
  357. * TODO: учесть слой, модальность и т.д.
  358. */
  359. dhtmlXCellObject.prototype.is_visible = function () {
  360. var rect = this.cell.getBoundingClientRect();
  361. return rect.right > 0 && rect.bottom > 0;
  362. };
  363.  
  364.  
  365. $p.iface.data_to_grid = function (data, attr){
  366.  
  367. if(this.data_to_grid)
  368. return this.data_to_grid(data, attr);
  369.  
  370. function cat_picture_class(r){
  371. var res;
  372. if(r.hasOwnProperty("posted")){
  373. res = r.posted ? "cell_doc_posted" : "cell_doc";
  374. }else{
  375. res = r.is_folder ? "cell_ref_folder" : "cell_ref_elm";
  376. }
  377.  
  378. if(r._deleted)
  379. res = res + "_deleted";
  380. return res ;
  381. }
  382.  
  383. function do_format(v){
  384.  
  385. if(v instanceof Date){
  386. if(v.getHours() || v.getMinutes())
  387. return $p.moment(v).format($p.moment._masks.date_time);
  388. else
  389. return $p.moment(v).format($p.moment._masks.date);
  390.  
  391. }else
  392. return typeof v == "number" ? v : $p.iface.normalize_xml(v || "");
  393. }
  394.  
  395. var xml = "<?xml version='1.0' encoding='UTF-8'?><rows total_count='%1' pos='%2' set_parent='%3'>"
  396. .replace("%1", attr._total_count || data.length).replace("%2", attr.start)
  397. .replace("%3", attr.set_parent || "" ),
  398. caption = this.caption_flds(attr);
  399.  
  400. // при первом обращении к методу добавляем описание колонок
  401. xml += caption.head;
  402.  
  403. data.forEach(function(r){
  404. xml += "<row id=\"" + r.ref + "\"><cell class=\"" + cat_picture_class(r) + "\">" + do_format(r[caption.acols[0].id]) + "</cell>";
  405. for(var col=1; col < caption.acols.length; col++ )
  406. xml += "<cell>" + do_format(r[caption.acols[col].id]) + "</cell>";
  407.  
  408. xml += "</row>";
  409. });
  410.  
  411. return xml + "</rows>";
  412. };
  413.  
  414. /**
  415. * Создаёт иерархический объект для построения dhtmlxTreeView
  416. * @param data
  417. * @return {*[]}
  418. */
  419. $p.iface.data_to_tree = function (data) {
  420.  
  421. var res = [{id: $p.utils.blank.guid, text: "..."}];
  422.  
  423. function add_hierarchically(arr, row){
  424. var curr = {id: row.ref, text: row.presentation, items: []};
  425. arr.push(curr);
  426. $p._find_rows(data, {parent: row.ref}, function(r){
  427. add_hierarchically(curr.items, r);
  428. });
  429. if(!curr.items.length)
  430. delete curr.items;
  431. }
  432. $p._find_rows(data, {parent: $p.utils.blank.guid}, function(r){
  433. add_hierarchically(res, r);
  434. });
  435.  
  436. return res;
  437. };
  438.  
  439.  
  440.