Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 
 
 

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