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.
 
 
 
 
 
 

1310 lines
35 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. if(this.parent)
  42. this.wrapper = $a(this.parent, (this.with_label || this.df.fieldtype=="HTML" ? 'div' : 'span'));
  43. else
  44. this.wrapper = document.createElement((this.with_label || this.df.fieldtype=="HTML" ? 'div' : 'span'));
  45. this.label_area = $a(this.wrapper, 'div', '',
  46. {margin:'0px 0px 2px 0px', minHeight:'1em'});
  47. if(ischk && !this.in_grid) {
  48. this.input_area = $a(this.label_area, 'span', '', {marginRight:'4px'});
  49. this.disp_area = $a(this.label_area, 'span', '', {marginRight:'4px'});
  50. }
  51. // label
  52. if(this.with_label) {
  53. this.label_span = $a(this.label_area, 'span', 'small')
  54. // error icon
  55. this.label_icon = $('<i class="icon icon-warning-sign">').toggle(false)
  56. .appendTo(this.label_area).css('margin-left','7px')
  57. .attr("title", "This field is mandatory.");
  58. } else {
  59. this.label_span = $a(this.label_area, 'span', '', {marginRight:'4px'})
  60. $dh(this.label_area);
  61. }
  62. // make the input areas
  63. if(!this.input_area) {
  64. this.input_area = $a(this.wrapper, (this.with_label ? 'div' : 'span'));
  65. this.disp_area = $a(this.wrapper, (this.with_label ? 'div' : 'span'));
  66. }
  67. // apply style
  68. if(this.in_grid) {
  69. if(this.label_area) $dh(this.label_area);
  70. } else {
  71. this.input_area.className = 'input_area';
  72. $y(this.wrapper,{marginBottom:'9px'});
  73. // set description
  74. this.set_description();
  75. }
  76. // bind label refresh
  77. if(this.onmake)this.onmake();
  78. }
  79. Field.prototype.set_max_width = function() {
  80. var no_max = ['Code', 'Text Editor', 'Text', 'Small Text', 'Table', 'HTML']
  81. if(this.wrapper && this.layout_cell && this.layout_cell.parentNode.cells
  82. && this.layout_cell.parentNode.cells.length==1 && !in_list(no_max, this.df.fieldtype)) {
  83. $y(this.wrapper, {paddingRight:'50%'});
  84. }
  85. }
  86. Field.prototype.set_label = function() {
  87. if(this.with_label && this.label_area && this.label!=this.df.label) {
  88. this.label_span.innerHTML = this.df.label;this.label = this.df.label;
  89. }
  90. }
  91. Field.prototype.set_description = function(txt) {
  92. if(this.df.description || txt) {
  93. // parent
  94. if(!this.desc_area) {
  95. var p = in_list(['Text Editor', 'Code', 'Check'], this.df.fieldtype)
  96. ? this.label_area : this.wrapper;
  97. this.desc_area = $a(p, 'div', 'help small');
  98. }
  99. $(this.desc_area).html(this.df.description || txt);
  100. }
  101. }
  102. // Field Refresh
  103. // --------------------------------------------------------------------------------------------
  104. Field.prototype.get_status = function() {
  105. // if used in filters
  106. if(this.in_filter)
  107. this.not_in_form = this.in_filter;
  108. if(this.not_in_form) {
  109. return 'Write';
  110. }
  111. if(!this.df.permlevel) this.df.permlevel = 0;
  112. var p = this.perm[this.df.permlevel];
  113. var ret;
  114. // permission level
  115. if(cur_frm.editable && p && p[WRITE] && !this.df.disabled)ret='Write';
  116. else if(p && p[READ])ret='Read';
  117. else ret='None';
  118. // binary
  119. if(this.df.fieldtype=='Binary')
  120. ret = 'None'; // no display for binary
  121. // hidden
  122. if(cint(this.df.hidden))
  123. ret = 'None';
  124. // for submit
  125. if(ret=='Write' && cint(cur_frm.doc.docstatus) > 0) ret = 'Read';
  126. // allow on submit
  127. var a_o_s = cint(this.df.allow_on_submit);
  128. if(a_o_s && (this.in_grid || (this.frm && this.frm.not_in_container))) {
  129. a_o_s = null;
  130. if(this.in_grid) a_o_s = this.grid.field.df.allow_on_submit; // take from grid
  131. if(this.frm && this.frm.not_in_container) { a_o_s = cur_grid.field.df.allow_on_submit;} // take from grid
  132. }
  133. if(cur_frm.editable && a_o_s && cint(cur_frm.doc.docstatus)>0 && !this.df.hidden) {
  134. tmp_perm = wn.perm.get_perm(cur_frm.doctype, cur_frm.docname, 1);
  135. if(tmp_perm[this.df.permlevel] && tmp_perm[this.df.permlevel][WRITE]) {
  136. ret='Write';
  137. }
  138. }
  139. return ret;
  140. }
  141. Field.prototype.set_style_mandatory = function(add) {
  142. if(add) {
  143. $(this.txt ? this.txt : this.input).addClass('input-mandatory');
  144. if(this.disp_area) $(this.disp_area).addClass('input-mandatory');
  145. } else {
  146. $(this.txt ? this.txt : this.input).removeClass('input-mandatory');
  147. if(this.disp_area) $(this.disp_area).removeClass('input-mandatory');
  148. }
  149. }
  150. Field.prototype.refresh_mandatory = function() {
  151. if(this.in_filter)return;
  152. // mandatory changes
  153. if(this.df.reqd) {
  154. if(this.label_area) this.label_area.style.color= "#d22";
  155. this.set_style_mandatory(1);
  156. } else {
  157. if(this.label_area) this.label_area.style.color= "#222";
  158. this.set_style_mandatory(0);
  159. }
  160. this.refresh_label_icon()
  161. this.set_reqd = this.df.reqd;
  162. }
  163. Field.prototype.refresh_display = function() {
  164. // from permission
  165. if(!this.current_status || this.current_status!=this.disp_status) { // status changed
  166. if(this.disp_status=='Write') { // write
  167. if(this.make_input&&(!this.input)) { // make input if reqd
  168. this.make_input();
  169. if(this.txt || this.input)
  170. $(this.txt || this.input).addClass("mousetrap");
  171. if(this.onmake_input) this.onmake_input();
  172. }
  173. if(this.show) this.show()
  174. else { $ds(this.wrapper); }
  175. // input or content
  176. if(this.input) { // if there, show it!
  177. $ds(this.input_area);
  178. $dh(this.disp_area);
  179. if(this.input.refresh)this.input.refresh();
  180. } else { // no widget
  181. $dh(this.input_area);
  182. $ds(this.disp_area);
  183. }
  184. } else if(this.disp_status=='Read') {
  185. // read
  186. if(this.show) this.show()
  187. else { $ds(this.wrapper); }
  188. $dh(this.input_area);
  189. $ds(this.disp_area);
  190. } else {
  191. // None - hide all
  192. if(this.hide) this.hide();
  193. else $dh(this.wrapper);
  194. }
  195. this.current_status = this.disp_status;
  196. }
  197. }
  198. Field.prototype.refresh = function() {
  199. // get status
  200. this.disp_status = this.get_status();
  201. // if there is a special refresh in case of table, then this is not valid
  202. if(this.in_grid
  203. && this.table_refresh
  204. && this.disp_status == 'Write')
  205. { this.table_refresh(); return; }
  206. this.set_label();
  207. this.refresh_display();
  208. if(this.input) {
  209. if(this.input.refresh) this.input.refresh(this.df);
  210. }
  211. // further refresh
  212. if(this.onrefresh)
  213. this.onrefresh(); // called by various fields
  214. if(this.wrapper) {
  215. this.wrapper.fieldobj = this;
  216. $(this.wrapper).trigger('refresh');
  217. }
  218. if(!this.not_in_form)
  219. this.set_input(_f.get_value(this.doctype,this.docname,this.df.fieldname));
  220. this.refresh_mandatory();
  221. this.set_max_width();
  222. }
  223. Field.prototype.refresh_label_icon = function() {
  224. // mandatory
  225. var to_update = false;
  226. if(this.df.reqd && this.get_value && is_null(this.get_value()))
  227. to_update = true;
  228. if(!to_update && this.df.has_error) this.df.has_error = false;
  229. if(this.label_icon) this.label_icon.css("display", (to_update ? "inline-block" : "none"));
  230. $(this.txt ? this.txt : this.input).toggleClass('field-to-update', to_update);
  231. $(this.txt ? this.txt : this.input).toggleClass('field-has-error',
  232. this.df.has_error ? true : false);
  233. }
  234. // Set / display values
  235. // --------------------------------------------------------------------------------------------
  236. Field.prototype.set = function(val) {
  237. // not in form
  238. if(this.not_in_form)
  239. return;
  240. if((!this.docname) && this.grid) {
  241. this.docname = this.grid.add_newrow(); // new row
  242. }
  243. if(this.validate)
  244. val = this.validate(val);
  245. cur_frm.set_value_in_locals(this.doctype, this.docname, this.df.fieldname, val);
  246. this.value = val; // for return
  247. }
  248. Field.prototype.set_input = function(val) {
  249. this.value = val;
  250. if(this.input&&this.input.set_input) {
  251. if(val==null)this.input.set_input('');
  252. else this.input.set_input(val); // in widget
  253. }
  254. var disp_val = val;
  255. if(val==null) disp_val = '';
  256. this.set_disp(disp_val); // text
  257. }
  258. Field.prototype.run_trigger = function() {
  259. // update mandatory icon
  260. this.refresh_label_icon();
  261. if(this.not_in_form) {
  262. return;
  263. }
  264. if(cur_frm.cscript[this.df.fieldname])
  265. cur_frm.runclientscript(this.df.fieldname, this.doctype, this.docname);
  266. cur_frm.refresh_dependency();
  267. }
  268. Field.prototype.set_disp_html = function(t) {
  269. if(this.disp_area){
  270. $(this.disp_area).addClass('disp_area');
  271. this.disp_area.innerHTML = (t==null ? '' : t);
  272. if(!t) $(this.disp_area).addClass('disp_area_no_val');
  273. }
  274. }
  275. Field.prototype.set_disp = function(val) {
  276. this.set_disp_html(val);
  277. }
  278. // Show in GRID
  279. // --------------------------------------------------------------------------------------------
  280. // for grids (activate against a particular record in the table
  281. Field.prototype.activate = function(docname) {
  282. this.docname = docname;
  283. this.refresh();
  284. if(this.input) {
  285. var v = _f.get_value(this.doctype, this.docname, this.df.fieldname);
  286. this.last_value=v;
  287. // set input value
  288. if(this.input.onchange && this.input.get_value && this.input.get_value() !=v) {
  289. if(this.validate)
  290. this.input.set_value(this.validate(v));
  291. else
  292. this.input.set_value((v==null)?'':v);
  293. if(this.format_input)
  294. this.format_input();
  295. }
  296. if(this.input.focus){
  297. try{this.input.focus();} catch(e){} // IE Fix - Unexpected call???
  298. }
  299. }
  300. if(this.txt) {
  301. try{this.txt.focus();} catch(e){} // IE Fix - Unexpected call???
  302. this.txt.field_object = this;
  303. }
  304. }
  305. // ======================================================================================
  306. function DataField() { } DataField.prototype = new Field();
  307. DataField.prototype.make_input = function() {
  308. var me = this;
  309. this.input = $a_input(this.input_area, this.df.fieldtype=='Password' ? 'password' : 'text');
  310. this.get_value= function() {
  311. var v = this.input.value;
  312. if(this.validate)
  313. v = this.validate(v);
  314. return v;
  315. }
  316. this.input.name = this.df.fieldname;
  317. $(this.input).change(function() {
  318. //me.set_value(me.get_value && me.get_value() || $(this.input).val());
  319. // fix: allow 0 as value
  320. me.set_value(me.get_value ? me.get_value() : $(this.input).val());
  321. });
  322. this.set_value = function(val) {
  323. if(!me.last_value)me.last_value='';
  324. if(me.validate) {
  325. val = me.validate(val);
  326. me.input.value = val==undefined ? '' : val;
  327. }
  328. me.set(val);
  329. if(me.format_input)
  330. me.format_input();
  331. if(in_list(['Currency','Float','Int'], me.df.fieldtype)) {
  332. if(flt(me.last_value)==flt(val)) {
  333. me.last_value = val;
  334. return; // do not run trigger
  335. }
  336. }
  337. me.last_value = val;
  338. me.run_trigger();
  339. }
  340. this.input.set_input = function(val) {
  341. if(val==null)val='';
  342. me.input.value = val;
  343. if(me.format_input)me.format_input();
  344. }
  345. }
  346. DataField.prototype.validate = function(v) {
  347. if(this.df.options == 'Phone') {
  348. if(v+''=='')return '';
  349. v1 = ''
  350. // phone may start with + and must only have numbers later, '-' and ' ' are stripped
  351. v = v.replace(/ /g, '').replace(/-/g, '').replace(/\(/g, '').replace(/\)/g, '');
  352. // allow initial +,0,00
  353. if(v && v.substr(0,1)=='+') {
  354. v1 = '+'; v = v.substr(1);
  355. }
  356. if(v && v.substr(0,2)=='00') {
  357. v1 += '00'; v = v.substr(2);
  358. }
  359. if(v && v.substr(0,1)=='0') {
  360. v1 += '0'; v = v.substr(1);
  361. }
  362. v1 += cint(v) + '';
  363. return v1;
  364. } else if(this.df.options == 'Email') {
  365. if(v+''=='')return '';
  366. if(!validate_email(v)) {
  367. msgprint(this.df.label + ': ' + v + ' is not a valid email id');
  368. return '';
  369. } else
  370. return v;
  371. } else {
  372. return v;
  373. }
  374. }
  375. // ======================================================================================
  376. function ReadOnlyField() { }
  377. ReadOnlyField.prototype = new Field();
  378. // ======================================================================================
  379. function HTMLField() { }
  380. HTMLField.prototype = new Field();
  381. HTMLField.prototype.with_label = 0;
  382. HTMLField.prototype.set_disp = function(val) { if(this.disp_area) this.disp_area.innerHTML = val; }
  383. HTMLField.prototype.set_input = function(val) { if(val) this.set_disp(val); }
  384. HTMLField.prototype.onrefresh = function() { if(this.df.options) this.set_disp(this.df.options); }
  385. // ======================================================================================
  386. var datepicker_active = 0;
  387. function DateField() { } DateField.prototype = new Field();
  388. DateField.prototype.make_input = function() {
  389. var me = this;
  390. this.user_fmt = sys_defaults.date_format;
  391. if(!this.user_fmt)this.user_fmt = 'dd-mm-yy';
  392. this.input = $a(this.input_area, 'input');
  393. $(this.input).datepicker({
  394. dateFormat: me.user_fmt.replace('yyyy','yy'),
  395. altFormat:'yy-mm-dd',
  396. changeYear: true,
  397. beforeShow: function(input, inst) {
  398. datepicker_active = 1
  399. },
  400. onClose: function(dateText, inst) {
  401. datepicker_active = 0;
  402. if(_f.cur_grid_cell)
  403. _f.cur_grid_cell.grid.cell_deselect();
  404. }
  405. });
  406. var me = this;
  407. me.input.onchange = function() {
  408. // input as dd-mm-yyyy
  409. if(this.value==null)this.value='';
  410. if(!this.not_in_form)
  411. me.set(dateutil.user_to_str(me.input.value));
  412. me.run_trigger();
  413. }
  414. me.input.set_input = function(val) {
  415. if(val==null)val='';
  416. else val=dateutil.str_to_user(val);
  417. me.input.value = val;
  418. }
  419. me.get_value = function() {
  420. if(me.input.value)
  421. return dateutil.user_to_str(me.input.value);
  422. }
  423. }
  424. DateField.prototype.set_disp = function(val) {
  425. var v = dateutil.str_to_user(val);
  426. if(v==null)v = '';
  427. this.set_disp_html(v);
  428. }
  429. DateField.prototype.validate = function(v) {
  430. if(!v) return;
  431. var me = this;
  432. this.clear = function() {
  433. msgprint ("Date must be in format " + this.user_fmt);
  434. me.input.set_input('');
  435. return '';
  436. }
  437. var t = $.map(v.split('-'), function(part) { return cint(part) ? part : null; });
  438. if(t.length!=3) { return this.clear(); }
  439. else if(cint(t[1])>12 || cint(t[1])<1) { return this.clear(); }
  440. else if(cint(t[2])>31 || cint(t[2])<1) { return this.clear(); }
  441. else if(String(cint(t[0])).length!=4) { return this.clear(); } // 4 char for year
  442. return v;
  443. };
  444. // ======================================================================================
  445. // reference when a new record is created via link
  446. function LinkField() { } LinkField.prototype = new Field();
  447. LinkField.prototype.make_input = function() {
  448. var me = this;
  449. if(me.df.no_buttons) {
  450. this.txt = $("<input type='text'>")
  451. .appendTo(this.input_area).get(0);
  452. this.input = this.txt;
  453. } else {
  454. me.input = me.input_area;
  455. me.txt = $('<input type="text">')
  456. .css({"width": me.in_filter ? "100px" : (me.in_grid ? "35%" : "65%")})
  457. .appendTo(me.input_area).get(0);
  458. me.btn = $('<i style="cursor: pointer; margin-left: 2px;" \
  459. class="icon icon-search" \
  460. title="Search Link"></i>').appendTo(me.input_area).get(0);
  461. me.btn1 = $('<i style="cursor: pointer; margin-left: 2px;" class="icon icon-play"\
  462. title="Open Link"></i>').appendTo(me.input_area).get(0);
  463. me.btn2 = $('<i style="cursor: pointer; margin-left: 2px;" class="icon icon-plus"\
  464. title="Make New"></i>').appendTo(me.input_area).get(0);
  465. me.txt.name = me.df.fieldname;
  466. me.setdisabled = function(tf) { me.txt.disabled = tf; }
  467. // setup buttons
  468. me.setup_buttons();
  469. }
  470. me.onrefresh = function() {
  471. $(me.btn2).toggle(in_list(wn.boot.profile.can_create, me.df.options));
  472. $(me.btn1).toggle(in_list(wn.boot.profile.can_read, me.df.options));
  473. }
  474. me.onrefresh();
  475. me.txt.field_object = this;
  476. // set onchange triggers
  477. me.input.set_input = function(val) {
  478. if(val==undefined)val='';
  479. me.txt.value = val;
  480. }
  481. me.get_value = function() { return me.txt.value; }
  482. $(me.txt).autocomplete({
  483. source: function(request, response) {
  484. wn.call({
  485. method:'webnotes.widgets.search.search_link',
  486. args: {
  487. 'txt': request.term,
  488. 'dt': me.df.options,
  489. 'query': me.get_custom_query()
  490. },
  491. callback: function(r) {
  492. response(r.results);
  493. },
  494. });
  495. },
  496. select: function(event, ui) {
  497. me.set_input_value(ui.item.value);
  498. }
  499. }).data('autocomplete')._renderItem = function(ul, item) {
  500. return $('<li></li>')
  501. .data('item.autocomplete', item)
  502. .append(repl('<a><span style="font-weight: bold;">%(label)s</span><br>\
  503. <span style="font-size:10px;">%(info)s</span></a>',
  504. item))
  505. .appendTo(ul);
  506. };
  507. $(this.txt).change(function() {
  508. var val = $(this).val();//me.get_value();
  509. me.set_input_value_executed = false;
  510. if(!val) {
  511. if(selector && selector.display)
  512. return;
  513. me.set_input_value('');
  514. } else {
  515. // SetTimeout hack! if in put is set via autocomplete, do not validate twice
  516. setTimeout(function() {
  517. if (!me.set_input_value_executed) {
  518. me.set_input_value(val);
  519. }
  520. }, 1000);
  521. }
  522. })
  523. }
  524. LinkField.prototype.get_custom_query = function() {
  525. this.set_get_query();
  526. if(this.get_query) {
  527. if(cur_frm)
  528. var doc = locals[cur_frm.doctype][cur_frm.docname];
  529. return this.get_query(doc, this.doctype, this.docname);
  530. }
  531. }
  532. LinkField.prototype.setup_buttons = function() {
  533. var me = this;
  534. // magnifier - search
  535. me.btn.onclick = function() {
  536. selector.set(me, me.df.options, me.df.label);
  537. selector.show(me.txt);
  538. }
  539. // open
  540. if(me.btn1)me.btn1.onclick = function() {
  541. if(me.txt.value && me.df.options) { loaddoc(me.df.options, me.txt.value); }
  542. }
  543. // add button - for inline creation of records
  544. me.can_create = 0;
  545. if((!me.not_in_form) && in_list(profile.can_create, me.df.options)) {
  546. me.can_create = 1;
  547. me.btn2.onclick = function() {
  548. var on_save_callback = function(new_rec) {
  549. if(new_rec) {
  550. var d = _f.calling_doc_stack.pop(); // patch for composites
  551. locals[d[0]][d[1]][me.df.fieldname] = new_rec;
  552. me.refresh();
  553. if(me.grid)me.grid.refresh();
  554. // call script
  555. me.run_trigger();
  556. }
  557. }
  558. _f.calling_doc_stack.push([me.doctype, me.docname]);
  559. new_doc(me.df.options);
  560. }
  561. } else {
  562. $dh(me.btn2);
  563. }
  564. }
  565. LinkField.prototype.set_input_value = function(val) {
  566. var me = this;
  567. // SetTimeout hack! if in put is set via autocomplete, do not validate twice
  568. me.set_input_value_executed = true;
  569. var from_selector = false;
  570. if(selector && selector.display) from_selector = true;
  571. // refresh mandatory style
  572. me.refresh_label_icon();
  573. // not in form, do nothing
  574. if(me.not_in_form) {
  575. $(this.txt).val(val);
  576. return;
  577. }
  578. // same value, do nothing
  579. if(cur_frm) {
  580. if(val == locals[me.doctype][me.docname][me.df.fieldname]) {
  581. //me.set(val); // one more time, grid bug?
  582. me.run_trigger(); // wanted - called as refresh?
  583. return;
  584. }
  585. }
  586. // set in locals
  587. me.set(val);
  588. // deselect cell if in grid
  589. if(_f.cur_grid_cell)
  590. _f.cur_grid_cell.grid.cell_deselect();
  591. // run trigger if value is cleared
  592. if(locals[me.doctype][me.docname][me.df.fieldname] && !val) {
  593. me.run_trigger();
  594. return;
  595. }
  596. // validate only if val is not empty
  597. if (val) { me.validate_link(val, from_selector); }
  598. }
  599. LinkField.prototype.validate_link = function(val, from_selector) {
  600. // validate the value just entered
  601. var me = this;
  602. if(this.df.options=="[Select]") {
  603. $(me.txt).val(val);
  604. me.run_trigger();
  605. return;
  606. }
  607. var fetch = '';
  608. if(cur_frm.fetch_dict[me.df.fieldname])
  609. fetch = cur_frm.fetch_dict[me.df.fieldname].columns.join(', ');
  610. $c('webnotes.widgets.form.utils.validate_link', {
  611. 'value':val,
  612. 'options':me.df.options,
  613. 'fetch': fetch
  614. },
  615. function(r,rt) {
  616. if(r.message=='Ok') {
  617. // set fetch values
  618. if($(me.txt).val()!=val) {
  619. if((me.grid && !from_selector) || (!me.grid)) {
  620. $(me.txt).val(val);
  621. }
  622. }
  623. if(r.fetch_values)
  624. me.set_fetch_values(r.fetch_values);
  625. me.run_trigger();
  626. } else {
  627. var astr = '';
  628. if(in_list(profile.can_create, me.df.options)) astr = repl('<br><br><span class="link_type" onclick="newdoc(\'%(dt)s\')">Click here</span> to create a new %(dtl)s', {dt:me.df.options, dtl:wn._(me.df.options)})
  629. msgprint(repl('error:<b>%(val)s</b> is not a valid %(dt)s.<br><br>You must first create a new %(dt)s <b>%(val)s</b> and then select its value. To find an existing %(dt)s, click on the magnifying glass next to the field.%(add)s', {val:me.txt.value, dt:wn._(me.df.options), add:astr}));
  630. me.txt.value = '';
  631. me.set('');
  632. }
  633. }
  634. );
  635. }
  636. LinkField.prototype.set_fetch_values = function(fetch_values) {
  637. var fl = cur_frm.fetch_dict[this.df.fieldname].fields;
  638. var changed_fields = [];
  639. for(var i=0; i< fl.length; i++) {
  640. if(locals[this.doctype][this.docname][fl[i]]!=fetch_values[i]) {
  641. locals[this.doctype][this.docname][fl[i]] = fetch_values[i];
  642. if(!this.grid) {
  643. refresh_field(fl[i]);
  644. // call trigger on the target field
  645. changed_fields.push(fl[i]);
  646. }
  647. }
  648. }
  649. // run triggers
  650. for(i=0; i<changed_fields.length; i++) {
  651. if(cur_frm.fields_dict[changed_fields[i]]) // on main
  652. cur_frm.fields_dict[changed_fields[i]].run_trigger();
  653. }
  654. // refresh grid
  655. if(this.grid) this.grid.refresh();
  656. }
  657. LinkField.prototype.set_get_query = function() {
  658. if(this.get_query)return;
  659. if(this.grid) {
  660. var f = this.grid.get_field(this.df.fieldname);
  661. if(f.get_query) this.get_query = f.get_query;
  662. }
  663. }
  664. LinkField.prototype.set_disp = function(val) {
  665. var t = null;
  666. if(val)t = "<a href=\'javascript:loaddoc(\""+this.df.options+"\", \""+val+"\")\'>"+val+"</a>";
  667. this.set_disp_html(t);
  668. }
  669. // ======================================================================================
  670. function IntField() { } IntField.prototype = new DataField();
  671. IntField.prototype.validate = function(v) {
  672. if(isNaN(parseInt(v)))return null;
  673. return cint(v);
  674. };
  675. IntField.prototype.format_input = function() {
  676. if(this.input.value==null) this.input.value='';
  677. }
  678. // ======================================================================================
  679. function FloatField() { } FloatField.prototype = new DataField();
  680. FloatField.prototype.validate = function(v) {
  681. var v= parseFloat(v);
  682. if(isNaN(v))
  683. return null;
  684. return v;
  685. };
  686. FloatField.prototype.format_input = function() {
  687. if(this.input.value==null) this.input.value='';
  688. }
  689. FloatField.prototype.onmake_input = function() {
  690. if(!this.input) return;
  691. this.input.onfocus = function() {
  692. this.select();
  693. }
  694. }
  695. // ======================================================================================
  696. function CurrencyField() { } CurrencyField.prototype = new FloatField();
  697. CurrencyField.prototype.format_input = function() {
  698. var v = fmt_money(this.input.value);
  699. if(this.not_in_form) {
  700. if(!flt(this.input.value)) v = ''; // blank in filter
  701. }
  702. this.input.value = v;
  703. }
  704. CurrencyField.prototype.validate = function(v) {
  705. if(v==null || v=='')
  706. return 0;
  707. return flt(v,2);
  708. }
  709. CurrencyField.prototype.set_disp = function(val) {
  710. var v = fmt_money(val);
  711. this.set_disp_html(v);
  712. }
  713. // ======================================================================================
  714. function CheckField() { } CheckField.prototype = new Field();
  715. CheckField.prototype.validate = function(v) {
  716. var v= parseInt(v); if(isNaN(v))return 0;
  717. return v;
  718. };
  719. CheckField.prototype.onmake = function() {
  720. this.checkimg = $a(this.disp_area, 'div');
  721. var img = $a(this.checkimg, 'img');
  722. img.src = 'lib/images/ui/tick.gif';
  723. $dh(this.checkimg);
  724. }
  725. CheckField.prototype.make_input = function() { var me = this;
  726. this.input = $a_input(this.input_area,'checkbox');
  727. $y(this.input, {width:"16px", border:'0px', margin:'2px'}); // no specs for checkbox
  728. $(this.input).click(function() {
  729. me.set(this.checked?1:0);
  730. me.run_trigger();
  731. })
  732. this.input.set_input = function(v) {
  733. v = parseInt(v); if(isNaN(v)) v = 0;
  734. if(v) me.input.checked = true;
  735. else me.input.checked=false;
  736. }
  737. this.get_value= function() {
  738. return this.input.checked?1:0;
  739. }
  740. }
  741. CheckField.prototype.set_disp = function(val) {
  742. if (val){ $ds(this.checkimg); }
  743. else { $dh(this.checkimg); }
  744. }
  745. // ======================================================================================
  746. function TextField() { } TextField.prototype = new Field();
  747. TextField.prototype.set_disp = function(val) {
  748. this.disp_area.innerHTML = replace_newlines(val);
  749. }
  750. TextField.prototype.make_input = function() {
  751. var me = this;
  752. if(this.in_grid)
  753. return; // do nothing, text dialog will take over
  754. this.input = $a(this.input_area, 'textarea');
  755. if(this.df.fieldtype=='Small Text')
  756. this.input.style.height = "80px";
  757. if(this.df.width)
  758. this.input.style.height = cint(this.df.width) + "px";
  759. this.input.set_input = function(v) {
  760. me.input.value = v;
  761. }
  762. this.input.onchange = function() {
  763. me.set(me.input.value);
  764. me.run_trigger();
  765. }
  766. this.get_value= function() {
  767. return this.input.value;
  768. }
  769. }
  770. // text dialog
  771. var text_dialog;
  772. function make_text_dialog() {
  773. var d = new Dialog(520,410,'Edit Text');
  774. d.make_body([
  775. ['Text', 'Enter Text'],
  776. ['HTML', 'Description'],
  777. ['Button', 'Update']
  778. ]);
  779. d.widgets['Update'].onclick = function() {
  780. var t = this.dialog;
  781. t.field.set(t.widgets['Enter Text'].value);
  782. t.hide();
  783. }
  784. d.onshow = function() {
  785. this.widgets['Enter Text'].style.height = '300px';
  786. var v = _f.get_value(this.field.doctype,this.field.docname,this.field.df.fieldname);
  787. this.widgets['Enter Text'].value = v==null?'':v;
  788. this.widgets['Enter Text'].focus();
  789. this.widgets['Description'].innerHTML = ''
  790. if(this.field.df.description)
  791. $a(this.widgets['Description'], 'div', 'help small', '', this.field.df.description);
  792. }
  793. d.onhide = function() {
  794. if(_f.cur_grid_cell)
  795. _f.cur_grid_cell.grid.cell_deselect();
  796. }
  797. text_dialog = d;
  798. }
  799. TextField.prototype.table_refresh = function() {
  800. if(!this.text_dialog)
  801. make_text_dialog();
  802. text_dialog.set_title('Enter text for "'+ this.df.label +'"');
  803. text_dialog.field = this;
  804. text_dialog.show();
  805. }
  806. // Select
  807. // ======================================================================================
  808. function SelectField() { } SelectField.prototype = new Field();
  809. SelectField.prototype.make_input = function() {
  810. var me = this;
  811. var opt=[];
  812. if(this.in_filter && (!this.df.single_select)) {
  813. // multiple select
  814. this.input = $a(this.input_area, 'select');
  815. this.input.multiple = true;
  816. this.input.style.height = '4em';
  817. this.input.lab = $a(this.input_area, 'div', {fontSize:'9px',color:'#999'});
  818. this.input.lab.innerHTML = '(Use Ctrl+Click to select multiple or de-select)'
  819. } else {
  820. // Single select
  821. this.input = $a(this.input_area, 'select');
  822. this.input.onchange = function() {
  823. if(me.validate)
  824. me.validate();
  825. me.set(sel_val(this));
  826. me.run_trigger();
  827. }
  828. if(this.df.options == 'attach_files:') {
  829. this.file_attach = true;
  830. }
  831. }
  832. // set as single (to be called from report builder)
  833. this.set_as_single = function() {
  834. var i = this.input;
  835. i.multiple = false;
  836. i.style.height = null;
  837. if(i.lab)$dh(i.lab)
  838. }
  839. // refresh options list
  840. this.refresh_options = function(options) {
  841. if(options)
  842. me.df.options = options;
  843. if(this.file_attach)
  844. this.set_attach_options();
  845. if(typeof me.df.options=="object")
  846. me.options_list = me.df.options || [""];
  847. else
  848. me.options_list = me.df.options?me.df.options.split('\n'):[''];
  849. // add options
  850. if(me.in_filter && me.options_list[0]!='') {
  851. me.options_list = add_lists([''], me.options_list);
  852. }
  853. $(this.input).empty().add_options(me.options_list);
  854. }
  855. // refresh options
  856. this.onrefresh = function() {
  857. this.refresh_options();
  858. if(this.not_in_form) {
  859. this.input.value = '';
  860. return;
  861. }
  862. if(_f.get_value)
  863. var v = _f.get_value(this.doctype,this.docname,this.df.fieldname);
  864. else {
  865. if(this.options_list && this.options_list.length)
  866. var v = this.options_list[0];
  867. else
  868. var v = null;
  869. }
  870. this.input.set_input(v);
  871. }
  872. this.input.set_input=function(v) {
  873. if(!v) {
  874. if(!me.input.multiple) {
  875. if(me.docname) { // if called from onload without docname being set on fields
  876. if(me.options_list && me.options_list.length) {
  877. me.set(me.options_list[0]);
  878. me.input.value = me.options_list[0];
  879. } else {
  880. me.input.value = '';
  881. }
  882. }
  883. }
  884. } else {
  885. if(me.options_list) {
  886. if(me.input.multiple) {
  887. for(var i=0; i<me.input.options.length; i++) {
  888. me.input.options[i].selected = 0;
  889. if(me.input.options[i].value && inList(typeof(v)=='string'?v.split(","):v, me.input.options[i].value))
  890. me.input.options[i].selected = 1;
  891. }
  892. } else if(in_list(me.options_list, v)){
  893. me.input.value = v;
  894. }
  895. }
  896. }
  897. }
  898. this.get_value= function() {
  899. if(me.input.multiple) {
  900. var l = [];
  901. for(var i=0;i<me.input.options.length; i++ ) {
  902. if(me.input.options[i].selected)l[l.length] = me.input.options[i].value;
  903. }
  904. return l;
  905. } else {
  906. if(me.input.options) {
  907. var val = sel_val(me.input);
  908. if(!val && !me.input.selectedIndex)
  909. val = me.input.options[0].value;
  910. return val;
  911. }
  912. return me.input.value;
  913. }
  914. }
  915. this.set_attach_options = function() {
  916. if(!cur_frm) return;
  917. var fl = cur_frm.doc.file_list;
  918. if(fl) {
  919. this.df.options = '';
  920. var fl = fl.split('\n');
  921. for(var i in fl) {
  922. this.df.options += '\n' + fl[i].split(',')[0];
  923. this.set_description("");
  924. }
  925. } else {
  926. this.df.options = ''
  927. this.set_description("Please upload file first.")
  928. }
  929. }
  930. this.refresh();
  931. }
  932. // Time
  933. // ======================================================================================
  934. function TimeField() { } TimeField.prototype = new Field();
  935. TimeField.prototype.get_time = function() {
  936. return time_to_hhmm(sel_val(this.input_hr), sel_val(this.input_mn), sel_val(this.input_am));
  937. }
  938. TimeField.prototype.set_time = function(v) {
  939. //show_alert(ret);
  940. ret = time_to_ampm(v);
  941. this.input_hr.inp.value = ret[0];
  942. this.input_mn.inp.value = ret[1];
  943. this.input_am.inp.value = ret[2];
  944. }
  945. TimeField.prototype.set_style_mandatory = function() { }
  946. TimeField.prototype.make_input = function() { var me = this;
  947. this.input = $a(this.input_area, 'div', 'time_field');
  948. var t = make_table(this.input, 1, 3, '200px');
  949. var opt_hr = ['1','2','3','4','5','6','7','8','9','10','11','12'];
  950. var opt_mn = ['00','05','10','15','20','25','30','35','40','45','50','55'];
  951. var opt_am = ['AM','PM'];
  952. this.input_hr = new SelectWidget($td(t,0,0), opt_hr, '50px');
  953. this.input_mn = new SelectWidget($td(t,0,1), opt_mn, '50px');
  954. this.input_am = new SelectWidget($td(t,0,2), opt_am, '50px');
  955. var onchange_fn = function() {
  956. me.set(me.get_time());
  957. me.run_trigger();
  958. }
  959. this.input_hr.inp.onchange = onchange_fn;
  960. this.input_mn.inp.onchange = onchange_fn;
  961. this.input_am.inp.onchange = onchange_fn;
  962. this.onrefresh = function() {
  963. var v = _f.get_value ? _f.get_value(me.doctype,me.docname,me.df.fieldname) : null;
  964. me.set_time(v);
  965. if(!v)
  966. me.set(me.get_time());
  967. }
  968. this.input.set_input=function(v) {
  969. if(v==null)v='';
  970. me.set_time(v);
  971. }
  972. this.get_value = function() {
  973. return this.get_time();
  974. }
  975. this.refresh();
  976. }
  977. TimeField.prototype.set_disp=function(v) {
  978. var t = time_to_ampm(v);
  979. var t = t[0]+':'+t[1]+' '+t[2];
  980. this.set_disp_html(t);
  981. }
  982. var tmpid = 0;
  983. // ======================================================================================
  984. _f.ButtonField = function() { };
  985. _f.ButtonField.prototype = new Field();
  986. _f.ButtonField.prototype.with_label = 0;
  987. _f.ButtonField.prototype.init = function() {
  988. this.prev_button = null;
  989. // if previous field is a button, add it to the same div!
  990. // button-set structure
  991. // + wrapper (1st button)
  992. // + input_area
  993. // + button_area
  994. // + button_area
  995. // + button_area
  996. if(!this.frm) return;
  997. if(cur_frm &&
  998. cur_frm.fields[cur_frm.fields.length-1] &&
  999. cur_frm.fields[cur_frm.fields.length-1].df.fieldtype=='Button') {
  1000. this.make_body = function() {
  1001. this.prev_button = cur_frm.fields[cur_frm.fields.length-1];
  1002. if(!this.prev_button.prev_button) {
  1003. // first button, make the button area
  1004. this.prev_button.button_area = $a(this.prev_button.input_area, 'span');
  1005. }
  1006. this.wrapper = this.prev_button.wrapper;
  1007. this.input_area = this.prev_button.input_area;
  1008. this.disp_area = this.prev_button.disp_area;
  1009. // all buttons in the same input_area
  1010. this.button_area = $a(this.prev_button.input_area, 'span');
  1011. }
  1012. }
  1013. }
  1014. _f.ButtonField.prototype.make_input = function() { var me = this;
  1015. if(!this.prev_button) {
  1016. $y(this.input_area,{marginTop:'4px', marginBottom: '4px'});
  1017. }
  1018. // make a button area for one button
  1019. if(!this.button_area)
  1020. this.button_area = $a(this.input_area, 'span','',{marginRight:'4px'});
  1021. // make the input
  1022. this.input = $btn(this.button_area,
  1023. me.df.label, null,
  1024. {fontWeight:'bold'}, null, 1)
  1025. $(this.input).click(function() {
  1026. if(me.not_in_form) return;
  1027. if(cur_frm.cscript[me.df.fieldname] && (!me.in_filter)) {
  1028. cur_frm.runclientscript(me.df.fieldname, me.doctype, me.docname);
  1029. } else {
  1030. cur_frm.runscript(me.df.options, me);
  1031. }
  1032. });
  1033. }
  1034. _f.ButtonField.prototype.hide = function() {
  1035. $dh(this.button_area);
  1036. };
  1037. _f.ButtonField.prototype.show = function() {
  1038. $ds(this.button_area);
  1039. };
  1040. _f.ButtonField.prototype.set = function(v) { }; // No Setter
  1041. _f.ButtonField.prototype.set_disp = function(val) { } // No Disp on readonly
  1042. // ======================================================================================
  1043. function make_field(docfield, doctype, parent, frm, in_grid, hide_label) { // Factory
  1044. switch(docfield.fieldtype.toLowerCase()) {
  1045. // general fields
  1046. case 'data':var f = new DataField(); break;
  1047. case 'password':var f = new DataField(); break;
  1048. case 'int':var f = new IntField(); break;
  1049. case 'float':var f = new FloatField(); break;
  1050. case 'currency':var f = new CurrencyField(); break;
  1051. case 'read only':var f = new ReadOnlyField(); break;
  1052. case 'link':var f = new LinkField(); break;
  1053. case 'date':var f = new DateField(); break;
  1054. case 'time':var f = new TimeField(); break;
  1055. case 'html':var f = new HTMLField(); break;
  1056. case 'check':var f = new CheckField(); break;
  1057. case 'text':var f = new TextField(); break;
  1058. case 'small text':var f = new TextField(); break;
  1059. case 'select':var f = new SelectField(); break;
  1060. case 'button':var f = new _f.ButtonField(); break;
  1061. // form fields
  1062. case 'code':var f = new _f.CodeField(); break;
  1063. case 'text editor':var f = new _f.CodeField(); break;
  1064. case 'table':var f = new _f.TableField(); break;
  1065. case 'section break':var f= new _f.SectionBreak(); break;
  1066. case 'column break':var f= new _f.ColumnBreak(); break;
  1067. case 'image':var f= new _f.ImageField(); break;
  1068. }
  1069. f.parent = parent;
  1070. f.doctype = doctype;
  1071. f.df = docfield;
  1072. f.perm = frm ? frm.perm : [[1,1,1]];
  1073. if(_f)
  1074. f.col_break_width = _f.cur_col_break_width;
  1075. if(in_grid) {
  1076. f.in_grid = true;
  1077. f.with_label = 0;
  1078. }
  1079. if(hide_label) {
  1080. f.with_label = 0;
  1081. }
  1082. if(frm) {
  1083. f.frm = frm;
  1084. if(parent)
  1085. f.layout_cell = parent.parentNode;
  1086. }
  1087. if(f.init) f.init();
  1088. f.make_body();
  1089. return f;
  1090. }