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.
 
 
 
 
 
 

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