Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.
 
 
 
 
 
 

948 rader
25 KiB

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