You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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