// Copyright (c) 2012 Web Notes Technologies Pvt Ltd (http://erpnext.com) // // MIT License (MIT) // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // // fields.js // // Fields are divided into 2 types // 1. Standard fields are loaded with the libarary // 2. Special fields are loaded with form.compressed.js // // // + wrapper // + input_area // + display_area // ====================================================================================== var no_value_fields = ['Section Break', 'Column Break', 'HTML', 'Table', 'FlexTable', 'Button', 'Image']; var codeid=0; var code_editors={}; function Field() { this.with_label = 1; } Field.prototype.make_body = function() { var ischk = (this.df.fieldtype=='Check' ? 1 : 0); // parent element if(this.parent) this.wrapper = $a(this.parent, (this.with_label ? 'div' : 'span')); else this.wrapper = document.createElement((this.with_label ? 'div' : 'span')); this.label_area = $a(this.wrapper, 'div', '', {margin:'0px 0px 2px 0px'}); if(ischk && !this.in_grid) { this.input_area = $a(this.label_area, 'span', '', {marginRight:'4px'}); this.disp_area = $a(this.label_area, 'span', '', {marginRight:'4px'}); } // label if(this.with_label) { this.label_span = $a(this.label_area, 'span', 'small') // error icon this.label_icon = $a(this.label_area,'img','',{margin:'-3px 4px -3px 4px'}); $dh(this.label_icon); this.label_icon.src = 'images/lib/icons/error.gif'; this.label_icon.title = 'Mandatory value needs to be entered'; // error icon this.suggest_icon = $a(this.label_area,'img','',{margin:'-3px 4px -3px 0px'}); $dh(this.suggest_icon); this.suggest_icon.src = 'images/lib/icons/bullet_arrow_down.png'; this.suggest_icon.title = 'With suggestions'; } else { this.label_span = $a(this.label_area, 'span', '', {marginRight:'4px'}) $dh(this.label_area); } // make the input areas if(!this.input_area) { this.input_area = $a(this.wrapper, (this.with_label ? 'div' : 'span')); this.disp_area = $a(this.wrapper, (this.with_label ? 'div' : 'span')); } // apply style if(this.in_grid) { if(this.label_area) $dh(this.label_area); } else { this.input_area.className = 'input_area'; $y(this.wrapper,{marginBottom:'9px'}); // set description this.set_description(); } // bind label refresh if(this.onmake)this.onmake(); } Field.prototype.set_max_width = function() { var no_max = ['Code', 'Text Editor', 'Text', 'Table', 'HTML'] if(this.wrapper && this.layout_cell && this.layout_cell.parentNode.cells && this.layout_cell.parentNode.cells.length==1 && !in_list(no_max, this.df.fieldtype)) { $y(this.wrapper, {paddingRight:'50%'}); } } Field.prototype.set_label = function() { if(this.with_label && this.label_area && this.label!=this.df.label) { this.label_span.innerHTML = this.df.label;this.label = this.df.label; } } Field.prototype.set_description = function() { if(this.df.description) { // parent var p = in_list(['Text Editor', 'Code', 'Check'], this.df.fieldtype) ? this.label_area : this.wrapper; this.desc_area = $a(p, 'div', 'help small', '', this.df.description) // padding on the bottom if(in_list(['Text Editor', 'Code'], this.df.fieldtype)) $(this.desc_area).addClass('help small'); } } // Field Refresh // -------------------------------------------------------------------------------------------- Field.prototype.get_status = function() { // if used in filters if(this.in_filter) this.not_in_form = this.in_filter; if(this.not_in_form) { return 'Write'; } if(!this.df.permlevel) this.df.permlevel = 0; var p = this.perm[this.df.permlevel]; var ret; // permission level if(cur_frm.editable && p && p[WRITE])ret='Write'; else if(p && p[READ])ret='Read'; else ret='None'; // binary if(this.df.fieldtype=='Binary') ret = 'None'; // no display for binary // hidden if(cint(this.df.hidden)) ret = 'None'; // for submit if(ret=='Write' && cint(cur_frm.doc.docstatus) > 0) ret = 'Read'; // allow on submit var a_o_s = cint(this.df.allow_on_submit); if(a_o_s && (this.in_grid || (this.frm && this.frm.not_in_container))) { a_o_s = null; if(this.in_grid) a_o_s = this.grid.field.df.allow_on_submit; // take from grid if(this.frm && this.frm.not_in_container) { a_o_s = cur_grid.field.df.allow_on_submit;} // take from grid } if(cur_frm.editable && a_o_s && cint(cur_frm.doc.docstatus)>0 && !this.df.hidden) { tmp_perm = get_perm(cur_frm.doctype, cur_frm.docname, 1); if(tmp_perm[this.df.permlevel] && tmp_perm[this.df.permlevel][WRITE])ret='Write'; } return ret; } Field.prototype.set_style_mandatory = function(add) { if(add) { $(this.txt ? this.txt : this.input).addClass('input-mandatory'); if(this.disp_area) $(this.disp_area).addClass('input-mandatory'); } else { $(this.txt ? this.txt : this.input).removeClass('input-mandatory'); if(this.disp_area) $(this.disp_area).removeClass('input-mandatory'); } } Field.prototype.refresh_mandatory = function() { if(this.in_filter)return; // mandatory changes if(this.df.reqd) { if(this.label_area) this.label_area.style.color= "#d22"; this.set_style_mandatory(1); } else { if(this.label_area) this.label_area.style.color= "#222"; this.set_style_mandatory(0); } this.refresh_label_icon() this.set_reqd = this.df.reqd; } Field.prototype.refresh_display = function() { // from permission if(!this.current_status || this.current_status!=this.disp_status) { // status changed if(this.disp_status=='Write') { // write if(this.make_input&&(!this.input)) { // make input if reqd this.make_input(); if(this.onmake_input) this.onmake_input(); } if(this.show) this.show() else { $ds(this.wrapper); } // input or content if(this.input) { // if there, show it! $ds(this.input_area); $dh(this.disp_area); if(this.input.refresh)this.input.refresh(); } else { // no widget $dh(this.input_area); $ds(this.disp_area); } } else if(this.disp_status=='Read') { // read if(this.show) this.show() else { $ds(this.wrapper); } $dh(this.input_area); $ds(this.disp_area); } else { // None - hide all if(this.hide) this.hide(); else $dh(this.wrapper); } this.current_status = this.disp_status; } } Field.prototype.refresh = function() { // get status this.disp_status = this.get_status(); // if there is a special refresh in case of table, then this is not valid if(this.in_grid && this.table_refresh && this.disp_status == 'Write') { this.table_refresh(); return; } this.set_label(); this.refresh_display(); // further refresh if(this.onrefresh) this.onrefresh(); // called by various fields if(this.input) { if(this.input.refresh) this.input.refresh(this.df); } if(this.wrapper) { this.wrapper.fieldobj = this; $(this.wrapper).trigger('refresh'); } if(!this.not_in_form) this.set_input(_f.get_value(this.doctype,this.docname,this.df.fieldname)); this.refresh_mandatory(); this.set_max_width(); } Field.prototype.refresh_label_icon = function() { // mandatory if(this.df.reqd) { if(this.get_value && is_null(this.get_value())) { if(this.label_icon) $ds(this.label_icon); $(this.txt ? this.txt : this.input).addClass('field-to-update') } else { if(this.label_icon) $dh(this.label_icon); $(this.txt ? this.txt : this.input).removeClass('field-to-update') } } } // Set / display values // -------------------------------------------------------------------------------------------- Field.prototype.set = function(val) { // not in form if(this.not_in_form) return; if((!this.docname) && this.grid) { this.docname = this.grid.add_newrow(); // new row } var set_val = val; if(this.validate)set_val = this.validate(val); _f.set_value(this.doctype, this.docname, this.df.fieldname, set_val); this.value = val; // for return } Field.prototype.set_input = function(val) { this.value = val; if(this.input&&this.input.set_input) { if(val==null)this.input.set_input(''); else this.input.set_input(val); // in widget } var disp_val = val; if(val==null) disp_val = ''; this.set_disp(disp_val); // text } Field.prototype.run_trigger = function() { // update mandatory icon this.refresh_label_icon(); if(this.df.reqd && this.get_value && !is_null(this.get_value()) && this.set_as_error) this.set_as_error(0); if(this.not_in_form) { return; } if(cur_frm.cscript[this.df.fieldname]) cur_frm.runclientscript(this.df.fieldname, this.doctype, this.docname); cur_frm.refresh_dependency(); } Field.prototype.set_disp_html = function(t) { if(this.disp_area){ $(this.disp_area).addClass('disp_area'); this.disp_area.innerHTML = (t==null ? '' : t); if(!t) $(this.disp_area).addClass('disp_area_no_val'); } } Field.prototype.set_disp = function(val) { this.set_disp_html(val); } Field.prototype.set_as_error = function(set) { if(this.in_grid || this.in_filter) return; var w = this.txt ? this.txt : this.input; if(set) { $y(w, {border: '2px solid RED'}); } else { $y(w, {border: '1px solid #888'}); } } // Show in GRID // -------------------------------------------------------------------------------------------- // for grids (activate against a particular record in the table Field.prototype.activate = function(docname) { this.docname = docname; this.refresh(); if(this.input) { var v = _f.get_value(this.doctype, this.docname, this.df.fieldname); this.last_value=v; // set input value if(this.input.onchange && this.input.get_value && this.input.get_value() !=v) { if(this.validate) this.input.set_value(this.validate(v)); else this.input.set_value((v==null)?'':v); if(this.format_input) this.format_input(); } if(this.input.focus){ try{this.input.focus();} catch(e){} // IE Fix - Unexpected call??? } } if(this.txt) { try{this.txt.focus();} catch(e){} // IE Fix - Unexpected call??? this.txt.field_object = this; } } // ====================================================================================== function DataField() { } DataField.prototype = new Field(); DataField.prototype.make_input = function() { var me = this; this.input = $a_input(this.input_area, this.df.fieldtype=='Password' ? 'password' : 'text'); this.get_value= function() { var v = this.input.value; if(this.validate) v = this.validate(v); return v; } this.input.name = this.df.fieldname; $(this.input).change(function() { //me.set_value(me.get_value && me.get_value() || $(this.input).val()); // fix: allow 0 as value me.set_value(me.get_value ? me.get_value() : $(this.input).val()); }); this.set_value = function(val) { if(!me.last_value)me.last_value=''; if(me.validate) { val = me.validate(val); me.input.value = val==undefined ? '' : val; } me.set(val); if(me.format_input) me.format_input(); if(in_list(['Currency','Float','Int'], me.df.fieldtype)) { if(flt(me.last_value)==flt(val)) { me.last_value = val; return; // do not run trigger } } me.last_value = val; me.run_trigger(); } this.input.set_input = function(val) { if(val==null)val=''; me.input.value = val; if(me.format_input)me.format_input(); } // autosuggest type fields // ----------------------- if(this.df.options=='Suggest') { // add auto suggest if(this.suggest_icon) $di(this.suggest_icon); $(me.input).autocomplete({ source: function(request, response) { wn.call({ method:'webnotes.widgets.search.search_link', args: { 'txt': request.term, 'dt': me.df.options, 'query': repl('SELECT DISTINCT `%(fieldname)s` FROM \ `tab%(dt)s` WHERE `%(fieldname)s` LIKE "%s" LIMIT 50', {fieldname:me.df.fieldname, dt:me.df.parent}) }, callback: function(r) { response(r.results); } }); }, select: function(event, ui) { me.set(ui.item.value); } }); } } DataField.prototype.validate = function(v) { if(this.df.options == 'Phone') { if(v+''=='')return ''; v1 = '' // phone may start with + and must only have numbers later, '-' and ' ' are stripped v = v.replace(/ /g, '').replace(/-/g, '').replace(/\(/g, '').replace(/\)/g, ''); // allow initial +,0,00 if(v && v.substr(0,1)=='+') { v1 = '+'; v = v.substr(1); } if(v && v.substr(0,2)=='00') { v1 += '00'; v = v.substr(2); } if(v && v.substr(0,1)=='0') { v1 += '0'; v = v.substr(1); } v1 += cint(v) + ''; return v1; } else if(this.df.options == 'Email') { if(v+''=='')return ''; if(!validate_email(v)) { msgprint(this.df.label + ': ' + v + ' is not a valid email id'); return ''; } else return v; } else { return v; } } DataField.prototype.onrefresh = function() { if(this.input&&this.df.colour) { var col = '#'+this.df.colour.split(':')[1]; $bg(this.input,col); } } // ====================================================================================== function ReadOnlyField() { } ReadOnlyField.prototype = new Field(); // ====================================================================================== function HTMLField() { } HTMLField.prototype = new Field(); HTMLField.prototype.with_label = 0; HTMLField.prototype.set_disp = function(val) { this.disp_area.innerHTML = val; } HTMLField.prototype.set_input = function(val) { if(val) this.set_disp(val); } HTMLField.prototype.onrefresh = function() { this.set_disp(this.df.options?this.df.options:''); } // ====================================================================================== var datepicker_active = 0; function DateField() { } DateField.prototype = new Field(); DateField.prototype.make_input = function() { var me = this; this.user_fmt = wn.control_panel.date_format; if(!this.user_fmt)this.user_fmt = 'dd-mm-yy'; this.input = $a(this.input_area, 'input'); $(this.input).datepicker({ dateFormat: me.user_fmt.replace('yyyy','yy'), altFormat:'yy-mm-dd', changeYear: true, beforeShow: function(input, inst) { datepicker_active = 1 }, onClose: function(dateText, inst) { datepicker_active = 0; if(_f.cur_grid_cell) _f.cur_grid_cell.grid.cell_deselect(); } }); var me = this; me.input.onchange = function() { // input as dd-mm-yyyy if(this.value==null)this.value=''; if(!this.not_in_form) me.set(dateutil.user_to_str(me.input.value)); me.run_trigger(); } me.input.set_input = function(val) { if(val==null)val=''; else val=dateutil.str_to_user(val); me.input.value = val; } me.get_value = function() { if(me.input.value) return dateutil.user_to_str(me.input.value); } } DateField.prototype.set_disp = function(val) { var v = dateutil.str_to_user(val); if(v==null)v = ''; this.set_disp_html(v); } DateField.prototype.validate = function(v) { if(!v) return; var me = this; this.clear = function() { msgprint ("Date must be in format " + this.user_fmt); me.input.set_input(''); return ''; } var t = v.split('-'); if(t.length!=3) { return this.clear(); } else if(cint(t[1])>12 || cint(t[1])<1) { return this.clear(); } else if(cint(t[2])>31 || cint(t[2])<1) { return this.clear(); } return v; }; // ====================================================================================== // reference when a new record is created via link function LinkField() { } LinkField.prototype = new Field(); LinkField.prototype.make_input = function() { var me = this; if(me.df.no_buttons) { this.txt = $a(this.input_area, 'input'); this.input = this.txt; } else { makeinput_popup(this, 'icon-search', 'icon-play', 'icon-plus'); // setup buttons me.setup_buttons(); me.onrefresh = function() { if(me.can_create && cur_frm.doc.docstatus==0) $(me.btn2).css('display', 'inline-block'); else $dh(me.btn2); } } me.txt.field_object = this; // set onchange triggers me.input.set_input = function(val) { if(val==undefined)val=''; me.txt.value = val; } me.get_value = function() { return me.txt.value; } $(me.txt).autocomplete({ source: function(request, response) { wn.call({ method:'webnotes.widgets.search.search_link', args: { 'txt': request.term, 'dt': me.df.options, 'query': me.get_custom_query() }, callback: function(r) { response(r.results); }, }); }, select: function(event, ui) { me.set_input_value(ui.item.value); } }).data('autocomplete')._renderItem = function(ul, item) { return $('
') .data('item.autocomplete', item) .append(repl('%(label)s