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.

fields.js 31 KiB

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