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.
 
 
 
 
 
 

824 lines
22 KiB

  1. // Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
  2. // MIT License. See license.txt
  3. wn.ui.form.make_control = function(opts) {
  4. var control_class_name = "Control" + opts.df.fieldtype.replace(/ /g, "");
  5. if(wn.ui.form[control_class_name]) {
  6. return new wn.ui.form[control_class_name](opts);
  7. } else {
  8. console.log("Invalid Control Name: " + opts.df.fieldtype);
  9. }
  10. }
  11. // old style
  12. function make_field(docfield, doctype, parent, frm, in_grid, hide_label) { // Factory
  13. return wn.ui.form.make_control({
  14. df: docfield,
  15. doctype: doctype,
  16. parent: parent,
  17. hide_label: hide_label,
  18. frm: frm
  19. });
  20. }
  21. wn.ui.form.Control = Class.extend({
  22. init: function(opts) {
  23. $.extend(this, opts);
  24. this.make();
  25. // if developer_mode=1, show fieldname as tooltip
  26. if(wn.boot.profile && wn.boot.profile.name==="Administrator" &&
  27. wn.boot.developer_mode===1 && this.$wrapper) {
  28. this.$wrapper.attr("title", wn._(this.df.fieldname));
  29. }
  30. },
  31. make: function() {
  32. this.$wrapper = $("<div>").appendTo(this.parent);
  33. this.wrapper = this.$wrapper.get(0);
  34. },
  35. // returns "Read", "Write" or "None"
  36. // as strings based on permissions
  37. get_status: function(explain) {
  38. if(!this.doctype)
  39. return "Write";
  40. return wn.perm.get_field_display_status(this.df,
  41. locals[this.doctype][this.docname], this.perm || this.frm.perm, explain);
  42. },
  43. refresh: function() {
  44. this.disp_status = this.get_status();
  45. this.$wrapper
  46. && this.$wrapper.toggle(this.disp_status!="None")
  47. && this.$wrapper.trigger("refresh");
  48. },
  49. get_doc: function() {
  50. return this.doctype && this.docname
  51. && locals[this.doctype] && locals[this.doctype][this.docname] || {};
  52. },
  53. parse_validate_and_set_in_model: function(value) {
  54. var me = this;
  55. this.inside_change_event = true;
  56. if(this.parse) value = this.parse(value);
  57. var set = function(value) {
  58. me.set_model_value(value);
  59. me.inside_change_event = false;
  60. me.set_mandatory && me.set_mandatory(value);
  61. }
  62. this.validate ? this.validate(value, set) : set(value);
  63. },
  64. get_parsed_value: function() {
  65. return this.get_value ?
  66. (this.parse ? this.parse(this.get_value()) : this.get_value()) :
  67. undefined;
  68. },
  69. set_model_value: function(value) {
  70. if(wn.model.set_value(this.doctype, this.docname, this.df.fieldname,
  71. value, this.df.fieldtype)) {
  72. this.last_value = value;
  73. }
  74. },
  75. });
  76. wn.ui.form.ControlHTML = wn.ui.form.Control.extend({
  77. make: function() {
  78. this._super();
  79. var me = this;
  80. this.disp_area = this.wrapper;
  81. this.$wrapper.on("refresh", function() {
  82. if(me.df.options)
  83. me.$wrapper.html(me.df.options);
  84. })
  85. }
  86. });
  87. wn.ui.form.ControlImage = wn.ui.form.Control.extend({
  88. make: function() {
  89. this._super();
  90. var me = this;
  91. this.$wrapper.on("refresh", function() {
  92. me.$wrapper.empty();
  93. if(me.df.options && me.frm.doc[me.df.options]) {
  94. $("<img src='"+me.frm.doc[me.df.options]+"' style='max-width: 70%;'>")
  95. .appendTo(me.$wrapper);
  96. } else {
  97. $("<div class='missing-image'><i class='icon-camera'></i></div>")
  98. .appendTo(me.$wrapper)
  99. }
  100. return false;
  101. })
  102. }
  103. });
  104. wn.ui.form.ControlReadOnly = wn.ui.form.Control.extend({
  105. make: function() {
  106. this._super();
  107. var me = this;
  108. this.$wrapper.on("refresh", function() {
  109. var value = wn.model.get_value(me.doctype, me.docname, me.fieldname);
  110. me.$wrapper.html(value);
  111. })
  112. },
  113. });
  114. wn.ui.form.ControlInput = wn.ui.form.Control.extend({
  115. make: function() {
  116. // parent element
  117. this.make_wrapper();
  118. this.wrapper = this.$wrapper.get(0);
  119. if(!this.wrapper) console.log(this.parent);
  120. this.wrapper.fieldobj = this; // reference for event handlers
  121. this.set_input_areas();
  122. // set description
  123. this.set_max_width();
  124. this.setup_update_on_refresh();
  125. },
  126. make_wrapper: function() {
  127. if(this.only_input) {
  128. this.$wrapper = $('<div class="form-group">').appendTo(this.parent);
  129. } else {
  130. this.$wrapper = $('<div class="form-group">\
  131. <label class="control-label"></label>\
  132. <div class="control-input"></div>\
  133. <div class="control-value like-disabled-input" style="display: none;"></div>\
  134. <p class="help-box small text-muted">&nbsp;</p>\
  135. </div>').appendTo(this.parent);
  136. }
  137. },
  138. set_input_areas: function() {
  139. if(this.only_input) {
  140. this.input_area = this.wrapper;
  141. } else {
  142. this.label_area = this.label_span = this.$wrapper.find("label").get(0);
  143. this.input_area = this.$wrapper.find(".control-input").get(0);
  144. this.disp_area = this.$wrapper.find(".control-value").get(0);
  145. }
  146. },
  147. set_max_width: function() {
  148. if(['Code', 'Text Editor', 'Text', 'Small Text', 'Table', 'HTML']
  149. .indexOf(this.df.fieldtype)==-1) {
  150. this.$wrapper.css({"max-width": "600px"});
  151. }
  152. },
  153. // update input value, label, description
  154. // display (show/hide/read-only),
  155. // mandatory style on refresh
  156. setup_update_on_refresh: function() {
  157. var me = this;
  158. this.$wrapper.on("refresh", function() {
  159. if(me.disp_status != "None") {
  160. // refresh value
  161. if(me.doctype && me.docname) {
  162. me.value = wn.model.get_value(me.doctype, me.docname, me.df.fieldname);
  163. }
  164. if(me.disp_status=="Write") {
  165. me.disp_area && $(me.disp_area).toggle(false);
  166. $(me.input_area).toggle(true);
  167. !me.has_input && me.make_input();
  168. if(me.doctype && me.docname)
  169. me.set_input(me.value);
  170. } else {
  171. $(me.input_area).toggle(false);
  172. me.disp_area && $(me.disp_area)
  173. .toggle(true)
  174. .html(
  175. wn.format(me.value, me.df, {no_icon:true}, locals[me.doctype][me.name])
  176. );
  177. }
  178. me.set_description();
  179. me.set_label();
  180. me.set_mandatory(me.value);
  181. }
  182. return false;
  183. });
  184. },
  185. bind_change_event: function() {
  186. var me = this;
  187. this.$input && this.$input.on("change", this.change || function() {
  188. me.doctype && me.docname && me.get_value
  189. && me.parse_validate_and_set_in_model(me.get_value()); } );
  190. },
  191. set_label: function(label) {
  192. if(label) this.df.label = label;
  193. if(this.only_input || this.df.label==this._label)
  194. return;
  195. var icon = wn.ui.form.fieldtype_icons[this.df.fieldtype];
  196. if(this.df.fieldtype==="Link") {
  197. icon = wn.boot.doctype_icons[this.df.options];
  198. } else if(this.df.link_doctype) {
  199. icon = wn.boot.doctype_icons[this.df.link_doctype];
  200. }
  201. this.label_span.innerHTML = (icon ? '<i class="'+icon+'"></i> ' : "") +
  202. wn._(this.df.label);
  203. this._label = this.df.label;
  204. },
  205. set_description: function() {
  206. if(this.only_input || this.df.description===this._description)
  207. return;
  208. if(this.df.description) {
  209. this.$wrapper.find(".help-box").html(wn._(this.df.description));
  210. } else {
  211. this.set_empty_description();
  212. }
  213. this._description = this.df.description;
  214. },
  215. set_empty_description: function() {
  216. this.$wrapper.find(".help-box").html("&nbsp;");
  217. },
  218. set_mandatory: function(value) {
  219. this.$wrapper.toggleClass("has-error", (this.df.reqd
  220. && (value==null || value==="")) ? true : false);
  221. },
  222. });
  223. wn.ui.form.ControlData = wn.ui.form.ControlInput.extend({
  224. html_element: "input",
  225. input_type: "text",
  226. make_input: function() {
  227. this.$input = $("<"+ this.html_element +">")
  228. .attr("type", this.input_type)
  229. .addClass("col-md-12 input-with-feedback form-control")
  230. .prependTo(this.input_area)
  231. this.set_input_attributes();
  232. this.input = this.$input.get(0);
  233. this.has_input = true;
  234. this.bind_change_event();
  235. },
  236. set_input_attributes: function() {
  237. this.$input
  238. .attr("data-fieldtype", this.df.fieldtype)
  239. .attr("data-fieldname", this.df.fieldname)
  240. .attr("placeholder", this.df.placeholder || "")
  241. if(this.doctype)
  242. this.$input.attr("data-doctype", this.doctype);
  243. if(this.df.input_css)
  244. this.$input.css(this.df.input_css);
  245. },
  246. set_input: function(val) {
  247. this.$input.val(this.format_for_input(val));
  248. this.last_value = val;
  249. this.set_mandatory && this.set_mandatory(val);
  250. },
  251. get_value: function() {
  252. return this.$input ? this.$input.val() : undefined;
  253. },
  254. format_for_input: function(val) {
  255. return val==null ? "" : val;
  256. },
  257. validate: function(v, callback) {
  258. if(this.df.options == 'Phone') {
  259. if(v+''=='')return '';
  260. v1 = ''
  261. // phone may start with + and must only have numbers later, '-' and ' ' are stripped
  262. v = v.replace(/ /g, '').replace(/-/g, '').replace(/\(/g, '').replace(/\)/g, '');
  263. // allow initial +,0,00
  264. if(v && v.substr(0,1)=='+') {
  265. v1 = '+'; v = v.substr(1);
  266. }
  267. if(v && v.substr(0,2)=='00') {
  268. v1 += '00'; v = v.substr(2);
  269. }
  270. if(v && v.substr(0,1)=='0') {
  271. v1 += '0'; v = v.substr(1);
  272. }
  273. v1 += cint(v) + '';
  274. callback(v1);
  275. } else if(this.df.options == 'Email') {
  276. if(v+''=='')return '';
  277. if(!validate_email(v)) {
  278. msgprint(wn._("Invalid Email") + ": " + v);
  279. callback("");
  280. } else
  281. callback(v);
  282. } else {
  283. callback(v);
  284. }
  285. }
  286. });
  287. wn.ui.form.ControlPassword = wn.ui.form.ControlData.extend({
  288. input_type: "password"
  289. });
  290. wn.ui.form.ControlInt = wn.ui.form.ControlData.extend({
  291. make_input: function() {
  292. var me = this;
  293. this._super();
  294. this.$input
  295. .css({"text-align": "right"})
  296. .on("focus", function() {
  297. setTimeout(function() {
  298. if(!document.activeElement) return;
  299. me.validate(document.activeElement.value, function(val) {
  300. document.activeElement.value = val;
  301. });
  302. document.activeElement.select()
  303. }, 100);
  304. return false;
  305. })
  306. },
  307. validate: function(value, callback) {
  308. return callback(cint(value, null));
  309. }
  310. });
  311. wn.ui.form.ControlFloat = wn.ui.form.ControlInt.extend({
  312. validate: function(value, callback) {
  313. return callback(isNaN(parseFloat(value)) ? null : flt(value));
  314. },
  315. format_for_input: function(value) {
  316. var formatted_value = format_number(parseFloat(value),
  317. null, cint(wn.boot.sysdefaults.float_precision, null));
  318. return isNaN(parseFloat(value)) ? "" : formatted_value;
  319. }
  320. });
  321. wn.ui.form.ControlCurrency = wn.ui.form.ControlFloat.extend({
  322. format_for_input: function(value) {
  323. var formatted_value = format_number(parseFloat(value),
  324. get_number_format(this.get_currency()));
  325. return isNaN(parseFloat(value)) ? "" : formatted_value;
  326. },
  327. get_currency: function() {
  328. return wn.meta.get_field_currency(this.df, this.get_doc());
  329. }
  330. });
  331. wn.ui.form.ControlPercent = wn.ui.form.ControlFloat;
  332. wn.ui.form.ControlDate = wn.ui.form.ControlData.extend({
  333. datepicker_options: {
  334. altFormat:'yy-mm-dd',
  335. changeYear: true,
  336. yearRange: "-70Y:+10Y",
  337. },
  338. make_input: function() {
  339. this._super();
  340. this.set_datepicker();
  341. },
  342. set_datepicker: function() {
  343. this.datepicker_options.dateFormat =
  344. (wn.boot.sysdefaults.date_format || 'yy-mm-dd').replace('yyyy','yy');
  345. if(this.not_in_form && this.dialog_wrapper) {
  346. this.$input.css("z-index", cint($(this.dialog_wrapper).zIndex()));
  347. }
  348. this.$input.datepicker(this.datepicker_options);
  349. },
  350. parse: function(value) {
  351. return value ? dateutil.user_to_str(value) : value;
  352. },
  353. format_for_input: function(value) {
  354. return value ? dateutil.str_to_user(value) : "";
  355. },
  356. validate: function(value, callback) {
  357. var value = wn.datetime.validate(value);
  358. if(!value) {
  359. msgprint (wn._("Date must be in format") + ": " + (sys_defaults.date_format || "yyyy-mm-dd"));
  360. callback("");
  361. }
  362. return callback(value);
  363. }
  364. })
  365. import_timepicker = function() {
  366. wn.require("lib/js/lib/jquery/jquery.ui.slider.min.js");
  367. wn.require("lib/js/lib/jquery/jquery.ui.sliderAccess.js");
  368. wn.require("lib/js/lib/jquery/jquery.ui.timepicker-addon.css");
  369. wn.require("lib/js/lib/jquery/jquery.ui.timepicker-addon.js");
  370. }
  371. wn.ui.form.ControlTime = wn.ui.form.ControlData.extend({
  372. make_input: function() {
  373. import_timepicker();
  374. this._super();
  375. this.$input.timepicker({
  376. timeFormat: 'hh:mm:ss',
  377. });
  378. }
  379. });
  380. wn.ui.form.ControlDatetime = wn.ui.form.ControlDate.extend({
  381. set_datepicker: function() {
  382. this.datepicker_options.dateFormat =
  383. (wn.boot.sysdefaults.date_format || 'yy-mm-dd').replace('yyyy','yy');
  384. this.datepicker_options.timeFormat = "hh:mm:ss";
  385. if(this.not_in_form && this.dialog_wrapper) {
  386. this.$input.css("z-index", cint($(this.dialog_wrapper).zIndex()));
  387. }
  388. this.$input.datetimepicker(this.datepicker_options);
  389. },
  390. make_input: function() {
  391. import_timepicker();
  392. this._super();
  393. },
  394. });
  395. wn.ui.form.ControlText = wn.ui.form.ControlData.extend({
  396. html_element: "textarea"
  397. });
  398. wn.ui.form.ControlLongText = wn.ui.form.ControlText;
  399. wn.ui.form.ControlSmallText = wn.ui.form.ControlText;
  400. wn.ui.form.ControlCheck = wn.ui.form.ControlData.extend({
  401. input_type: "checkbox",
  402. make_wrapper: function() {
  403. this.$wrapper = $('<div class="checkbox">\
  404. <label class="input-area">\
  405. <span class="disp-area" style="display:none;"></span>\
  406. <span class="label-area"></span>\
  407. </label>\
  408. <p class="help-box small text-muted">&nbsp;</p>\
  409. </div>').appendTo(this.parent)
  410. },
  411. set_input_areas: function() {
  412. this.label_area = this.label_span = this.$wrapper.find(".label-area").get(0);
  413. this.input_area = this.$wrapper.find(".input-area").get(0);
  414. this.disp_area = this.$wrapper.find(".disp-area").get(0);
  415. },
  416. make_input: function() {
  417. this._super();
  418. this.$input.removeClass("col-md-12 form-control");
  419. },
  420. parse: function(value) {
  421. return this.input.checked ? 1 : 0;
  422. },
  423. validate: function(value, callback) {
  424. return callback(cint(value));
  425. },
  426. set_input: function(value) {
  427. this.input.checked = value ? 1 : 0;
  428. this.last_value = value;
  429. }
  430. });
  431. wn.ui.form.ControlButton = wn.ui.form.ControlData.extend({
  432. make_input: function() {
  433. var me = this;
  434. this.$input = $('<button class="btn btn-default">')
  435. .prependTo(me.input_area)
  436. .css({"margin-bottom": "7px"})
  437. .on("click", function() {
  438. if(me.frm && me.frm.doc && me.frm.cscript) {
  439. if(me.frm.cscript[me.df.fieldname]) {
  440. me.frm.script_manager.trigger(me.df.fieldname, me.doctype, me.docname);
  441. } else {
  442. me.frm.runscript(me.df.options, me);
  443. }
  444. }
  445. else if(me.df.click) {
  446. me.df.click();
  447. }
  448. });
  449. this.input = this.$input.get(0);
  450. this.has_input = true;
  451. },
  452. set_input_areas: function() {
  453. this._super();
  454. $(this.disp_area).removeClass();
  455. },
  456. set_empty_description: function() {
  457. this.$wrapper.find(".help-box").empty().toggle(false);
  458. },
  459. set_label: function() {
  460. this.$input && this.$input.html(this.df.label);
  461. }
  462. });
  463. wn.ui.form.ControlSelect = wn.ui.form.ControlData.extend({
  464. html_element: "select",
  465. make_input: function() {
  466. var me = this;
  467. this._super();
  468. if(this.df.options=="attach_files:") {
  469. this.setup_attachment();
  470. }
  471. this.set_options();
  472. },
  473. set_input: function(value) {
  474. // refresh options first - (new ones??)
  475. this.set_options();
  476. this._super(value);
  477. // not a possible option, repair
  478. if(this.doctype && this.docname) {
  479. // model value is not an option,
  480. // set the default option (displayed)
  481. var input_value = this.$input.val();
  482. var model_value = wn.model.get_value(this.doctype, this.docname, this.df.fieldname);
  483. if(input_value != (model_value || "")) {
  484. this.set_model_value(input_value);
  485. } else {
  486. this.last_value = value;
  487. }
  488. }
  489. },
  490. setup_attachment: function() {
  491. var me = this;
  492. $(this.input).css({"width": "85%", "display": "inline-block"});
  493. this.$attach = $("<button class='btn btn-default' title='"+ wn._("Add attachment") + "'\
  494. style='padding-left: 6px; padding-right: 6px; margin-left: 6px;'>\
  495. <i class='icon-plus'></i></button>")
  496. .click(function() {
  497. me.frm.attachments.new_attachment(me.df.fieldname);
  498. })
  499. .appendTo(this.input_area);
  500. $(document).on("upload_complete", function(event, filename, file_url) {
  501. if(cur_frm === me.frm) {
  502. me.set_options();
  503. }
  504. })
  505. this.$wrapper.on("refresh", function() {
  506. me.$attach.toggle(!me.frm.doc.__islocal);
  507. });
  508. },
  509. set_options: function() {
  510. var options = this.df.options || [];
  511. if(this.df.options=="attach_files:") {
  512. options = this.get_file_attachment_list();
  513. } else if(typeof this.df.options==="string") {
  514. options = this.df.options.split("\n");
  515. }
  516. if(this.in_filter && options[0] != "") {
  517. options = add_lists([''], options);
  518. }
  519. var selected = this.$input.find(":selected").val();
  520. this.$input.empty().add_options(options || []);
  521. if(selected) this.$input.val(selected);
  522. },
  523. get_file_attachment_list: function() {
  524. if(!this.frm) return;
  525. var fl = wn.model.docinfo[this.frm.doctype][this.frm.docname];
  526. if(fl && fl.attachments) {
  527. fl = fl.attachments;
  528. this.set_description("");
  529. var options = [""];
  530. for(var fname in fl) {
  531. if(fname.indexOf("/")===-1)
  532. fname = "files/" + fname;
  533. options.push(fname);
  534. }
  535. return options;
  536. } else {
  537. this.set_description(wn._("Please attach a file first."))
  538. return [""];
  539. }
  540. }
  541. });
  542. // special features for link
  543. // buttons
  544. // autocomplete
  545. // link validation
  546. // custom queries
  547. // add_fetches
  548. wn.ui.form.ControlLink = wn.ui.form.ControlData.extend({
  549. make_input: function() {
  550. $('<div class="input-group link-field">\
  551. <input type="text" class="input-with-feedback form-control">\
  552. <span class="input-group-btn">\
  553. <button class="btn btn-default btn-search" title="Search Link">\
  554. <i class="icon-search"></i>\
  555. </button>\
  556. <button class="btn btn-default btn-open" title="Open Link">\
  557. <i class="icon-play"></i>\
  558. </button><button class="btn btn-default btn-new" title="Make New">\
  559. <i class="icon-plus"></i>\
  560. </button>\
  561. </span>\
  562. </div>').appendTo(this.input_area);
  563. this.$input_area = $(this.input_area);
  564. this.$input = this.$input_area.find('input');
  565. this.set_input_attributes();
  566. this.input = this.$input.get(0);
  567. this.has_input = true;
  568. //this.bind_change_event();
  569. var me = this;
  570. this.$input.on("blur", function() {
  571. if(me.selected) {
  572. me.selected = false;
  573. return;
  574. }
  575. if(me.doctype && me.docname) {
  576. var value = me.get_value();
  577. if(value!==me.last_value) {
  578. me.parse_validate_and_set_in_model(value);
  579. }
  580. }});
  581. this.setup_buttons();
  582. this.setup_autocomplete();
  583. },
  584. setup_buttons: function() {
  585. var me = this;
  586. // magnifier - search
  587. this.$input_area.find(".btn-search").on("click", function() {
  588. new wn.ui.form.LinkSelector({
  589. doctype: me.df.options,
  590. target: me,
  591. txt: me.$input.val()
  592. });
  593. });
  594. // open
  595. if(wn.model.can_read(me.df.options)) {
  596. this.$input_area.find(".btn-open").on("click", function() {
  597. var value = me.get_value();
  598. if(value && me.df.options) wn.set_route("Form", me.df.options, value);
  599. });
  600. } else {
  601. this.$input_area.find(".btn-open").remove();
  602. }
  603. // new
  604. if(wn.model.can_create(me.df.options)) {
  605. this.$input_area.find(".btn-new").on("click", function() {
  606. new_doc(me.df.options)
  607. });
  608. } else {
  609. this.$input_area.find(".btn-new").remove();
  610. }
  611. },
  612. setup_autocomplete: function() {
  613. var me = this;
  614. this.$input.autocomplete({
  615. source: function(request, response) {
  616. var args = {
  617. 'txt': request.term,
  618. 'doctype': me.df.options,
  619. };
  620. me.set_custom_query(args);
  621. return wn.call({
  622. type: "GET",
  623. method:'webnotes.widgets.search.search_link',
  624. args: args,
  625. callback: function(r) {
  626. response(r.results);
  627. },
  628. });
  629. },
  630. open: function(event, ui) {
  631. me.autocomplete_open = true;
  632. },
  633. close: function(event, ui) {
  634. me.autocomplete_open = false;
  635. },
  636. select: function(event, ui) {
  637. me.autocomplete_open = false;
  638. if(me.frm && me.frm.doc) {
  639. me.selected = true;
  640. me.parse_validate_and_set_in_model(ui.item.value);
  641. } else {
  642. me.$input.val(ui.item.value);
  643. me.$input.trigger("change");
  644. }
  645. }
  646. }).data('uiAutocomplete')._renderItem = function(ul, item) {
  647. return $('<li></li>')
  648. .data('item.autocomplete', item)
  649. .append(repl('<a><span style="font-weight: bold;">%(label)s</span><br>\
  650. <span style="font-size:10px;">%(info)s</span></a>',
  651. item))
  652. .appendTo(ul);
  653. };
  654. // remove accessibility span (for now)
  655. this.$wrapper.find(".ui-helper-hidden-accessible").remove();
  656. },
  657. set_custom_query: function(args) {
  658. var set_nulls = function(obj) {
  659. $.each(obj, function(key, value) {
  660. if(value!==undefined) {
  661. obj[key] = value || null;
  662. }
  663. });
  664. return obj;
  665. }
  666. if(this.get_query || this.df.get_query) {
  667. var get_query = this.get_query || this.df.get_query;
  668. if($.isPlainObject(get_query)) {
  669. $.extend(args, set_nulls(get_query));
  670. } else if(typeof(get_query)==="string") {
  671. args.query = get_query;
  672. } else {
  673. var q = (get_query)(this.frm && this.frm.doc, this.doctype, this.docname);
  674. if (typeof(q)==="string") {
  675. args.query = q;
  676. } else if($.isPlainObject(q)) {
  677. if(q.filters) {
  678. set_nulls(q.filters);
  679. }
  680. $.extend(args, q);
  681. }
  682. }
  683. }
  684. },
  685. validate: function(value, callback) {
  686. // validate the value just entered
  687. var me = this;
  688. if(this.df.options=="[Select]") {
  689. callback(value);
  690. return;
  691. }
  692. this.frm.script_manager.validate_link_and_fetch(this.df, this.docname, value, callback);
  693. },
  694. });
  695. wn.ui.form.ControlCode = wn.ui.form.ControlInput.extend({
  696. editor_name: "ACE",
  697. make_input: function() {
  698. $(this.input_area).css({"min-height":"360px"});
  699. var me = this;
  700. this.editor = new wn.editors[this.editor_name]({
  701. parent: this.input_area,
  702. change: function(value) {
  703. me.parse_validate_and_set_in_model(value);
  704. },
  705. field: this
  706. });
  707. this.has_input = true;
  708. if(this.editor.$editor) {
  709. this.editor.$editor.keypress("ctrl+s meta+s", function() {
  710. me.frm.save_or_update();
  711. });
  712. }
  713. },
  714. get_value: function() {
  715. return this.editor.get_value();
  716. },
  717. set_input: function(value) {
  718. this.editor.set_input(value);
  719. this.last_value = value;
  720. }
  721. });
  722. wn.ui.form.ControlTextEditor = wn.ui.form.ControlCode.extend({
  723. editor_name: "BootstrapWYSIWYG"
  724. });
  725. wn.ui.form.ControlTable = wn.ui.form.Control.extend({
  726. make: function() {
  727. this._super();
  728. // add title if prev field is not column / section heading or html
  729. if(["Column Break", "Section Break", "HTML"].indexOf(
  730. wn.model.get("DocField", {parent: this.frm.doctype, idx: this.df.idx-1}).fieldtype)===-1) {
  731. $("<label>" + wn._(this.df.label) + "<label>").appendTo(this.wrapper);
  732. }
  733. this.grid = new wn.ui.form.Grid({
  734. frm: this.frm,
  735. df: this.df,
  736. perm: this.perm || this.frm.perm,
  737. parent: this.wrapper
  738. })
  739. if(this.frm)
  740. this.frm.grids[this.frm.grids.length] = this;
  741. // description
  742. if(this.df.description) {
  743. $('<p class="text-muted small">' + wn._(this.df.description) + '</p>')
  744. .appendTo(this.wrapper);
  745. }
  746. var me = this;
  747. this.$wrapper.on("refresh", function() {
  748. me.grid.refresh();
  749. return false;
  750. });
  751. }
  752. })
  753. wn.ui.form.fieldtype_icons = {
  754. "Date": "icon-calendar",
  755. "Time": "icon-time",
  756. "Datetime": "icon-time",
  757. "Code": "icon-code",
  758. "Select": "icon-flag"
  759. };