You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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