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