No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
 
 
 
 
 
 

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