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 36 KiB

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