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