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.
 
 
 
 
 
 

1228 line
31 KiB

  1. // Copyright (c) 2012 Web Notes Technologies Pvt Ltd (http://erpnext.com)
  2. //
  3. // MIT License (MIT)
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a
  6. // copy of this software and associated documentation files (the "Software"),
  7. // to deal in the Software without restriction, including without limitation
  8. // the rights to use, copy, modify, merge, publish, distribute, sublicense,
  9. // and/or sell copies of the Software, and to permit persons to whom the
  10. // Software is furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  16. // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  17. // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  18. // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
  19. // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
  20. // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  21. //
  22. // fields.js
  23. //
  24. // Fields are divided into 2 types
  25. // 1. Standard fields are loaded with the libarary
  26. // 2. Special fields are loaded with form.compressed.js
  27. //
  28. //
  29. // + wrapper
  30. // + input_area
  31. // + display_area
  32. // ======================================================================================
  33. var no_value_fields = ['Section Break', 'Column Break', 'HTML', 'Table', 'FlexTable', 'Button', 'Image'];
  34. var codeid=0; var code_editors={};
  35. function Field() {
  36. this.with_label = 1;
  37. }
  38. Field.prototype.make_body = function() {
  39. var ischk = (this.df.fieldtype=='Check' ? 1 : 0);
  40. // parent element
  41. this.$wrapper = $('<div class="control-group">\
  42. <label class="control-label"></label>\
  43. <div class="controls">\
  44. <div class="control-input"></div>\
  45. <div class="control-value"></div>\
  46. </div>\
  47. </div>').appendTo(this.parent);
  48. this.wrapper = this.$wrapper.get(0);
  49. this.label_area = this.label_span = this.$wrapper.find(".control-label").get(0);
  50. this.input_area = this.$wrapper.find(".control-input").get(0);
  51. this.disp_area = this.$wrapper.find(".control-value").get(0);
  52. // set description
  53. this.set_description();
  54. if(this.onmake)this.onmake();
  55. }
  56. Field.prototype.set_max_width = function() {
  57. var no_max = ['Code', 'Text Editor', 'Text', 'Small Text', 'Table', 'HTML']
  58. if(this.wrapper && this.layout_cell && this.layout_cell.parentNode.cells
  59. && this.layout_cell.parentNode.cells.length==1 && !in_list(no_max, this.df.fieldtype)) {
  60. $y(this.wrapper, {paddingRight:'50%'});
  61. }
  62. }
  63. Field.prototype.set_label = function(label) {
  64. this.label_span.innerHTML = wn._(label || this.df.label);
  65. }
  66. Field.prototype.set_description = function(txt) {
  67. if(txt) {
  68. if(!this.$wrapper.find(".help-box").length) {
  69. $('<p class="help-box small"></p>').appendTo(this.input_area);
  70. }
  71. this.$wrapper.find(".help-box").html(txt);
  72. } else {
  73. this.$wrapper.find(".help-box").empty().toggle(false);
  74. }
  75. }
  76. Field.prototype.get_status = function(explain) {
  77. if(!this.doctype)
  78. return "Write";
  79. return wn.perm.get_field_display_status(this.df,
  80. locals[this.doctype][this.docname], this.perm, explain)
  81. }
  82. Field.prototype.refresh_mandatory = function() {
  83. if(this.in_filter)return;
  84. //this.$wrapper.toggleClass("has-warning", cint(this.df.reqd) ? true : false);
  85. this.refresh_label_icon()
  86. }
  87. Field.prototype.refresh_display = function() {
  88. // from permission
  89. if(!this.current_status || this.current_status!=this.disp_status) { // status changed
  90. if(this.disp_status=='Write') { // write
  91. if(this.make_input&&(!this.input)) { // make input if reqd
  92. this.make_input();
  93. if(this.txt || this.input)
  94. $(this.txt || this.input).addClass("mousetrap");
  95. if(this.onmake_input) this.onmake_input();
  96. }
  97. if(this.show) this.show()
  98. else { $ds(this.wrapper); }
  99. // input or content
  100. if(this.input) { // if there, show it!
  101. $ds(this.input_area);
  102. $dh(this.disp_area);
  103. if(this.input.refresh)
  104. this.input.refresh();
  105. } else { // no widget
  106. $dh(this.input_area);
  107. $ds(this.disp_area);
  108. }
  109. } else if(this.disp_status=='Read') {
  110. // read
  111. if(this.show) this.show()
  112. else { $ds(this.wrapper); }
  113. $dh(this.input_area);
  114. $ds(this.disp_area);
  115. } else {
  116. // None - hide all
  117. if(this.hide) this.hide();
  118. else $dh(this.wrapper);
  119. }
  120. this.current_status = this.disp_status;
  121. }
  122. }
  123. Field.prototype.refresh = function() {
  124. // get status
  125. this.disp_status = this.get_status();
  126. // if there is a special refresh in case of table, then this is not valid
  127. if(this.in_grid
  128. && this.table_refresh
  129. && this.disp_status == 'Write')
  130. { this.table_refresh(); return; }
  131. this.set_label();
  132. this.refresh_display();
  133. if(this.input) {
  134. if(this.input.refresh) this.input.refresh(this.df);
  135. }
  136. // further refresh
  137. if(this.onrefresh)
  138. this.onrefresh(); // called by various fields
  139. if(this.wrapper) {
  140. this.wrapper.fieldobj = this;
  141. $(this.wrapper).trigger('refresh');
  142. }
  143. if(!this.not_in_form)
  144. this.set_input(_f.get_value(this.doctype,this.docname,this.df.fieldname));
  145. this.refresh_mandatory();
  146. this.set_max_width();
  147. }
  148. Field.prototype.refresh_label_icon = function() {
  149. // mandatory
  150. var to_update = false;
  151. if(this.df.reqd && this.get_value && is_null(this.get_value()))
  152. to_update = true;
  153. this.$wrapper.toggleClass("has-error", to_update);
  154. }
  155. Field.prototype.set = function(val) {
  156. // not in form
  157. if(this.not_in_form)
  158. return;
  159. if((!this.docname) && this.grid) {
  160. this.docname = this.grid.add_newrow(); // new row
  161. }
  162. if(this.validate)
  163. val = this.validate(val);
  164. cur_frm.set_value_in_locals(this.doctype, this.docname,
  165. this.df.fieldname, val);
  166. this.value = val; // for return
  167. }
  168. Field.prototype.set_input = function(val) {
  169. this.value = val;
  170. if(this.input && this.input.set_input) {
  171. this.input.set_input(val); // in widget
  172. }
  173. var disp_val = val;
  174. if(val==null)
  175. disp_val = '';
  176. this.set_disp(disp_val); // text
  177. }
  178. Field.prototype.run_trigger = function() {
  179. // update mandatory icon
  180. this.refresh_label_icon();
  181. if(this.not_in_form) {
  182. return;
  183. }
  184. if(cur_frm.cscript[this.df.fieldname])
  185. cur_frm.runclientscript(this.df.fieldname, this.doctype, this.docname);
  186. cur_frm.refresh_dependency();
  187. }
  188. Field.prototype.set_disp_html = function(t) {
  189. if(this.disp_area){
  190. $(this.disp_area).addClass('disp-area');
  191. this.disp_area.innerHTML = (t==null ? '' : t);
  192. if(!t) $(this.disp_area).addClass('disp-area-no-val');
  193. }
  194. }
  195. Field.prototype.set_disp = function(val) {
  196. this.set_disp_html(val);
  197. }
  198. Field.prototype.get_input = function() {
  199. return this.txt || this.input;
  200. }
  201. // for grids (activate against a particular record in the table
  202. Field.prototype.activate = function(docname) {
  203. this.docname = docname;
  204. this.refresh();
  205. if(this.input) {
  206. var v = _f.get_value(this.doctype, this.docname, this.df.fieldname);
  207. this.last_value=v;
  208. // set input value
  209. if(this.input.onchange && this.input.get_value && this.input.get_value() !=v) {
  210. if(this.validate)
  211. this.input.set_value(this.validate(v));
  212. else
  213. this.input.set_value((v==null)?'':v);
  214. if(this.format_input)
  215. this.format_input();
  216. }
  217. if(this.input.focus){
  218. try{this.input.focus();} catch(e){} // IE Fix - Unexpected call???
  219. }
  220. }
  221. if(this.txt) {
  222. try{this.txt.focus();} catch(e){} // IE Fix - Unexpected call???
  223. this.txt.field_object = this;
  224. }
  225. }
  226. function DataField() { } DataField.prototype = new Field();
  227. DataField.prototype.make_input = function() {
  228. var me = this;
  229. this.input = $a_input(this.input_area, this.df.fieldtype=='Password' ? 'password' : 'text');
  230. if(this.df.placeholder) $(this.input).attr("placeholder", this.df.placeholder);
  231. this.get_value= function() {
  232. var v = this.input.value;
  233. if(this.validate)
  234. v = this.validate(v);
  235. return v;
  236. }
  237. this.input.name = this.df.fieldname;
  238. $(this.input).blur(function() {
  239. me.set_value(me.get_value ? me.get_value() : $(this).val());
  240. });
  241. this.set_value = function(val) {
  242. if(!me.last_value) me.last_value=undefined;
  243. if(me.validate) {
  244. val = me.validate(val);
  245. if(me.last_value === val) return;
  246. me.input.value = (val==undefined) ? '' : val;
  247. } else if(me.last_value === val) { return; }
  248. me.set(val);
  249. if(me.format_input)
  250. me.format_input();
  251. if(in_list(['Currency','Float','Int'], me.df.fieldtype)) {
  252. if(flt(me.last_value)==flt(val)) {
  253. me.last_value = val;
  254. return; // do not run trigger
  255. }
  256. }
  257. me.last_value = val;
  258. me.run_trigger();
  259. }
  260. this.input.set_input = function(val) {
  261. if(val==null)val='';
  262. me.input.value = val;
  263. if(me.format_input)me.format_input();
  264. }
  265. }
  266. DataField.prototype.validate = function(v) {
  267. if(this.df.options == 'Phone') {
  268. if(v+''=='')return '';
  269. v1 = ''
  270. // phone may start with + and must only have numbers later, '-' and ' ' are stripped
  271. v = v.replace(/ /g, '').replace(/-/g, '').replace(/\(/g, '').replace(/\)/g, '');
  272. // allow initial +,0,00
  273. if(v && v.substr(0,1)=='+') {
  274. v1 = '+'; v = v.substr(1);
  275. }
  276. if(v && v.substr(0,2)=='00') {
  277. v1 += '00'; v = v.substr(2);
  278. }
  279. if(v && v.substr(0,1)=='0') {
  280. v1 += '0'; v = v.substr(1);
  281. }
  282. v1 += cint(v) + '';
  283. return v1;
  284. } else if(this.df.options == 'Email') {
  285. if(v+''=='')return '';
  286. if(!validate_email(v)) {
  287. msgprint(this.df.label + ': ' + v + ' is not a valid email id');
  288. return '';
  289. } else
  290. return v;
  291. } else {
  292. return v;
  293. }
  294. }
  295. // ======================================================================================
  296. function ReadOnlyField() { }
  297. ReadOnlyField.prototype = new Field();
  298. // ======================================================================================
  299. function HTMLField() {
  300. var me = this;
  301. this.make_body = function() {
  302. me.wrapper = $("<div>").appendTo(me.parent);
  303. }
  304. this.set_disp = function(val) {
  305. if(val)
  306. $(me.wrapper).html(val);
  307. }
  308. this.set_input = function(val) {
  309. me.set_disp(val);
  310. }
  311. this.refresh = function() {
  312. if(me.df.options)
  313. me.set_disp(me.df.options);
  314. }
  315. }
  316. // ======================================================================================
  317. var datepicker_active = 0;
  318. function get_datepicker_options() {
  319. var datepicker_options = {
  320. dateFormat: (sys_defaults.date_format || 'yy-mm-dd').replace('yyyy','yy'),
  321. altFormat:'yy-mm-dd',
  322. changeYear: true,
  323. yearRange: "-70Y:+10Y",
  324. beforeShow: function(input, inst) {
  325. datepicker_active = 1
  326. },
  327. onClose: function(dateText, inst) {
  328. datepicker_active = 0;
  329. if(_f.cur_grid_cell)
  330. _f.cur_grid_cell.grid.cell_deselect();
  331. },
  332. }
  333. return datepicker_options;
  334. }
  335. function DateField() { } DateField.prototype = new Field();
  336. DateField.prototype.make_input = function() {
  337. var me = this;
  338. this.input = $("<input type='text' data-fieldtype='Date'>")
  339. .appendTo(this.input_area).get(0);
  340. $(this.input).datepicker(get_datepicker_options());
  341. this.setup_input();
  342. }
  343. DateField.prototype.setup_input = function() {
  344. var me = this;
  345. me.input.onchange = function() {
  346. // input as dd-mm-yyyy
  347. if(this.value==null)this.value='';
  348. if(!this.not_in_form)
  349. me.set(dateutil.user_to_str(me.input.value));
  350. me.run_trigger();
  351. }
  352. me.input.set_input = function(val) {
  353. if(val==null)val='';
  354. else val=dateutil.str_to_user(val);
  355. me.input.value = val;
  356. }
  357. me.get_value = function() {
  358. if(me.input.value)
  359. return dateutil.user_to_str(me.input.value);
  360. }
  361. }
  362. DateField.prototype.set_disp = function(val) {
  363. var v = dateutil.str_to_user(val);
  364. if(v==null)v = '';
  365. this.set_disp_html(v);
  366. }
  367. DateField.prototype.validate = function(v) {
  368. var v = wn.datetime.validate(v);
  369. if(!v) {
  370. msgprint (wn._("Date must be in format") + ": " + (sys_defaults.date_format || "yyyy-mm-dd"));
  371. this.input.set_input('');
  372. return '';
  373. }
  374. return v;
  375. };
  376. // reference when a new record is created via link
  377. function LinkField() { } LinkField.prototype = new Field();
  378. LinkField.prototype.make_input = function() {
  379. var me = this;
  380. if(me.df.no_buttons) {
  381. this.txt = $("<input type='text'>")
  382. .appendTo(this.input_area).get(0);
  383. this.input = this.txt;
  384. } else {
  385. me.input = me.input_area;
  386. me.input_group = $('<div class="input-group link-field">').appendTo(me.input_area);
  387. me.txt = $('<input type="text" style="margin-right: 0px;">')
  388. .appendTo(me.input_group).get(0);
  389. me.input_group_btn = $('<div class="input-group-btn">').appendTo(me.input_group);
  390. me.btn = $('<button class="btn" title="'+wn._('Search Link')+'">\
  391. <i class="icon-search"></i></button>').appendTo(me.input_group_btn).get(0);
  392. me.btn1 = $('<button class="btn" title="'+wn._('Open Link')+'">\
  393. <i class="icon-play"></i></button>').appendTo(me.input_group_btn).get(0);
  394. me.btn2 = $('<button class="btn" title="'+wn._('Make New')+'">\
  395. <i class="icon-plus"></i></button>').appendTo(me.input_group_btn).get(0);
  396. me.txt.name = me.df.fieldname;
  397. me.setdisabled = function(tf) { me.txt.disabled = tf; }
  398. // setup buttons
  399. me.setup_buttons();
  400. }
  401. me.onrefresh = function() {
  402. var can_create = in_list(wn.boot.profile.can_create, me.df.options);
  403. var can_read = in_list(wn.boot.profile.can_read, me.df.options);
  404. if(!can_create) $(this.btn2).remove();
  405. if(!can_read) $(this.btn1).remove();
  406. }
  407. me.onrefresh();
  408. me.txt.field_object = this;
  409. // set onchange triggers
  410. me.input.set_input = function(val) {
  411. if(val==undefined)val='';
  412. me.txt.value = val;
  413. }
  414. me.get_value = function() { return me.txt.value; }
  415. // increasing zindex of input to increase zindex of autosuggest
  416. // because of the increase in zindex of dialog_wrapper
  417. if(cur_dialog || me.dialog_wrapper) {
  418. var $dialog_wrapper = $(cur_dialog ? cur_dialog.wrapper : me.dialog_wrapper)
  419. var zindex = cint($dialog_wrapper.css("z-index"));
  420. $(me.txt).css({"z-index": (zindex >= 10 ? zindex : 10) + 1});
  421. }
  422. $(me.txt).autocomplete({
  423. source: function(request, response) {
  424. var args = {
  425. 'txt': request.term,
  426. 'dt': me.df.options,
  427. };
  428. var q = me.get_custom_query();
  429. if (typeof(q)==="string") {
  430. args.query = q;
  431. } else if($.isPlainObject(q)) {
  432. if(q.filters) {
  433. $.each(q.filters, function(key, value) {
  434. q.filters[key] = value===undefined ? null : value;
  435. });
  436. }
  437. $.extend(args, q);
  438. }
  439. wn.call({
  440. method:'webnotes.widgets.search.search_link',
  441. args: args,
  442. callback: function(r) {
  443. response(r.results);
  444. },
  445. });
  446. },
  447. select: function(event, ui) {
  448. me.set_input_value(ui.item.value);
  449. }
  450. }).data('autocomplete')._renderItem = function(ul, item) {
  451. return $('<li></li>')
  452. .data('item.autocomplete', item)
  453. .append(repl('<a><span style="font-weight: bold;">%(label)s</span><br>\
  454. <span style="font-size:10px;">%(info)s</span></a>',
  455. item))
  456. .appendTo(ul);
  457. };
  458. $(this.txt).change(function() {
  459. var val = $(this).val();//me.get_value();
  460. me.set_input_value_executed = false;
  461. if(!val) {
  462. if(selector && selector.display)
  463. return;
  464. me.set_input_value('');
  465. } else {
  466. // SetTimeout hack! if in put is set via autocomplete, do not validate twice
  467. setTimeout(function() {
  468. if (!me.set_input_value_executed) {
  469. me.set_input_value(val);
  470. }
  471. }, 1000);
  472. }
  473. })
  474. }
  475. LinkField.prototype.get_custom_query = function() {
  476. this.set_get_query();
  477. if(this.get_query) {
  478. if(cur_frm)
  479. var doc = locals[cur_frm.doctype][cur_frm.docname];
  480. return this.get_query(doc, this.doctype, this.docname);
  481. }
  482. }
  483. LinkField.prototype.setup_buttons = function() {
  484. var me = this;
  485. // magnifier - search
  486. me.btn.onclick = function() {
  487. selector.set(me, me.df.options, me.df.label);
  488. selector.show(me.txt);
  489. }
  490. // open
  491. if(me.btn1)me.btn1.onclick = function() {
  492. if(me.txt.value && me.df.options) { loaddoc(me.df.options, me.txt.value); }
  493. }
  494. // add button - for inline creation of records
  495. me.can_create = 0;
  496. if((!me.not_in_form) && in_list(profile.can_create, me.df.options)) {
  497. me.can_create = 1;
  498. me.btn2.onclick = function() {
  499. var on_save_callback = function(new_rec) {
  500. if(new_rec) {
  501. var d = _f.calling_doc_stack.pop(); // patch for composites
  502. locals[d[0]][d[1]][me.df.fieldname] = new_rec;
  503. me.refresh();
  504. if(me.grid)me.grid.refresh();
  505. // call script
  506. me.run_trigger();
  507. }
  508. }
  509. _f.calling_doc_stack.push([me.doctype, me.docname]);
  510. new_doc(me.df.options);
  511. }
  512. } else {
  513. $(me.btn2).remove();
  514. }
  515. }
  516. LinkField.prototype.set_input_value = function(val) {
  517. var me = this;
  518. // SetTimeout hack! if in put is set via autocomplete, do not validate twice
  519. me.set_input_value_executed = true;
  520. var from_selector = false;
  521. if(selector && selector.display) from_selector = true;
  522. // refresh mandatory style
  523. me.refresh_label_icon();
  524. // not in form, do nothing
  525. if(me.not_in_form) {
  526. $(this.txt).val(val);
  527. return;
  528. }
  529. // same value, do nothing
  530. if(cur_frm) {
  531. if(val == locals[me.doctype][me.docname][me.df.fieldname]) {
  532. //me.set(val); // one more time, grid bug?
  533. me.run_trigger(); // wanted - called as refresh?
  534. return;
  535. }
  536. }
  537. // set in locals
  538. me.set(val);
  539. // deselect cell if in grid
  540. if(_f.cur_grid_cell)
  541. _f.cur_grid_cell.grid.cell_deselect();
  542. if(val) {
  543. // validate only if val is not empty
  544. me.validate_link(val, from_selector);
  545. } else {
  546. // run trigger if value is cleared
  547. me.run_trigger();
  548. }
  549. }
  550. LinkField.prototype.validate_link = function(val, from_selector) {
  551. // validate the value just entered
  552. var me = this;
  553. if(this.df.options=="[Select]") {
  554. $(me.txt).val(val);
  555. me.run_trigger();
  556. return;
  557. }
  558. var fetch = '';
  559. if(cur_frm.fetch_dict[me.df.fieldname])
  560. fetch = cur_frm.fetch_dict[me.df.fieldname].columns.join(', ');
  561. $c('webnotes.widgets.form.utils.validate_link', {
  562. 'value':val,
  563. 'options':me.df.options,
  564. 'fetch': fetch
  565. },
  566. function(r,rt) {
  567. if(r.message=='Ok') {
  568. // set fetch values
  569. if($(me.txt).val()!=val) {
  570. if((me.grid && !from_selector) || (!me.grid)) {
  571. $(me.txt).val(val);
  572. }
  573. }
  574. if(r.fetch_values)
  575. me.set_fetch_values(r.fetch_values);
  576. me.run_trigger();
  577. } else {
  578. me.txt.value = '';
  579. me.set('');
  580. }
  581. }
  582. );
  583. }
  584. LinkField.prototype.set_fetch_values = function(fetch_values) {
  585. var fl = cur_frm.fetch_dict[this.df.fieldname].fields;
  586. var changed_fields = [];
  587. for(var i=0; i< fl.length; i++) {
  588. if(locals[this.doctype][this.docname][fl[i]]!=fetch_values[i]) {
  589. locals[this.doctype][this.docname][fl[i]] = fetch_values[i];
  590. if(!this.grid) {
  591. refresh_field(fl[i]);
  592. // call trigger on the target field
  593. changed_fields.push(fl[i]);
  594. }
  595. }
  596. }
  597. // run triggers
  598. for(i=0; i<changed_fields.length; i++) {
  599. if(cur_frm.fields_dict[changed_fields[i]]) // on main
  600. cur_frm.fields_dict[changed_fields[i]].run_trigger();
  601. }
  602. // refresh grid
  603. if(this.grid) this.grid.refresh();
  604. }
  605. LinkField.prototype.set_get_query = function() {
  606. if(this.get_query)return;
  607. if(this.grid) {
  608. var f = this.grid.get_field(this.df.fieldname);
  609. if(f.get_query) this.get_query = f.get_query;
  610. }
  611. }
  612. LinkField.prototype.set_disp = function(val) {
  613. var t = null;
  614. if(val)t = "<a href=\'javascript:loaddoc(\""+this.df.options+"\", \""+val+"\")\'>"+val+"</a>";
  615. this.set_disp_html(t);
  616. }
  617. // ======================================================================================
  618. function IntField() { } IntField.prototype = new DataField();
  619. IntField.prototype.validate = function(v) {
  620. if(isNaN(parseInt(v)))return null;
  621. return cint(v);
  622. };
  623. IntField.prototype.format_input = function() {
  624. if(this.input.value==null) this.input.value='';
  625. }
  626. // ======================================================================================
  627. function FloatField() { } FloatField.prototype = new DataField();
  628. FloatField.prototype.validate = function(v) {
  629. if(isNaN(parseFloat(v)))
  630. return null;
  631. else
  632. v = flt(v);
  633. return v;
  634. };
  635. FloatField.prototype.format_input = function() {
  636. if(this.input.value==null || this.input.value=='')
  637. this.input.value='';
  638. else {
  639. var format;
  640. if(this.get_field_currency) {
  641. format = get_number_format(this.get_field_currency());
  642. this.input.value =
  643. format_number(parseFloat(this.input.value), format);
  644. } else {
  645. var decimals = wn.boot.sysdefaults.float_precision ?
  646. parseInt(wn.boot.sysdefaults.float_precision) : null;
  647. this.input.value = format_number(parseFloat(this.input.value), null, decimals);
  648. }
  649. }
  650. }
  651. FloatField.prototype.onmake_input = function() {
  652. if(!this.input) return;
  653. this.input.onfocus = function() {
  654. this.select();
  655. }
  656. }
  657. FloatField.prototype.set_disp = function(val) {
  658. this.set_disp_html(wn.format(val, this.df, null, locals[this.doctype][this.name]));
  659. }
  660. function PercentField() { } PercentField.prototype = new FloatField();
  661. PercentField.prototype.set_disp = function(val) {
  662. this.set_disp_html(wn.format(val, this.df));
  663. }
  664. function CurrencyField() { } CurrencyField.prototype = new FloatField();
  665. CurrencyField.prototype.validate = function(v) {
  666. if(v==null || v=='')
  667. return 0;
  668. return flt(v, null, get_number_format(this.get_field_currency()));
  669. }
  670. CurrencyField.prototype.get_field_currency = function() {
  671. var doc = null;
  672. if(this.doctype && this.docname && locals[this.doctype])
  673. doc = locals[this.doctype][this.docname];
  674. return wn.meta.get_field_currency(this.df, doc);
  675. };
  676. CurrencyField.prototype.get_formatted = function(val) {
  677. if(this.not_in_form)
  678. return val;
  679. return format_currency(val, this.get_field_currency());
  680. }
  681. CurrencyField.prototype.set_disp = function(val) {
  682. this.set_disp_html(this.get_formatted(val));
  683. }
  684. function CheckField() { } CheckField.prototype = new Field();
  685. CheckField.prototype.validate = function(v) {
  686. return cint(v);
  687. };
  688. CheckField.prototype.onmake = function() {
  689. this.checkimg = $("<i class='icon-check'></i>").appendTo(this.disp_area);
  690. }
  691. CheckField.prototype.make_input = function() { var me = this;
  692. this.input = $("<input type='checkbox'>")
  693. .appendTo(this.input_area)
  694. .get(0);
  695. $(this.input).change(function() {
  696. me.set(this.checked ? 1 : 0);
  697. me.run_trigger();
  698. })
  699. this.input.set_input = function(v) {
  700. me.input.checked = cint(v) ? true : false;
  701. }
  702. this.get_value= function() {
  703. return this.input.checked ? 1 : 0;
  704. }
  705. }
  706. CheckField.prototype.set_disp = function(val) {
  707. this.checkimg.toggle(cint(val) ? true : false);
  708. }
  709. function TextField() { } TextField.prototype = new Field();
  710. TextField.prototype.set_disp = function(val) {
  711. this.disp_area.innerHTML = replace_newlines(val);
  712. }
  713. TextField.prototype.make_input = function() {
  714. var me = this;
  715. if(this.in_grid)
  716. return; // do nothing, text dialog will take over
  717. this.input = $a(this.input_area, 'textarea');
  718. if(this.df.fieldtype=='Small Text') {
  719. $(this.input).css({height: "60px"});
  720. } else if(this.df.width) {
  721. $(this.input).css({height: cint(this.df.width) + "px"});
  722. } else {
  723. $(this.input).css({height: "60px"});
  724. }
  725. this.input.set_input = function(v) {
  726. me.input.value = (v==null ? "" : v);
  727. }
  728. this.input.onchange = function() {
  729. me.set(me.input.value);
  730. me.run_trigger();
  731. }
  732. this.get_value= function() {
  733. return this.input.value;
  734. }
  735. }
  736. // text dialog
  737. var text_dialog;
  738. function make_text_dialog() {
  739. var d = new Dialog(520,410,'Edit Text');
  740. d.make_body([
  741. ['Text', 'Enter Text'],
  742. ['HTML', 'Description'],
  743. ['Button', 'Update']
  744. ]);
  745. d.widgets['Update'].onclick = function() {
  746. var t = this.dialog;
  747. t.field.set(t.widgets['Enter Text'].value);
  748. t.hide();
  749. }
  750. d.onshow = function() {
  751. this.widgets['Enter Text'].style.height = '300px';
  752. var v = _f.get_value(this.field.doctype,this.field.docname,this.field.df.fieldname);
  753. this.widgets['Enter Text'].value = v==null?'':v;
  754. this.widgets['Enter Text'].focus();
  755. this.widgets['Description'].innerHTML = ''
  756. if(this.field.df.description)
  757. $a(this.widgets['Description'], 'div', 'help small', '', this.field.df.description);
  758. }
  759. d.onhide = function() {
  760. if(_f.cur_grid_cell)
  761. _f.cur_grid_cell.grid.cell_deselect();
  762. }
  763. text_dialog = d;
  764. }
  765. TextField.prototype.table_refresh = function() {
  766. if(!this.text_dialog)
  767. make_text_dialog();
  768. text_dialog.set_title(wn._('Enter text for')+': "'+ wn._(this.df.label) +'"');
  769. text_dialog.field = this;
  770. text_dialog.show();
  771. }
  772. // Select
  773. // ======================================================================================
  774. function SelectField() { } SelectField.prototype = new Field();
  775. SelectField.prototype.make_input = function() {
  776. var me = this;
  777. var opt=[];
  778. if(this.in_filter && (!this.df.single_select)) {
  779. // multiple select
  780. this.input = $a(this.input_area, 'select');
  781. this.input.multiple = true;
  782. this.input.style.height = '4em';
  783. this.input.lab = $a(this.input_area, 'div', {fontSize:'9px',color:'#999'});
  784. this.input.lab.innerHTML = '(Use Ctrl+Click to select multiple or de-select)'
  785. } else {
  786. // Single select
  787. this.input = $a(this.input_area, 'select');
  788. this.input.onchange = function() {
  789. if(me.validate)
  790. me.validate();
  791. me.set(sel_val(this));
  792. me.run_trigger();
  793. }
  794. if(this.df.options == 'attach_files:') {
  795. this.attach_files = true;
  796. $(this.input).css({"width": "70%"});
  797. $("<button class='btn' title='Add attachment'\
  798. style='margin-bottom: 9px; \
  799. padding-left: 6px; padding-right: 6px; margin-left: 6px;'>\
  800. <i class='icon-plus'></i></button>")
  801. .click(function() {
  802. cur_frm.attachments.new_attachment();
  803. })
  804. .appendTo(this.input_area);
  805. }
  806. }
  807. // set as single (to be called from report builder)
  808. this.set_as_single = function() {
  809. var i = this.input;
  810. i.multiple = false;
  811. i.style.height = null;
  812. if(i.lab)$dh(i.lab)
  813. }
  814. // refresh options list
  815. this.refresh_options = function(options) {
  816. if(options)
  817. me.df.options = options;
  818. if(this.attach_files)
  819. this.set_attach_options();
  820. if(typeof me.df.options=="object")
  821. me.options_list = me.df.options || [""];
  822. else
  823. me.options_list = me.df.options?me.df.options.split('\n'):[''];
  824. // add options
  825. if(me.in_filter && me.options_list[0]!='') {
  826. me.options_list = add_lists([''], me.options_list);
  827. }
  828. $(this.input).empty().add_options(me.options_list);
  829. }
  830. // refresh options
  831. this.onrefresh = function() {
  832. this.refresh_options();
  833. if(this.not_in_form) {
  834. this.input.value = '';
  835. return;
  836. }
  837. if(_f.get_value)
  838. var v = _f.get_value(this.doctype,this.docname,this.df.fieldname);
  839. else {
  840. if(this.options_list && this.options_list.length)
  841. var v = this.options_list[0];
  842. else
  843. var v = null;
  844. }
  845. this.input.set_input(v);
  846. }
  847. var _set_value = function(value) {
  848. // use option's value if dict, else use string for comparison and setting
  849. for(var i in (me.options_list || [""])) {
  850. var option = me.options_list[i];
  851. if($.isPlainObject(option)){
  852. option = option.value;
  853. }
  854. if(option === value) {
  855. me.input.value = value;
  856. break;
  857. }
  858. }
  859. }
  860. this.input.set_input=function(v) {
  861. if(!v) {
  862. if(!me.input.multiple) {
  863. if(me.docname) { // if called from onload without docname being set on fields
  864. _set_value(v);
  865. me.set(me.get_value());
  866. }
  867. }
  868. } else {
  869. if(me.options_list) {
  870. if(me.input.multiple) {
  871. for(var i=0; i<me.input.options.length; i++) {
  872. me.input.options[i].selected = 0;
  873. if(me.input.options[i].value && inList(typeof(v)=='string'?v.split(","):v, me.input.options[i].value))
  874. me.input.options[i].selected = 1;
  875. }
  876. } else {
  877. _set_value(v);
  878. }
  879. }
  880. }
  881. }
  882. this.get_value= function() {
  883. if(me.input.multiple) {
  884. var l = [];
  885. for(var i=0;i<me.input.options.length; i++ ) {
  886. if(me.input.options[i].selected)l[l.length] = me.input.options[i].value;
  887. }
  888. return l;
  889. } else {
  890. if(me.input.options) {
  891. var val = sel_val(me.input);
  892. if(!val && !me.input.selectedIndex)
  893. val = me.input.options[0].value;
  894. return val;
  895. }
  896. return me.input.value;
  897. }
  898. }
  899. this.set_attach_options = function() {
  900. if(!cur_frm) return;
  901. var fl = cur_frm.doc.file_list;
  902. if(fl) {
  903. fl = JSON.parse(fl);
  904. this.df.options = '';
  905. for(var fname in fl) {
  906. if(fname.substr(0,4)!="http")
  907. fname = "files/" + fname;
  908. this.df.options += '\n' + fname;
  909. }
  910. this.set_description("");
  911. } else {
  912. this.df.options = ''
  913. this.set_description(wn._("Please attach a file first."))
  914. }
  915. }
  916. this.refresh();
  917. }
  918. function TimeField() { } TimeField.prototype = new DataField();
  919. function import_timepicker() {
  920. wn.require("lib/js/lib/jquery/jquery.ui.slider.min.js");
  921. wn.require("lib/js/lib/jquery/jquery.ui.sliderAccess.js");
  922. wn.require("lib/js/lib/jquery/jquery.ui.timepicker-addon.css");
  923. wn.require("lib/js/lib/jquery/jquery.ui.timepicker-addon.js");
  924. }
  925. TimeField.prototype.make_input = function() {
  926. import_timepicker();
  927. var me = this;
  928. this.input = $('<input type="text">')
  929. .appendTo(this.input_area)
  930. .timepicker({
  931. timeFormat: 'hh:mm:ss',
  932. }).get(0);
  933. this.input.set_input = function(v) {
  934. $(me.input).val(v);
  935. };
  936. this.input.onchange = function() {
  937. if(!this.not_in_form)
  938. me.set(me.input.value);
  939. me.run_trigger();
  940. };
  941. }
  942. function DateTimeField() { } DateTimeField.prototype = new DateField();
  943. DateTimeField.prototype.make_input = function() {
  944. import_timepicker();
  945. var me = this;
  946. args = get_datepicker_options();
  947. args.timeFormat = "hh:mm:ss";
  948. this.input = $('<input type="text" data-fieldtype="Datetime">')
  949. .appendTo(this.input_area)
  950. .datetimepicker(args).get(0);
  951. this.setup_input();
  952. }
  953. var tmpid = 0;
  954. _f.ButtonField = function() { };
  955. _f.ButtonField.prototype = new Field();
  956. _f.ButtonField.prototype.set_label = function(label) { };
  957. _f.ButtonField.prototype.make_input = function() { var me = this;
  958. // make a button area for one button
  959. if(!this.button_area)
  960. this.button_area = $a(this.input_area, 'div','',{
  961. marginBottom:'4px'});
  962. // make the input
  963. this.input = $btn(this.button_area,
  964. me.df.label, null,
  965. {fontWeight:'bold'}, null, 1)
  966. $(this.input).click(function() {
  967. if(me.not_in_form) return;
  968. if(cur_frm.cscript[me.df.fieldname] && (!me.in_filter)) {
  969. cur_frm.runclientscript(me.df.fieldname, me.doctype, me.docname);
  970. } else {
  971. cur_frm.runscript(me.df.options, me);
  972. }
  973. });
  974. }
  975. _f.ButtonField.prototype.hide = function() {
  976. $dh(this.button_area);
  977. };
  978. _f.ButtonField.prototype.show = function() {
  979. $ds(this.button_area);
  980. };
  981. _f.ButtonField.prototype.set = function(v) { }; // No Setter
  982. _f.ButtonField.prototype.set_disp = function(val) { } // No Disp on readonly
  983. function make_field(docfield, doctype, parent, frm, in_grid, hide_label) { // Factory
  984. switch(docfield.fieldtype.toLowerCase()) {
  985. // general fields
  986. case 'data':var f = new DataField(); break;
  987. case 'password':var f = new DataField(); break;
  988. case 'int':var f = new IntField(); break;
  989. case 'float':var f = new FloatField(); break;
  990. case 'currency':var f = new CurrencyField(); break;
  991. case 'percent':var f = new PercentField(); break;
  992. case 'read only':var f = new ReadOnlyField(); break;
  993. case 'link':var f = new LinkField(); break;
  994. case 'long text': var f = new TextField(); break;
  995. case 'date':var f = new DateField(); break;
  996. case 'datetime':var f = new DateTimeField(); break;
  997. case 'time':var f = new TimeField(); break;
  998. case 'html':var f = new HTMLField(); break;
  999. case 'check':var f = new CheckField(); break;
  1000. case 'text':var f = new TextField(); break;
  1001. case 'small text':var f = new TextField(); break;
  1002. case 'select':var f = new SelectField(); break;
  1003. case 'button':var f = new _f.ButtonField(); break;
  1004. // form fields
  1005. case 'code':var f = new _f.CodeField(); break;
  1006. case 'text editor':var f = new _f.CodeField(); break;
  1007. case 'table':var f = new _f.TableField(); break;
  1008. case 'image':var f= new _f.ImageField(); break;
  1009. }
  1010. f.parent = parent;
  1011. f.doctype = doctype;
  1012. f.df = docfield;
  1013. f.perm = frm ? frm.perm : [[1,1,1]];
  1014. if(_f)
  1015. f.col_break_width = _f.cur_col_break_width;
  1016. if(in_grid) {
  1017. f.in_grid = true;
  1018. f.with_label = 0;
  1019. }
  1020. if(hide_label) {
  1021. f.with_label = 0;
  1022. }
  1023. if(frm) {
  1024. f.frm = frm;
  1025. if(parent)
  1026. f.layout_cell = parent.parentNode;
  1027. }
  1028. if(f.init) f.init();
  1029. f.make_body();
  1030. return f;
  1031. }