選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。
 
 
 
 
 
 

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