Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.
 
 
 
 
 
 

988 rindas
26 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", 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"></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("");
  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 || 'yyyy-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. .on("click", function() {
  437. me.onclick();
  438. });
  439. this.input = this.$input.get(0);
  440. this.has_input = true;
  441. },
  442. onclick: function() {
  443. if(this.frm && this.frm.doc && this.frm.cscript) {
  444. if(this.frm.cscript[this.df.fieldname]) {
  445. this.frm.script_manager.trigger(this.df.fieldname, this.doctype, this.docname);
  446. } else {
  447. this.frm.runscript(this.df.options, me);
  448. }
  449. }
  450. else if(this.df.click) {
  451. this.df.click();
  452. }
  453. },
  454. set_input_areas: function() {
  455. this._super();
  456. $(this.disp_area).removeClass();
  457. },
  458. set_empty_description: function() {
  459. this.$wrapper.find(".help-box").empty().toggle(false);
  460. },
  461. set_label: function() {
  462. $(this.label_span).html("&nbsp;");
  463. this.$input && this.$input.html(this.df.label);
  464. }
  465. });
  466. wn.ui.form.ControlAttach = wn.ui.form.ControlButton.extend({
  467. onclick: function() {
  468. if(!this.dialog) {
  469. this.dialog = new wn.ui.Dialog({
  470. title: wn._(this.df.label || "Upload Attachment"),
  471. });
  472. }
  473. $(this.dialog.body).empty();
  474. this.set_upload_options();
  475. wn.upload.make(this.upload_options);
  476. this.dialog.show();
  477. },
  478. set_upload_options: function() {
  479. var me = this;
  480. this.upload_options = {
  481. parent: this.dialog.body,
  482. args: {},
  483. max_width: this.df.max_width,
  484. max_height: this.df.max_height,
  485. callback: function() {
  486. me.dialog.hide();
  487. me.on_upload_complete(fileid, filename, r);
  488. me.show_ok_on_button();
  489. },
  490. onerror: function() {
  491. me.dialog.hide();
  492. },
  493. }
  494. if(this.frm) {
  495. this.upload_options.args = {
  496. from_form: 1,
  497. doctype: this.frm.doctype,
  498. docname: this.frm.docname,
  499. }
  500. } else {
  501. this.upload_options.on_attach = function(fileobj, dataurl) {
  502. me.dialog.hide();
  503. me.fileobj = fileobj;
  504. me.dataurl = dataurl;
  505. if(me.on_attach) {
  506. me.on_attach(fileobj, dataurl)
  507. }
  508. if(me.df.on_attach) {
  509. me.df.on_attach(fileobj, dataurl);
  510. }
  511. me.show_ok_on_button();
  512. }
  513. }
  514. },
  515. on_upload_complete: function(fileid, filename, r) {
  516. this.frm && this.frm.attachments.update_attachment(fileid, filename, this.df.fieldname, r);
  517. },
  518. show_ok_on_button: function() {
  519. if(!$(this.input).find(".icon-ok").length) {
  520. $(this.input).html('<i class="icon-ok"></i> ' + this.df.label);
  521. }
  522. }
  523. });
  524. wn.ui.form.ControlAttachImage = wn.ui.form.ControlAttach.extend({
  525. make_input: function() {
  526. this._super();
  527. this.img = $("<img class='img-responsive'>").appendTo($('<div style="margin-top: 7px;">\
  528. <div class="missing-image"><i class="icon-camera"></i></div></div>').appendTo(this.input_area)).toggle(false);
  529. },
  530. on_attach: function(fileobj, dataurl) {
  531. $(this.input_area).find(".missing-image").toggle(false);
  532. this.img.attr("src", dataurl).toggle(true);
  533. }
  534. });
  535. wn.ui.form.ControlSelect = wn.ui.form.ControlData.extend({
  536. html_element: "select",
  537. make_input: function() {
  538. var me = this;
  539. this._super();
  540. if(this.df.options=="attach_files:") {
  541. this.setup_attachment();
  542. }
  543. this.set_options();
  544. },
  545. set_input: function(value) {
  546. // refresh options first - (new ones??)
  547. this.set_options();
  548. this._super(value);
  549. // not a possible option, repair
  550. if(this.doctype && this.docname) {
  551. // model value is not an option,
  552. // set the default option (displayed)
  553. var input_value = this.$input.val();
  554. var model_value = wn.model.get_value(this.doctype, this.docname, this.df.fieldname);
  555. if(input_value != (model_value || "")) {
  556. this.set_model_value(input_value);
  557. } else {
  558. this.last_value = value;
  559. }
  560. }
  561. },
  562. setup_attachment: function() {
  563. var me = this;
  564. $(this.input).css({"width": "85%", "display": "inline-block"});
  565. this.$attach = $("<button class='btn btn-default' title='"+ wn._("Add attachment") + "'\
  566. style='padding-left: 6px; padding-right: 6px; margin-left: 6px;'>\
  567. <i class='icon-plus'></i></button>")
  568. .click(function() {
  569. me.frm.attachments.new_attachment(me.df.fieldname);
  570. })
  571. .appendTo(this.input_area);
  572. $(document).on("upload_complete", function(event, filename, file_url) {
  573. if(cur_frm === me.frm) {
  574. me.set_options();
  575. }
  576. })
  577. this.$wrapper.on("refresh", function() {
  578. me.$attach.toggle(!me.frm.doc.__islocal);
  579. });
  580. },
  581. set_options: function() {
  582. var options = this.df.options || [];
  583. if(this.df.options=="attach_files:") {
  584. options = this.get_file_attachment_list();
  585. } else if(typeof this.df.options==="string") {
  586. options = this.df.options.split("\n");
  587. }
  588. if(this.in_filter && options[0] != "") {
  589. options = add_lists([''], options);
  590. }
  591. var selected = this.$input.find(":selected").val();
  592. this.$input.empty().add_options(options || []);
  593. if(selected) this.$input.val(selected);
  594. },
  595. get_file_attachment_list: function() {
  596. if(!this.frm) return;
  597. var fl = wn.model.docinfo[this.frm.doctype][this.frm.docname];
  598. if(fl && fl.attachments) {
  599. fl = fl.attachments;
  600. this.set_description("");
  601. var options = [""];
  602. for(var fname in fl) {
  603. if(fname.indexOf("/")===-1)
  604. fname = "files/" + fname;
  605. options.push(fname);
  606. }
  607. return options;
  608. } else {
  609. this.set_description(wn._("Please attach a file first."))
  610. return [""];
  611. }
  612. }
  613. });
  614. // special features for link
  615. // buttons
  616. // autocomplete
  617. // link validation
  618. // custom queries
  619. // add_fetches
  620. wn.ui.form.ControlLink = wn.ui.form.ControlData.extend({
  621. make_input: function() {
  622. $('<div class="input-group link-field">\
  623. <input type="text" class="input-with-feedback form-control">\
  624. <span class="input-group-btn">\
  625. <button class="btn btn-default btn-search" title="Search Link">\
  626. <i class="icon-search"></i>\
  627. </button>\
  628. <button class="btn btn-default btn-open" title="Open Link">\
  629. <i class="icon-play"></i>\
  630. </button><button class="btn btn-default btn-new" title="Make New">\
  631. <i class="icon-plus"></i>\
  632. </button>\
  633. </span>\
  634. </div>').appendTo(this.input_area);
  635. this.$input_area = $(this.input_area);
  636. this.$input = this.$input_area.find('input');
  637. this.set_input_attributes();
  638. this.input = this.$input.get(0);
  639. this.has_input = true;
  640. //this.bind_change_event();
  641. var me = this;
  642. this.setup_buttons();
  643. //this.setup_typeahead();
  644. this.setup_autocomplete();
  645. },
  646. setup_buttons: function() {
  647. var me = this;
  648. // magnifier - search
  649. this.$input_area.find(".btn-search").on("click", function() {
  650. new wn.ui.form.LinkSelector({
  651. doctype: me.df.options,
  652. target: me,
  653. txt: me.get_value()
  654. });
  655. });
  656. // open
  657. if(wn.model.can_read(me.df.options)) {
  658. this.$input_area.find(".btn-open").on("click", function() {
  659. var value = me.get_value();
  660. if(value && me.df.options) wn.set_route("Form", me.df.options, value);
  661. });
  662. } else {
  663. this.$input_area.find(".btn-open").remove();
  664. }
  665. // new
  666. if(wn.model.can_create(me.df.options)) {
  667. this.$input_area.find(".btn-new").on("click", function() {
  668. new_doc(me.df.options)
  669. });
  670. } else {
  671. this.$input_area.find(".btn-new").remove();
  672. }
  673. },
  674. // setup_typeahead: function() {
  675. // var me = this;
  676. // var method = "webnotes.widgets.search.search_link";
  677. // var args = {};
  678. // this.set_custom_query(args);
  679. //
  680. // // custom query
  681. // if(args.query) {
  682. // method = args.query
  683. // }
  684. //
  685. // var _change = function() {
  686. // var val = me.get_value();
  687. // if(me.frm && me.frm.doc) {
  688. // me.selected = true;
  689. // me.parse_validate_and_set_in_model(val);
  690. // } else {
  691. // me.$input.trigger("change");
  692. // }
  693. // }
  694. //
  695. // // filter based on arguments
  696. // var filter_fn = function(r) {
  697. // if(r.exc) console.log(r.exc);
  698. // var filter_args = {};
  699. // me.set_custom_query(filter_args)
  700. // if(filter_args.filters) {
  701. // return wn.utils.filter_dict(r.results, filter_args.filters);
  702. // } else {
  703. // return r.results;
  704. // }
  705. // }
  706. //
  707. // // default query args
  708. // var query_args = {
  709. // cmd: method,
  710. // txt: "%",
  711. // page_len: "9999",
  712. // doctype: me.df.options,
  713. // }
  714. //
  715. // // append filter keys (needed for client-side filtering)
  716. // if(args.filters) {
  717. // query_args.search_fields = ["name"].concat(keys(args.filters));
  718. // }
  719. //
  720. // this.$input.typeahead("destroy").typeahead({
  721. // name: me.df.parent + ":" + me.df.fieldname,
  722. // prefetch: {
  723. // url: "server.py?" + wn.utils.get_url_from_dict(query_args),
  724. // filter: filter_fn,
  725. // },
  726. // remote: {
  727. // url: "server.py?" + wn.utils.get_url_from_dict($.extend(query_args, {"txt": null})) + "&txt=%QUERY",
  728. // filter: filter_fn,
  729. // },
  730. // template: function(d) {
  731. // if(keys(d).length > 1) {
  732. // d.info = $.map(d, function(val, key) { return key==="name" ? null : val }).join(", ");
  733. // return repl("<p>%(value)s<br><span class='text-muted'>%(info)s</span></p>", d);
  734. // } else {
  735. // return d.value;
  736. // }
  737. // }
  738. // }).on("typeahead:selected", function(d) {
  739. // _change();
  740. // }).on("typeahead:autocompleted", function(d) {
  741. // _change();
  742. // });
  743. //
  744. // this.set_input = function(val) {
  745. // me.$input.typeahead("setQuery", val || "");
  746. // }
  747. // },
  748. setup_autocomplete: function() {
  749. var me = this;
  750. this.$input.on("blur", function() {
  751. if(me.selected) {
  752. me.selected = false;
  753. return;
  754. }
  755. if(me.doctype && me.docname) {
  756. var value = me.get_value();
  757. if(value!==me.last_value) {
  758. me.parse_validate_and_set_in_model(value);
  759. }
  760. }});
  761. this.$input.autocomplete({
  762. source: function(request, response) {
  763. var args = {
  764. 'txt': request.term,
  765. 'doctype': me.df.options,
  766. };
  767. me.set_custom_query(args);
  768. return wn.call({
  769. type: "GET",
  770. method:'webnotes.widgets.search.search_link',
  771. no_spinner: true,
  772. args: args,
  773. callback: function(r) {
  774. response(r.results);
  775. },
  776. });
  777. },
  778. open: function(event, ui) {
  779. me.autocomplete_open = true;
  780. },
  781. close: function(event, ui) {
  782. me.autocomplete_open = false;
  783. },
  784. select: function(event, ui) {
  785. me.autocomplete_open = false;
  786. if(me.frm && me.frm.doc) {
  787. me.selected = true;
  788. me.parse_validate_and_set_in_model(ui.item.value);
  789. } else {
  790. me.$input.val(ui.item.value);
  791. me.$input.trigger("change");
  792. }
  793. }
  794. }).data('uiAutocomplete')._renderItem = function(ul, d) {
  795. var html = "";
  796. if(keys(d).length > 1) {
  797. d.info = $.map(d, function(val, key) { return ["value", "label"].indexOf(key)!==-1 ? null : val }).join(", ") || "";
  798. html = repl("<a>%(value)s<br><span class='text-muted'>%(info)s</span></a>", d);
  799. } else {
  800. html = "<a>" + d.value + "</a>";
  801. }
  802. return $('<li></li>')
  803. .data('item.autocomplete', d)
  804. .append(html)
  805. .appendTo(ul);
  806. };
  807. // remove accessibility span (for now)
  808. this.$wrapper.find(".ui-helper-hidden-accessible").remove();
  809. },
  810. set_custom_query: function(args) {
  811. var set_nulls = function(obj) {
  812. $.each(obj, function(key, value) {
  813. if(value!==undefined) {
  814. obj[key] = value || null;
  815. }
  816. });
  817. return obj;
  818. }
  819. if(this.get_query || this.df.get_query) {
  820. var get_query = this.get_query || this.df.get_query;
  821. if($.isPlainObject(get_query)) {
  822. $.extend(args, set_nulls(get_query));
  823. } else if(typeof(get_query)==="string") {
  824. args.query = get_query;
  825. } else {
  826. var q = (get_query)(this.frm && this.frm.doc, this.doctype, this.docname);
  827. if (typeof(q)==="string") {
  828. args.query = q;
  829. } else if($.isPlainObject(q)) {
  830. if(q.filters) {
  831. set_nulls(q.filters);
  832. }
  833. $.extend(args, q);
  834. }
  835. }
  836. }
  837. },
  838. validate: function(value, callback) {
  839. // validate the value just entered
  840. var me = this;
  841. if(this.df.options=="[Select]") {
  842. callback(value);
  843. return;
  844. }
  845. this.frm.script_manager.validate_link_and_fetch(this.df, this.docname, value, callback);
  846. },
  847. });
  848. wn.ui.form.ControlCode = wn.ui.form.ControlInput.extend({
  849. editor_name: "ACE",
  850. make_input: function() {
  851. $(this.input_area).css({"min-height":"360px"});
  852. var me = this;
  853. this.editor = new wn.editors[this.editor_name]({
  854. parent: this.input_area,
  855. change: function(value) {
  856. me.parse_validate_and_set_in_model(value);
  857. },
  858. field: this
  859. });
  860. this.has_input = true;
  861. if(this.editor.$editor) {
  862. this.editor.$editor.keypress("ctrl+s meta+s", function() {
  863. me.frm.save_or_update();
  864. });
  865. }
  866. },
  867. get_value: function() {
  868. return this.editor.get_value();
  869. },
  870. set_input: function(value) {
  871. this.editor.set_input(value);
  872. this.last_value = value;
  873. }
  874. });
  875. wn.ui.form.ControlTextEditor = wn.ui.form.ControlCode.extend({
  876. editor_name: "BootstrapWYSIWYG"
  877. });
  878. wn.ui.form.ControlTable = wn.ui.form.Control.extend({
  879. make: function() {
  880. this._super();
  881. // add title if prev field is not column / section heading or html
  882. if(["Column Break", "Section Break", "HTML"].indexOf(
  883. wn.model.get("DocField", {parent: this.frm.doctype, idx: this.df.idx-1}).fieldtype)===-1) {
  884. $("<label>" + this.df.label + "<label>").appendTo(this.wrapper);
  885. }
  886. this.grid = new wn.ui.form.Grid({
  887. frm: this.frm,
  888. df: this.df,
  889. perm: this.perm || this.frm.perm,
  890. parent: this.wrapper
  891. })
  892. if(this.frm)
  893. this.frm.grids[this.frm.grids.length] = this;
  894. // description
  895. if(this.df.description) {
  896. $('<p class="text-muted small">' + this.df.description + '</p>')
  897. .appendTo(this.wrapper);
  898. }
  899. var me = this;
  900. this.$wrapper.on("refresh", function() {
  901. me.grid.refresh();
  902. return false;
  903. });
  904. }
  905. })
  906. wn.ui.form.fieldtype_icons = {
  907. "Date": "icon-calendar",
  908. "Time": "icon-time",
  909. "Datetime": "icon-time",
  910. "Code": "icon-code",
  911. "Select": "icon-flag"
  912. };