/* * (c) 2005-2009 Richard Cowin (http://openrico.org) * (c) 2005-2009 Matt Brown (http://dowdybrown.com) * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or implied. See the License for the specific language governing permissions * and limitations under the License. */ if(typeof Rico=='undefined') throw("LiveGridForms requires the Rico JavaScript framework"); Rico.TableEdit = function(liveGrid) { this.initialize(liveGrid); } Rico.TableEdit.prototype = { /** * @class Supports editing LiveGrid data. * @constructs */ initialize: function(liveGrid) { Rico.log('Rico.TableEdit initialize: '+liveGrid.tableId); this.grid=liveGrid; this.options = { maxDisplayLen : 20, // max displayed text field length panelHeight : 200, // size of tabbed panels panelWidth : 500, compact : false, // compact corners RecordName : Rico.getPhraseById("record"), updateURL : window.location.href, // default is that updates post back to the generating page showSaveMsg : 'errors' // disposition of database update responses (full - show full response, errors - show full response for errors and short response otherwise) }; Rico.extend(this.options, liveGrid.options); var self=this; this.menu=liveGrid.menu; this.menu.options.dataMenuHandler=function(grid,r,c,onBlankRow) { return self.editMenu(grid,r,c,onBlankRow); }; this.menu.ignoreClicks(); this.editText=Rico.getPhraseById("editRecord",this.options.RecordName); this.cloneText=Rico.getPhraseById("cloneRecord",this.options.RecordName); this.delText=Rico.getPhraseById("deleteRecord",this.options.RecordName); this.addText=Rico.getPhraseById("addRecord",this.options.RecordName); this.buttonHover=new Rico.HoverSet(); this.dateRegExp=/^\s*(\w+)(\W)(\w+)(\W)(\w+)/i; Rico.EditControls.atLoad(); this.createKeyArray(); this.createEditDiv(); this.saveMsg=Rico.$(liveGrid.tableId+'_savemsg'); Rico.eventBind(document,"click", Rico.eventHandle(this,'clearSaveMsg')); this.extraMenuItems=[]; this.responseHandler=function(xhr) { self.processResponse(xhr); }; Rico.log("Rico.TableEdit.initialize complete"); }, createKeyArray: function() { this.keys=[]; for (var i=0; i0) return; var wi=parseInt(this.options.panelWidth,10); if (this.form) { //this.form.style.width=(wi+10)+'px'; if (Rico.isWebKit) this.formPopup.container.style.display='block'; // this causes display to flash briefly this.options.bgColor = Rico.Color.createColorFromBackground(this.form).toString(); } this.formPopup.container.style.display='none'; this.formPanels=new Rico.TabbedPanel(this.panelGroup, this.options); }, notEmpty: function(v) { return typeof(v)!='undefined'; }, startForm: function() { this.form = document.createElement('form'); /** @ignore */ this.form.onsubmit=function() {return false;}; this.form.autocomplete="off"; // seems to fix "Permission denied..." errors in FF this.formPopup.contentDiv.appendChild(this.form); var tab = document.createElement('div'); tab.className='ButtonBar'; var button=tab.appendChild(this.createButton(Rico.getPhraseById("saveRecord",this.options.RecordName))); Rico.eventBind(button,"click", Rico.eventHandle(this,'TESubmit'), false); button=tab.appendChild(this.createButton(Rico.getPhraseById("cancel"))); Rico.eventBind(button,"click", Rico.eventHandle(this,'cancelEdit'), false); this.form.appendChild(tab); // hidden fields this.hiddenFields = document.createElement('div'); this.hiddenFields.style.display='none'; this.action = this.appendHiddenField(this.grid.actionId,''); var i,fldSpec; for (i=0; i=0; i--) { tables[i]=this.createPanel(i); } } else { for (i=0; i 0 ? table.rows[table.rows.length-1] : table.insertRow(-1); var hdr = row.insertCell(-1); column.formLabel=hdr; if (hdr.noWrap) hdr.noWrap=true; var entry = row.insertCell(-1); if (entry.noWrap) entry.noWrap=true; hdr.id='lbl_'+fmt.FieldName; var field, name=fmt.FieldName; switch (fmt.EntryType) { case 'TA': case 'tinyMCE': field=Rico.createFormField(entry,'textarea',null,name); field.cols=fmt.TxtAreaCols; field.rows=fmt.TxtAreaRows; field.innerHTML=fmt.ColData; hdr.style.verticalAlign='top'; break; case 'R': case 'RL': field=Rico.createFormField(entry,'div',null,name); if (fmt.DescriptionField) field.RicoUpdate=fmt.DescriptionField; if (fmt.MultiSelect) Rico.addClass(field, 'MultiSelect'); if (fmt.isNullable && !fmt.MultiSelect) this.addSelectNone(field); this.selectValuesRequest(field,column); break; case 'N': field=Rico.createFormField(entry,'select',null,name); if (fmt.isNullable) this.addSelectNone(field); Rico.eventBind(field,"change", Rico.eventHandle(this,'checkSelectNew')); this.selectValuesRequest(field,column); field=document.createElement('span'); field.className='ricoEditLabel'; field.id='labelnew__'+fmt.FieldName; field.innerHTML='   '+Rico.getPhraseById('formNewValue').replace(' ',' '); entry.appendChild(field); name='textnew__'+fmt.FieldName; field=Rico.createFormField(entry,'input','text',name,name); break; case 'S': case 'SL': if (fmt.ReadOnly) { field=Rico.createFormField(entry,'input','text',name,name); this.initField(field,fmt); } else { field=Rico.createFormField(entry,'select',null,name); if (fmt.MultiSelect) field.multiple=true; if (fmt.SelectRows) field.size=parseInt(fmt.SelectRows,10); if (fmt.isNullable && !fmt.MultiSelect) this.addSelectNone(field); if (fmt.DescriptionField) { field.RicoUpdate=fmt.DescriptionField; Rico.eventBind(field,"change", Rico.eventHandle(this,'selectClick'), false); } this.selectValuesRequest(field,column); } break; case 'D': if (!fmt.isNullable) fmt.required=true; if (!fmt.dateFmt) fmt.dateFmt=Rico.dateFmt; if (!fmt.Help) fmt.Help=fmt.dateFmt; if (typeof fmt.min=='string') fmt.min=Rico.setISO8601(fmt.min) || new Date(fmt.min); if (typeof fmt.max=='string') fmt.max=Rico.setISO8601(fmt.max) || new Date(fmt.max); if (Rico.inputtypes.date) { field=Rico.createFormField(entry,'input','date',name,name); field.required=fmt.required; if (fmt.min) field.min=Rico.toISO8601String(fmt.min,3); if (fmt.max) field.max=Rico.toISO8601String(fmt.max,3); field.required=fmt.required; fmt.SelectCtl=null; // use the WebForms calendar instead of the Rico calendar } else { field=Rico.createFormField(entry,'input','text',name,name); } this.initField(field,fmt); break; case 'I': if (!fmt.isNullable) fmt.required=true; if (!fmt.pattern) fmt.pattern='int-signed'; if (Rico.inputtypes.number) { field=Rico.createFormField(entry,'input','number',name,name); field.required=fmt.required; field.min=fmt.min; field.max=fmt.max; field.step=1; } else { field=Rico.createFormField(entry,'input','text',name,name); } if (typeof fmt.min=='string') fmt.min=parseInt(fmt.min,10); if (typeof fmt.max=='string') fmt.max=parseInt(fmt.max,10); this.initField(field,fmt); break; case 'F': if (!fmt.isNullable) fmt.required=true; if (!fmt.pattern) fmt.pattern='float-signed'; field=Rico.createFormField(entry,'input','text',name,name); this.initField(field,fmt); if (typeof fmt.min=='string') fmt.min=parseFloat(fmt.min); if (typeof fmt.max=='string') fmt.max=parseFloat(fmt.max); break; default: field=Rico.createFormField(entry,'input','text',name,name); if (!fmt.isNullable && fmt.EntryType!='T') fmt.required=true; this.initField(field,fmt); break; } if (field && fmt.SelectCtl) { Rico.EditControls.applyTo(column,field); } var hdrSuffix=''; hdr.className='ricoEditLabel'; if (fmt.Help) { hdr.title=fmt.Help; hdrSuffix=" "; } var hdrText=fmt.EntryType.length>1 && fmt.EntryType.charAt(1)=='L' ? column.next.displayName : column.displayName; hdr.innerHTML=hdrText+hdrSuffix; }, addSelectNone: function(field) { this.addSelectOption(field,this.options.TableSelectNone,Rico.getPhraseById("selectNone")); }, initField: function(field,fmt) { if (fmt.Length) { field.maxLength=fmt.Length; field.size=Math.min(fmt.Length, this.options.maxDisplayLen); } field.value=fmt.ColData; }, selectClick: function(e) { var SelObj=Rico.eventElement(e); if (SelObj.readOnly) { Rico.eventStop(e); return false; } if (SelObj.RicoUpdate) { var opt=SelObj.options[SelObj.selectedIndex]; Rico.$(SelObj.RicoUpdate).value=opt.innerHTML; } }, radioClick: function(e) { var ChkBoxObj=Rico.eventElement(e); if (ChkBoxObj.readOnly) { Rico.eventStop(e); return false; } var container=Rico.getParentByTagName(ChkBoxObj,'div'); if (container.RicoUpdate) { Rico.$(container.RicoUpdate).value=ChkBoxObj.nextSibling.innerHTML; } }, checkSelectNew: function(e) { this.updateSelectNew(Rico.eventElement(e)); }, updateSelectNew: function(SelObj) { var vis=(SelObj.value==this.options.TableSelectNew) ? "" : "hidden"; Rico.$("labelnew__" + SelObj.id).style.visibility=vis; Rico.$("textnew__" + SelObj.id).style.visibility=vis; }, selectValuesRequest: function(elem,column) { var fldSpec=column.format; if (fldSpec.SelectValues) { var valueList=fldSpec.SelectValues.split(','); for (var i=0; i 0) { var errmsg=Rico.getContentAsString(error[0],this.grid.buffer.isEncoded); Rico.log("Data provider returned an error:\n"+errmsg); alert(Rico.getPhraseById("requestError",errmsg)); return; } response=response.getElementsByTagName('response')[0]; var rowsElement = response.getElementsByTagName('rows')[0]; var rows = this.grid.buffer.dom2jstable(rowsElement); Rico.log("selectValuesUpdate: id="+elem.id+' rows='+rows.length); for (var i=0; i0) { var c0=rows[i][0]; var c1=(rows[i].length>1) ? rows[i][1] : c0; this.addSelectOption(elem,c0,c1,i); } } if (Rico.$('textnew__'+elem.id)) this.addSelectOption(elem,this.options.TableSelectNew,Rico.getPhraseById("selectNewVal")); if (this.panelGroup) Rico.runLater(50,this,'initPanelGroup'); }, addSelectOption: function(elem,value,text,idx) { switch (elem.tagName.toLowerCase()) { case 'div': var opt=Rico.createFormField(elem,'input', Rico.hasClass(elem, 'MultiSelect') ? 'checkbox' : 'radio', elem.id+'_'+idx, elem.id); opt.value=value; var lbl=document.createElement('label'); lbl.innerHTML=text; lbl.htmlFor=opt.id; elem.appendChild(lbl); Rico.eventBind(opt,"click", Rico.eventHandle(this,'radioClick'), false); break; case 'select': Rico.addSelectOption(elem,value,text); break; } }, clearSaveMsg: function() { if (this.saveMsg) this.saveMsg.innerHTML=""; }, addMenuItem: function(menuText,menuAction,enabled) { this.extraMenuItems.push({menuText:menuText,menuAction:menuAction,enabled:enabled}); }, editMenu: function(grid,r,c,onBlankRow) { this.clearSaveMsg(); if (this.grid.buffer.sessionExpired==true || this.grid.buffer.startPos<0) return false; this.rowIdx=r; var elemTitle=Rico.$('pageTitle'); var pageTitle=elemTitle ? elemTitle.innerHTML : document.title; this.menu.addMenuHeading(pageTitle); var self=this; if (onBlankRow==false) { for (var i=0; i=0; i--) { if (ch[i].nodeType==1 && ch[i].nodeName!='P' && ch[i].nodeName!='DIV' && ch[i].nodeName!='BR') this.responseDiv.removeChild(ch[i]); } responseText=Rico.stripTags(this.responseDiv.innerHTML); success=(responseText.toLowerCase().indexOf('error')==-1); } if (success && this.options.showSaveMsg!='full') { this.hideResponse(''); this.grid.resetContents(); this.grid.buffer.foundRowCount = false; this.grid.buffer.fetch(this.grid.lastRowPos || 0); if (this.saveMsg) this.saveMsg.innerHTML=' '+responseText+' '; } this.processCallback(this.options.onSubmitResponse); Rico.log('Processing response completed'); }, processCallback: function(callback) { switch (typeof callback) { case 'string': return eval(callback); case 'function': return callback(); } }, // called when ok pressed on error response message ackResponse: function(e) { this.hideResponse(''); this.grid.highlightEnabled=true; }, cloneRecord: function() { this.formPopup.setTitle(this.cloneText); this.displayEditForm("ins"); }, editRecord: function() { this.formPopup.setTitle(this.editText); this.displayEditForm("upd"); }, displayEditForm: function(action) { this.grid.highlightEnabled=false; this.menu.cancelmenu(); this.hideResponse(Rico.getPhraseById('saving')); this.grid.outerDiv.style.cursor = 'auto'; this.action.value=action; for (var i=0; i1 && c.format.EntryType.charAt(1)=='L') v=this.grid.columns[i+1].getValue(this.rowIdx); v=c._format(v); if (v==='') v=' '; c.format.selectDesc.innerHTML=v; } if (c.format.SelectCtl) Rico.EditControls.displayClrImg(c, !c.format.InsertOnly); } } this.setReadOnly(action); for (var k=0; k winWi ? (winWi-editWi)+'px' : (odOffset.left+1)+'px'; // set top position var scrTop=Rico.docScrollTop(); var editHt=this.formPopup.container.offsetHeight; var newTop=odOffset.top+this.grid.hdrHt+scrTop; var bottom=Rico.windowHeight()+scrTop; if (row >= 0) { newTop+=(row+1)*this.grid.rowHeight; if (newTop+editHt>bottom) newTop-=(editHt+this.grid.rowHeight); } else { if (newTop+editHt>bottom) newTop=bottom-editHt-2; } if (this.processCallback(this.options.formOpen) === false) return; this.formPopup.openPopup(null,Math.max(newTop,scrTop)); this.formPopup.container.style.visibility='visible'; Rico.EditControls.setZ(Rico.getStyle(this.formPopup.container,'zIndex')); if (this.initialized) return; var i, spec; for (i = 0; i < this.grid.columns.length; i++) { spec=this.grid.columns[i].format; if (!spec || !spec.EntryType || !spec.FieldName) continue; switch (spec.EntryType) { case 'tinyMCE': if (typeof tinyMCE!='undefined') tinyMCE.execCommand('mceAddControl', true, spec.FieldName); break; } } this.initialized=true; }, makeFormInvisible: function() { for (var i=0; i1 && fmt.EntryType.charAt(1)=='L') i++; var value=Rico.stripTags(this.grid.cell(rowIdx,i).innerHTML).replace(/ /g,' '); if (desc) desc+=', '; desc+=this.grid.columns[i].displayName+" "+value; } break; default : desc='\"' + Rico.truncate(this.getConfirmDesc(this.rowIdx),50) + '\"'; break; } if (!this.options.ConfirmDelete.valueOf || confirm(Rico.getPhraseById("confirmDelete",desc))) { this.hideResponse(Rico.getPhraseById('deleting')); this.showResponse(); var parms={}; parms[this.grid.actionId]="del"; for (var k=0; k31) return NaN; var mm=parseInt(aDate[dateParts.mm], 10) - 1; if (mm > 11) return NaN; var yy=parseInt(aDate[dateParts.yyyy], 10); if (yy < 100) { // apply a century to 2-digit years yy+=curyr - (curyr % 100); } d.setFullYear(yy,mm,dd); return d; }, TESubmit: function(e) { var i,ro,lbl,spec,elem,n,dateValues=[]; Rico.eventStop(e); Rico.log('Event: TESubmit called to validate input'); // check fields that are supposed to be non-blank for (i = 0; i < this.grid.columns.length; i++) { spec=this.grid.columns[i].format; if (!spec || !spec.EntryType || !spec.FieldName) continue; elem=Rico.$(spec.FieldName); if (!this.isTextInput(elem)) continue; switch (this.action.value) { case 'ins': ro=!spec.Writeable || spec.ReadOnly || spec.UpdateOnly; break; case 'upd': ro=!spec.Writeable || spec.ReadOnly || spec.InsertOnly; break; default: ro=false; break; } if (ro) continue; // readonly, so don't validate Rico.log(' Validating field #'+i+' EntryType='+spec.EntryType+' ('+spec.FieldName+')'); // check for blanks if (elem.value.length == 0) { if (spec.required) return this.validationMsg(elem,i,"formPleaseEnter"); else continue; } // check pattern if (elem.value.length > 0 && spec.regexp && !spec.regexp.test(elem.value)) return this.validationMsg(elem,i,"formInvalidFmt"); // check min/max and date values switch (spec.EntryType.charAt(0)) { case 'I': n=parseInt(elem.value,10); break; case 'F': n=parseFloat(elem.value); break; case 'D': n=this.parseDate(elem.value,spec.dateFmt); if (isNaN(n)) return this.validationMsg(elem,i,"formInvalidFmt"); dateValues.push({e:elem,v:n}); break; default: n=NaN; break; } if (typeof spec.min!='undefined' && !isNaN(n) && n < spec.min) return this.validationMsg(elem,i,"formOutOfRange"); if (typeof spec.max!='undefined' && !isNaN(n) && n > spec.max) return this.validationMsg(elem,i,"formOutOfRange"); } if (this.processCallback(this.options.formSubmit) === false) return false; // update drop-down for any columns with entry type of N for (i = 0; i < this.grid.columns.length; i++) { spec=this.grid.columns[i].format; if (!spec || !spec.EntryType || !spec.FieldName) continue; if (spec.EntryType.charAt(0) != 'N') continue; var SelObj=Rico.$(spec.FieldName); if (!SelObj || SelObj.value!=this.options.TableSelectNew) continue; var newtext=Rico.$("textnew__" + SelObj.id).value; this.addSelectOption(SelObj,newtext,newtext); } // set date values to ISO format for (i = 0; i < dateValues.length; i++) { dateValues[i].e.value = Rico.formatDate(dateValues[i].v,'yyyy-mm-dd'); } if (typeof tinyMCE!='undefined') tinyMCE.triggerSave(); this.makeFormInvisible(); this.sendForm(); this.menu.cancelmenu(); return false; }, sendForm: function() { this.setReadOnly("reset"); // reset disabled flag so that all fields are sent to server this.showResponse(); Rico.log("sendForm: "+this.grid.tableId); Rico.ajaxSubmit(this.form, this.options.updateURL, {method:'post',onComplete:this.responseHandler}); } }; /** * @namespace Registers custom popup widgets to fill in a text box (e.g. ricoCalendar and ricoTree) *
 * Custom widget must implement:
 *   open() method (make control visible)
 *   close() method (hide control)
 *   container property (div element that contains the control)
 *   id property (uniquely identifies the widget class)
 *
 * widget calls returnValue method to return a value to the caller
 *
 * this object handles clicks on the control's icon and positions the control appropriately.
 * 
*/ Rico.EditControls = { widgetList : {}, elemList : {}, zIndex : 0, register: function(widget, imgsrc) { this.widgetList[widget.id] = {imgsrc:imgsrc, widget:widget, currentEl:''}; var self=this; widget.returnValue=function(newVal,newDesc) { self.setValue(widget,newVal,newDesc); }; Rico.log("Rico.EditControls.register:"+widget.id); }, setZ: function(z) { this.zIndex=Math.max(this.zIndex,z+10); }, atLoad: function() { for (var id in this.widgetList) { var widget=this.widgetList[id].widget; if (widget.atLoad && !widget.atLoadComplete) { Rico.log("Rico.EditControls.atLoad: "+id); widget.atLoad(); widget.atLoadComplete=true; } } }, applyTo: function(column,inputCtl) { var wInfo=this.widgetList[column.format.SelectCtl]; if (!wInfo) return; Rico.log('Rico.EditControls.applyTo: '+column.displayName+' : '+column.format.SelectCtl); var newimg, descSpan = document.createElement('span'); if (wInfo.imgsrc.indexOf('.')==-1 && wInfo.imgsrc.indexOf('/')==-1) { // treat imgsrc as a class name newimg = document.createElement('span'); newimg.className=wInfo.imgsrc; } else { // treat imgsrc as an image uri newimg = document.createElement('img'); newimg.src=wInfo.imgsrc; } newimg.style.verticalAlign='top'; newimg.style.marginLeft='4px'; newimg.style.cursor='pointer'; newimg.id=this.imgId(column.format.FieldName); Rico.eventBind(newimg,"click", Rico.eventHandle(this,'processClick')); inputCtl.parentNode.appendChild(descSpan); inputCtl.parentNode.appendChild(newimg); inputCtl.style.display='none'; // comment out this line for debugging var clr; if (column.format.isNullable) { clr=Rico.clearButton(Rico.eventHandle(this,'processClear')); clr.id=newimg.id+'_clear'; inputCtl.parentNode.appendChild(clr); } this.elemList[newimg.id] = {descSpan:descSpan, inputCtl:inputCtl, widget:wInfo.widget, listObj:wInfo, column:column, clrimg:clr}; column.format.selectIcon=newimg; column.format.selectDesc=descSpan; }, displayClrImg: function(column,bShow) { var el=this.elemList[this.imgId(column.format.FieldName)]; //alert(column.format.FieldName+': '+bShow+' '+el.clrimg.id); if (el && el.clrimg) el.clrimg.style.display=bShow ? 'inline-block' : 'none'; }, processClear: function(e) { var elem=Rico.eventElement(e); var el=this.elemList[elem.id.slice(0,-6)]; if (!el) return; el.inputCtl.value=''; el.descSpan.innerHTML=el.column._format(''); }, processClick: function(e) { var elem=Rico.eventElement(e); var el=this.elemList[elem.id]; if (!el) return; if (el.listObj.currentEl==elem.id && el.widget.container.style.display!='none') { el.widget.close(); el.listObj.currentEl=''; } else { el.listObj.currentEl=elem.id; Rico.log('Rico.EditControls.processClick: '+el.widget.id+' : '+el.inputCtl.value); el.widget.container.style.zIndex=this.zIndex; el.widget.open(el.inputCtl.value,el.column); // this may change the size of the widget Rico.positionCtlOverIcon(el.widget.container,elem); } }, imgId: function(fieldname) { return 'icon_'+fieldname; }, resetValue: function(column) { var el=this.elemList[this.imgId(column.format.FieldName)]; if (!el) return; el.inputCtl.value=column.format.ColData; var v=column._format(column.format.ColData); if (v==='') v=' '; el.descSpan.innerHTML=v; }, setValue: function(widget,newVal,newDesc) { var wInfo=this.widgetList[widget.id]; if (!wInfo) return null; var id=wInfo.currentEl; if (!id) return null; var el=this.elemList[id]; if (!el) return null; el.inputCtl.value=newVal; if (!newDesc) newDesc=el.column._format(newVal); el.descSpan.innerHTML=newDesc; if (el.column.format.DescriptionField) Rico.$(el.column.format.DescriptionField).value = newDesc; //alert(widget.id+':'+id+':'+el.inputCtl.id+':'+el.inputCtl.value+':'+newDesc); }, close: function(id) { var wInfo=this.widgetList[id]; if (!wInfo) return; if (wInfo.widget.container.style.display!='none') wInfo.widget.close(); } };