Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

11 лет назад
11 лет назад
11 лет назад
11 лет назад
11 лет назад
11 лет назад
11 лет назад
11 лет назад
11 лет назад
11 лет назад
12 лет назад
12 лет назад
12 лет назад
12 лет назад
12 лет назад
12 лет назад
12 лет назад
12 лет назад
12 лет назад
12 лет назад
12 лет назад
11 лет назад
11 лет назад
11 лет назад
11 лет назад
11 лет назад
11 лет назад
11 лет назад
11 лет назад
11 лет назад
11 лет назад
11 лет назад
11 лет назад
11 лет назад
11 лет назад
11 лет назад
12 лет назад
11 лет назад
11 лет назад
12 лет назад
11 лет назад
12 лет назад
12 лет назад
12 лет назад
12 лет назад
11 лет назад
11 лет назад
11 лет назад
12 лет назад
12 лет назад
12 лет назад
12 лет назад
12 лет назад
11 лет назад
11 лет назад
11 лет назад
11 лет назад
11 лет назад
11 лет назад
11 лет назад
11 лет назад
11 лет назад
12 лет назад
12 лет назад
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  1. // Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
  2. // MIT License. See license.txt
  3. wn.ui.form.Grid = Class.extend({
  4. init: function(opts) {
  5. $.extend(this, opts);
  6. this.fieldinfo = {};
  7. this.doctype = this.df.options;
  8. },
  9. make: function() {
  10. var me = this;
  11. this.wrapper = $('<div>\
  12. <div class="" style="border: 2px solid #c7c7c7; margin-bottom: 15px; border-radius: 3px;">\
  13. <div class="grid-heading-row" style="font-size: 15px; background-color: #f9f9f9;"></div>\
  14. <div class="panel-body">\
  15. <div class="rows"></div>\
  16. <div class="small">\
  17. <a href="#" class="grid-add-row pull-right">+ '+wn._("Add new row")+'.</a>\
  18. <span class="text-muted pull-right" style="margin-right: 5px;">' + wn._("Click on row to view / edit.") + '</span>\
  19. <div class="clearfix"></div>\
  20. </div>\
  21. </div>\
  22. </div>\
  23. </div>').appendTo(this.parent);
  24. $(this.wrapper).find(".grid-add-row").click(function() {
  25. me.add_new_row(null, null, true);
  26. return false;
  27. })
  28. },
  29. make_head: function() {
  30. // labels
  31. this.header_row = new wn.ui.form.GridRow({
  32. parent: $(this.parent).find(".grid-heading-row"),
  33. parent_df: this.df,
  34. docfields: this.docfields,
  35. frm: this.frm,
  36. grid: this
  37. });
  38. },
  39. refresh: function(force) {
  40. !this.wrapper && this.make();
  41. var me = this,
  42. $rows = $(me.parent).find(".rows"),
  43. data = this.get_data();
  44. this.docfields = wn.meta.get_docfields(this.doctype, this.frm.docname);
  45. this.display_status = wn.perm.get_field_display_status(this.df, this.frm.doc,
  46. this.perm);
  47. if(this.display_status==="None") return;
  48. if(!force && this.data_rows_are_same(data)) {
  49. // soft refresh
  50. this.header_row.refresh();
  51. for(var i in this.grid_rows) {
  52. this.grid_rows[i].refresh();
  53. }
  54. } else {
  55. // redraw
  56. this.wrapper.find(".grid-row").remove();
  57. this.make_head();
  58. this.grid_rows = [];
  59. this.grid_rows_by_docname = {};
  60. for(var ri in data) {
  61. var d = data[ri];
  62. var grid_row = new wn.ui.form.GridRow({
  63. parent: $rows,
  64. parent_df: this.df,
  65. docfields: this.docfields,
  66. doc: d,
  67. frm: this.frm,
  68. grid: this
  69. });
  70. this.grid_rows.push(grid_row)
  71. this.grid_rows_by_docname[d.name] = grid_row;
  72. }
  73. this.wrapper.find(".grid-add-row").toggle(this.can_add_rows());
  74. if(this.is_editable()) {
  75. this.make_sortable($rows);
  76. }
  77. this.last_display_status = this.display_status;
  78. this.last_docname = this.frm.docname;
  79. }
  80. },
  81. refresh_row: function(docname) {
  82. this.grid_rows_by_docname[docname] &&
  83. this.grid_rows_by_docname[docname].refresh();
  84. },
  85. data_rows_are_same: function(data) {
  86. if(this.grid_rows) {
  87. var same = data.length==this.grid_rows.length
  88. && this.display_status==this.last_display_status
  89. && this.frm.docname==this.last_docname
  90. && !$.map(this.grid_rows, function(g, i) {
  91. return (g.doc && g.doc.name==data[i].name) ? null : true;
  92. }).length;
  93. return same;
  94. }
  95. },
  96. make_sortable: function($rows) {
  97. var me =this;
  98. $rows.sortable({
  99. handle: ".data-row, .panel-heading",
  100. update: function(event, ui) {
  101. $rows.find(".grid-row").each(function(i, item) {
  102. var doc = $(item).data("doc");
  103. doc.idx = i + 1;
  104. $(this).find(".row-index").html(i + 1);
  105. me.frm.dirty();
  106. })
  107. }
  108. });
  109. },
  110. get_data: function() {
  111. var data = wn.model.get(this.df.options, {
  112. "parenttype": this.frm.doctype,
  113. "parentfield": this.df.fieldname,
  114. "parent": this.frm.docname
  115. });
  116. data.sort(function(a, b) { return a.idx - b.idx});
  117. return data;
  118. },
  119. set_column_disp: function(fieldname, show) {
  120. if($.isArray(fieldname)) {
  121. var me = this;
  122. $.each(fieldname, function(i, fname) {
  123. wn.meta.get_docfield(me.doctype, fname, me.frm.docname).hidden = show ? 0 : 1;
  124. });
  125. } else {
  126. wn.meta.get_docfield(this.doctype, fieldname, this.frm.docname).hidden = show ? 0 : 1;
  127. }
  128. this.refresh(true);
  129. },
  130. toggle_reqd: function(fieldname, reqd) {
  131. wn.meta.get_docfield(this.doctype, fieldname, this.frm.docname).reqd = reqd;
  132. this.refresh();
  133. },
  134. get_field: function(fieldname) {
  135. // Note: workaround for get_query
  136. if(!this.fieldinfo[fieldname])
  137. this.fieldinfo[fieldname] = {
  138. }
  139. return this.fieldinfo[fieldname];
  140. },
  141. set_value: function(fieldname, value, doc) {
  142. if(this.display_status!=="None")
  143. this.grid_rows_by_docname[doc.name].refresh_field(fieldname);
  144. },
  145. add_new_row: function(idx, callback, show) {
  146. if(this.is_editable()) {
  147. var d = wn.model.add_child(this.frm.doc, this.df.options, this.df.fieldname, idx);
  148. this.frm.script_manager.trigger(this.df.fieldname + "_add", d.doctype, d.name);
  149. this.refresh();
  150. if(show) {
  151. if(idx) {
  152. this.wrapper.find("[data-idx='"+idx+"']").data("grid_row")
  153. .toggle_view(true, callback);
  154. } else {
  155. this.wrapper.find(".grid-row:last").data("grid_row").toggle_view(true, callback);
  156. }
  157. }
  158. }
  159. },
  160. is_editable: function() {
  161. return this.display_status=="Write" && !this.static_rows
  162. },
  163. can_add_rows: function() {
  164. return this.is_editable() && !this.cannot_add_rows
  165. }
  166. });
  167. wn.ui.form.GridRow = Class.extend({
  168. init: function(opts) {
  169. $.extend(this, opts);
  170. this.show = false;
  171. this.make();
  172. },
  173. make: function() {
  174. var me = this;
  175. this.wrapper = $('<div class="grid-row"></div>').appendTo(this.parent).data("grid_row", this);
  176. this.row = $('<div class="data-row" style="min-height: 26px;"></div>').appendTo(this.wrapper)
  177. .on("click", function() {
  178. me.toggle_view();
  179. return false;
  180. });
  181. this.divider = $('<div class="divider row"></div>').appendTo(this.wrapper);
  182. this.set_row_index();
  183. this.make_static_display();
  184. if(this.doc) {
  185. this.set_data();
  186. }
  187. },
  188. set_row_index: function() {
  189. if(this.doc) {
  190. this.wrapper
  191. .attr("data-idx", this.doc.idx)
  192. .find(".row-index").html(this.doc.idx)
  193. }
  194. },
  195. remove: function() {
  196. if(this.grid.is_editable()) {
  197. var me = this;
  198. me.wrapper.toggle(false);
  199. wn.model.clear_doc(me.doc.doctype, me.doc.name);
  200. me.frm.script_manager.trigger(me.grid.df.fieldname + "_remove", me.doc.doctype, me.doc.name);
  201. me.frm.dirty();
  202. me.grid.refresh();
  203. }
  204. },
  205. insert: function(show) {
  206. var idx = this.doc.idx;
  207. this.toggle_view(false);
  208. this.grid.add_new_row(idx, null, show);
  209. },
  210. refresh: function() {
  211. if(this.doc)
  212. this.doc = locals[this.doc.doctype][this.doc.name];
  213. // re write columns
  214. this.grid.static_display_template = null;
  215. this.make_static_display();
  216. // refersh form fields
  217. if(this.show) {
  218. this.layout.refresh(this.doc);
  219. }
  220. },
  221. make_static_display: function() {
  222. var me = this;
  223. this.make_static_display_template();
  224. this.row.empty();
  225. $('<div class="col col-xs-1 row-index">' + (this.doc ? this.doc.idx : "#")+ '</div>')
  226. .appendTo(this.row);
  227. for(var ci in this.static_display_template) {
  228. var df = this.static_display_template[ci][0];
  229. var colsize = this.static_display_template[ci][1];
  230. var txt = this.doc ?
  231. wn.format(this.doc[df.fieldname], df, null, this.doc) :
  232. wn._(df.label);
  233. if(this.doc && df.fieldtype === "Select") {
  234. txt = wn._(txt);
  235. }
  236. var add_class = (["Text", "Small Text"].indexOf(df.fieldtype)===-1) ?
  237. " grid-overflow-ellipsis" : " grid-overflow-no-ellipsis";
  238. add_class += (["Int", "Currency", "Float"].indexOf(df.fieldtype)!==-1) ?
  239. " text-right": "";
  240. $col = $('<div class="col col-xs-'+colsize+add_class+'"></div>')
  241. .html(txt)
  242. .attr("data-fieldname", df.fieldname)
  243. .data("df", df)
  244. .appendTo(this.row)
  245. if(!this.doc) $col.css({"font-weight":"bold"})
  246. }
  247. // TODO find a better solution
  248. // append button column
  249. if(this.doc && this.grid.is_editable()) {
  250. if(!this.grid.$row_actions) {
  251. this.grid.$row_actions = $('<div class="col-md-1 pull-right" \
  252. style="text-align: right; padding-right: 5px;">\
  253. <span class="text-success grid-insert-row" style="padding: 4px;">\
  254. <i class="icon icon-plus-sign"></i></span>\
  255. <span class="grid-delete-row" style="padding: 4px;">\
  256. <i class="icon icon-trash"></i></span>\
  257. </div>');
  258. }
  259. $col = this.grid.$row_actions.clone().appendTo(this.row);
  260. if($col.width() < 50) {
  261. $col.toggle(false);
  262. } else {
  263. $col.toggle(true);
  264. $col.find(".grid-insert-row").click(function() { me.insert(); return false; });
  265. $col.find(".grid-delete-row").click(function() { me.remove(); return false; });
  266. }
  267. }
  268. $(this.frm.wrapper).trigger("grid-row-render", [this]);
  269. },
  270. make_static_display_template: function() {
  271. if(this.static_display_template) return;
  272. var total_colsize = 1;
  273. this.static_display_template = [];
  274. for(var ci in this.docfields) {
  275. var df = this.docfields[ci];
  276. if(!df.hidden && df.in_list_view && this.grid.frm.get_perm(df.permlevel, "read")
  277. && !in_list(["Section Break", "Column Break"], df.fieldtype)) {
  278. var colsize = 2;
  279. switch(df.fieldtype) {
  280. case "Text":
  281. case "Small Text":
  282. colsize = 3;
  283. break;
  284. case "Check":
  285. colsize = 1;
  286. break;
  287. }
  288. total_colsize += colsize
  289. if(total_colsize > 11)
  290. return false;
  291. this.static_display_template.push([df, colsize]);
  292. }
  293. }
  294. // redistribute if total-col size is less than 12
  295. var passes = 0;
  296. while(total_colsize < 11 && passes < 10) {
  297. for(var i in this.static_display_template) {
  298. var df = this.static_display_template[i][0];
  299. var colsize = this.static_display_template[i][1];
  300. if(colsize>1 && colsize<12 && ["Int", "Currency", "Float"].indexOf(df.fieldtype)===-1) {
  301. this.static_display_template[i][1] += 1;
  302. total_colsize++;
  303. }
  304. if(total_colsize >= 11)
  305. break;
  306. }
  307. passes++;
  308. }
  309. },
  310. toggle_view: function(show, callback) {
  311. if(!this.doc) return this;
  312. this.doc = locals[this.doc.doctype][this.doc.name];
  313. // hide other
  314. var open_row = $(".grid-row-open").data("grid_row");
  315. this.fields = [];
  316. this.fields_dict = {};
  317. this.show = show===undefined ?
  318. show = !this.show :
  319. show
  320. // call blur
  321. document.activeElement && document.activeElement.blur()
  322. if(show && open_row) {
  323. if(open_row==this) {
  324. // already open, do nothing
  325. callback();
  326. return;
  327. } else {
  328. // close other views
  329. open_row.toggle_view(false);
  330. }
  331. }
  332. this.wrapper.toggleClass("grid-row-open", this.show);
  333. if(this.show) {
  334. if(!this.form_panel) {
  335. this.form_panel = $('<div class="panel panel-warning" style="display: none;"></div>')
  336. .insertBefore(this.divider);
  337. }
  338. this.render_form();
  339. this.row.toggle(false);
  340. this.form_panel.toggle(true);
  341. if(this.frm.doc.docstatus===0)
  342. this.form_area.find(":input:first").focus();
  343. } else {
  344. if(this.form_panel)
  345. this.form_panel.toggle(false);
  346. this.row.toggle(true);
  347. this.make_static_display();
  348. }
  349. callback && callback();
  350. return this;
  351. },
  352. open_prev: function() {
  353. if(this.grid.grid_rows[this.doc.idx-2]) {
  354. this.grid.grid_rows[this.doc.idx-2].toggle_view(true);
  355. }
  356. },
  357. open_next: function() {
  358. if(this.grid.grid_rows[this.doc.idx]) {
  359. this.grid.grid_rows[this.doc.idx].toggle_view(true);
  360. } else {
  361. this.grid.add_new_row(null, null, true);
  362. }
  363. },
  364. toggle_add_delete_button_display: function($parent) {
  365. $parent.find(".grid-delete-row, .grid-insert-row, .grid-append-row")
  366. .toggle(this.grid.is_editable());
  367. },
  368. render_form: function() {
  369. this.make_form();
  370. this.form_area.empty();
  371. this.layout = new wn.ui.form.Layout({
  372. fields: this.docfields,
  373. body: this.form_area,
  374. no_submit_on_enter: true,
  375. frm: this.frm,
  376. });
  377. this.layout.make();
  378. this.layout.refresh(this.doc)
  379. this.fields = this.layout.fields;
  380. this.fields_dict = this.layout.fields_dict;
  381. // var me = this,
  382. // make_row = function(label) {
  383. // if(label)
  384. // $('<div><h4 style="margin-bottom: 0px;"><b>'+ label +'</b></h4>\
  385. // <hr style="margin-top: 10px;"></div>')
  386. // .appendTo(me.form_area);
  387. //
  388. // var row = $('<div class="row">')
  389. // .appendTo(me.form_area);
  390. //
  391. // var col_spans = 6;
  392. // if(row.parents(".form-column:first").hasClass("col-md-6"))
  393. // col_spans = 12;
  394. //
  395. // var col1 = $('<div class="col-md-'+col_spans+'"></div>').appendTo(row),
  396. // col2 = $('<div class="col-md-'+col_spans+'"></div>').appendTo(row);
  397. //
  398. // return [col1, col2];
  399. // },
  400. // cols = make_row(),
  401. // cnt = 0;
  402. //
  403. // $.each(me.docfields, function(ci, df) {
  404. // if(!df.hidden) {
  405. // if(df.fieldtype=="Section Break") {
  406. // cols = make_row(df.label);
  407. // cnt = 0;
  408. // return;
  409. // }
  410. // var fieldwrapper = $('<div>')
  411. // .appendTo(cols[cnt % 2])
  412. // var fieldobj = make_field(df, me.parent_df.options,
  413. // fieldwrapper.get(0), me.frm);
  414. // fieldobj.docname = me.doc.name;
  415. // fieldobj.refresh();
  416. // fieldobj.input &&
  417. // $(fieldobj.input).css({"max-height": "100px"});
  418. //
  419. // // set field properties
  420. // // used for setting custom get queries in links
  421. // if(me.grid.fieldinfo[df.fieldname])
  422. // $.extend(fieldobj, me.grid.fieldinfo[df.fieldname]);
  423. //
  424. // me.fields.push(fieldobj);
  425. // me.fields_dict[df.fieldname] = fieldobj;
  426. // cnt++;
  427. // }
  428. // });
  429. this.toggle_add_delete_button_display(this.wrapper.find(".panel:first"));
  430. this.grid.open_grid_row = this;
  431. this.frm.script_manager.trigger(this.doc.parentfield + "_on_form_rendered", this);
  432. },
  433. make_form: function() {
  434. if(!this.form_area) {
  435. $('<div class="panel-heading">\
  436. <div class="toolbar">\
  437. <span class="panel-title">Editing Row #<span class="row-index"></span></span>\
  438. <span class="text-success pull-right grid-toggle-row" \
  439. title="'+wn._("Close")+'"\
  440. style="margin-left: 7px;">\
  441. <i class="icon-chevron-up"></i></span>\
  442. <span class="pull-right grid-insert-row" \
  443. title="'+wn._("Insert Row")+'"\
  444. style="margin-left: 7px;">\
  445. <i class="icon-plus grid-insert-row"></i></span>\
  446. <span class="pull-right grid-delete-row"\
  447. title="'+wn._("Delete Row")+'"\
  448. ><i class="icon-trash grid-delete-row"></i></span>\
  449. </div>\
  450. </div>\
  451. <div class="panel-body">\
  452. <div class="form-area"></div>\
  453. <div class="toolbar footer-toolbar" style="margin-top: 15px">\
  454. <span class="text-muted"><a href="#" class="shortcuts"><i class="icon-keyboard"></i> Shortcuts</a></span>\
  455. <span class="text-success pull-right grid-toggle-row" \
  456. title="'+wn._("Close")+'"\
  457. style="margin-left: 7px; cursor: pointer;">\
  458. <i class="icon-chevron-up"></i></span>\
  459. <span class="pull-right grid-append-row" \
  460. title="'+wn._("Insert Below")+'"\
  461. style="margin-left: 7px; cursor: pointer;">\
  462. <i class="icon-plus"></i></span>\
  463. </div>\
  464. </div>').appendTo(this.form_panel);
  465. this.form_area = this.wrapper.find(".form-area");
  466. this.set_row_index();
  467. this.set_form_events();
  468. }
  469. },
  470. set_form_events: function() {
  471. var me = this;
  472. this.form_panel.find(".grid-delete-row")
  473. .click(function() { me.remove(); return false; })
  474. this.form_panel.find(".grid-insert-row")
  475. .click(function() { me.insert(true); return false; })
  476. this.form_panel.find(".grid-append-row")
  477. .click(function() {
  478. me.toggle_view(false);
  479. me.grid.add_new_row(me.doc.idx+1, null, true);
  480. return false;
  481. })
  482. this.form_panel.find(".panel-heading, .grid-toggle-row").on("click", function() {
  483. me.toggle_view();
  484. return false;
  485. });
  486. this.form_panel.find(".shortcuts").on("click", function() {
  487. msgprint('Move Up: Ctrl+<i class="icon-arrow-up"></i>');
  488. msgprint('Move Down: Ctrl+<i class="icon-arrow-down"></i>');
  489. msgprint('Close: Esc');
  490. return false;
  491. })
  492. },
  493. set_data: function() {
  494. this.wrapper.data({
  495. "doc": this.doc
  496. })
  497. },
  498. refresh_field: function(fieldname) {
  499. var $col = this.row.find("[data-fieldname='"+fieldname+"']");
  500. if($col.length) {
  501. var value = wn.model.get_value(this.doc.doctype, this.doc.name, fieldname);
  502. $col.html(wn.format(value, $col.data("df"), null, this.doc));
  503. }
  504. // in form
  505. if(this.fields_dict && this.fields_dict[fieldname]) {
  506. this.fields_dict[fieldname].refresh();
  507. }
  508. },
  509. });