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

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