2 * (c) 2005-2009 Matt Brown (http://dowdybrown.com)
4 * Rico is licensed under the Apache License, Version 2.0 (the "License"); you may not use this
5 * file except in compliance with the License. You may obtain a copy of the License at
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software distributed under the
10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
11 * either express or implied. See the License for the specific language governing permissions
12 * and limitations under the License.
15 if(typeof Rico=='undefined') throw("LiveGridForms requires the Rico JavaScript framework");
16 if(typeof RicoUtil=='undefined') throw("LiveGridForms requires the RicoUtil object");
17 if(typeof RicoTranslate=='undefined') throw("LiveGridForms requires the RicoTranslate object");
20 Rico.TableEdit = Class.create(
21 /** @lends Rico.TableEdit# */
24 * @class Supports editing LiveGrid data.
27 initialize: function(liveGrid) {
28 Rico.writeDebugMsg('Rico.TableEdit initialize: '+liveGrid.tableId);
31 maxDisplayLen : 20, // max displayed text field length
32 panelHeight : 200, // size of tabbed panels
34 hoverClass : 'tabHover',
35 selectedClass : 'tabSelected',
36 compact : false, // compact corners
37 RecordName : RicoTranslate.getPhraseById("record"),
38 updateURL : window.location.href, // default is that updates post back to the generating page
39 readOnlyColor : '#AAA', // read-only fields displayed using this color
40 showSaveMsg : 'errors' // disposition of database update responses (full - show full response, errors - show full response for errors and short response otherwise)
42 Object.extend(this.options, liveGrid.options);
43 this.hasWF2=(document.implementation && document.implementation.hasFeature && document.implementation.hasFeature('WebForms', '2.0'));
44 this.menu=liveGrid.menu;
45 this.menu.options.dataMenuHandler=this.editMenu.bind(this);
46 this.menu.ignoreClicks();
47 RicoEditControls.atLoad();
49 this.createKeyArray();
50 this.saveMsg=$(liveGrid.tableId+'_savemsg');
51 Event.observe(document,"click", this.clearSaveMsg.bindAsEventListener(this), false);
52 this.extraMenuItems=new Array();
53 this.responseHandler=this.processResponse.bind(this);
54 Rico.writeDebugMsg("Rico.TableEdit.initialize complete, hasWF2="+this.hasWF2);
57 canDragFunc: function(elem,event) {
58 if (elem.componentFromPoint) {
59 //Rico.writeDebugMsg('canDragFunc: '+elem.tagName+' '+elem.componentFromPoint(event.clientX,event.clientY));
60 var c=elem.componentFromPoint(event.clientX,event.clientY);
61 // for some reason, IE returns outside when this is called inside a frame
62 if (c!='' && c!='outside') return false;
64 return (elem==this.editDiv || elem.tagName=='FORM');
67 createKeyArray: function() {
69 for (var i=0; i<this.grid.columns.length; i++) {
70 if (this.grid.columns[i].format && this.grid.columns[i].format.isKey)
75 createEditDiv: function() {
77 // create editDiv (form)
80 this.editDiv = this.grid.createDiv('edit',document.body);
81 this.editDiv.style.display='none';
82 if (this.options.canEdit || this.options.canAdd) {
84 this.createForm(this.form);
86 var buttonClose=this.createButton(RicoTranslate.getPhraseById("close"));
87 Event.observe(buttonClose,"click", this.cancelEdit.bindAsEventListener(this), false);
88 this.createForm(this.editDiv);
90 this.editDivCreated=true;
91 this.formPopup=new Rico.Popup({ignoreClicks:true, hideOnClick:false, canDragFunc: this.canDragFunc.bind(this) }, this.editDiv);
93 // create responseDialog
95 this.responseDialog = this.grid.createDiv('editResponse',document.body);
96 this.responseDialog.style.display='none';
98 var buttonOK = document.createElement('button');
99 buttonOK.appendChild(document.createTextNode('OK'));
100 buttonOK.onclick=this.ackResponse.bindAsEventListener(this);
101 this.responseDialog.appendChild(buttonOK);
103 this.responseDiv = this.grid.createDiv('editResponseText',this.responseDialog);
105 if (this.panelGroup) {
106 Rico.writeDebugMsg("createEditDiv complete, requestCount="+this.requestCount);
107 setTimeout(this.initPanelGroup.bind(this),50);
111 initPanelGroup: function() {
113 Rico.writeDebugMsg("initPanelGroup: "+this.requestCount);
114 if (this.requestCount>0) return;
115 var wi=parseInt(this.options.panelWidth,10);
117 this.form.style.width=(wi+10)+'px';
118 if (Prototype.Browser.WebKit) this.editDiv.style.display='block'; // this causes display to flash briefly
119 this.options.bgColor = Rico.Color.createColorFromBackground(this.form);
121 this.editDiv.style.display='none';
122 this.options.panelHdrWidth=(Math.floor(wi / this.options.panels.length)-4)+'px';
123 this.Accordion=new Rico.TabbedPanel(this.panelHdr.findAll(this.notEmpty), this.panelContent.findAll(this.notEmpty), this.options);
126 notEmpty: function(v) {
127 return typeof(v)!='undefined';
130 startForm: function() {
131 this.form = document.createElement('form');
133 this.form.onsubmit=function() {return false;};
134 this.editDiv.appendChild(this.form);
136 var tab = document.createElement('table');
137 var row = tab.insertRow(-1);
138 var cell = row.insertCell(-1);
139 var button=cell.appendChild(this.createButton(RicoTranslate.getPhraseById("saveRecord",this.options.RecordName)));
140 Event.observe(button,"click", this.TESubmit.bindAsEventListener(this), false);
141 cell = row.insertCell(-1);
142 button=cell.appendChild(this.createButton(RicoTranslate.getPhraseById("cancel")));
143 Event.observe(button,"click", this.cancelEdit.bindAsEventListener(this), false);
144 this.form.appendChild(tab);
147 this.hiddenFields = document.createElement('div');
148 this.hiddenFields.style.display='none';
149 this.action = this.appendHiddenField(this.grid.tableId+'__action','');
151 for (i=0; i<this.grid.columns.length; i++) {
152 fldSpec=this.grid.columns[i].format;
153 if (fldSpec && fldSpec.FormView && fldSpec.FormView=="hidden")
154 this.appendHiddenField(fldSpec.FieldName,fldSpec.ColData);
156 this.form.appendChild(this.hiddenFields);
159 createButton: function(buttonLabel) {
160 var button = document.createElement('button');
161 button.innerHTML="<span style='text-decoration:underline;'>"+buttonLabel.charAt(0)+"</span>"+buttonLabel.substr(1);
162 button.accessKey=buttonLabel.charAt(0);
166 createPanel: function(i) {
168 for (var j=0; j<this.grid.columns.length; j++) {
169 var fldSpec=this.grid.columns[j].format;
170 if (!fldSpec) continue;
171 if (!fldSpec.EntryType) continue;
172 if (fldSpec.EntryType=='H') continue;
173 var panelIdx=fldSpec.panelIdx || 0;
179 if (!hasFields) return false;
180 this.panelHdr[i] = document.createElement('div');
181 this.panelHdr[i].className='tabHeader';
182 this.panelHdr[i].innerHTML=this.options.panels[i];
183 this.panelHdrs.appendChild(this.panelHdr[i]);
184 this.panelContent[i] = document.createElement('div');
185 this.panelContent[i].className='tabContent';
186 this.panelContents.appendChild(this.panelContent[i]);
190 createForm: function(parentDiv) {
191 var i,div,fldSpec,panelIdx,tables=[];
193 this.panelContent=[];
194 if (this.options.panels) {
195 this.panelGroup = document.createElement('div');
196 this.panelGroup.className='tabPanelGroup';
197 this.panelHdrs = document.createElement('div');
198 this.panelGroup.appendChild(this.panelHdrs);
199 this.panelContents = document.createElement('div');
200 this.panelContents.className='tabContentContainer';
201 this.panelGroup.appendChild(this.panelContents);
202 parentDiv.appendChild(this.panelGroup);
203 if (this.grid.direction=='rtl') {
204 for (i=this.options.panels.length-1; i>=0; i--) {
205 if (this.createPanel(i))
206 tables[i]=this.createFormTable(this.panelContent[i],'tabContent');
209 for (i=0; i<this.options.panels.length; i++) {
210 if (this.createPanel(i))
211 tables[i]=this.createFormTable(this.panelContent[i],'tabContent');
214 parentDiv.appendChild(this.panelGroup);
216 div=document.createElement('div');
217 div.className='noTabContent';
218 tables[0]=this.createFormTable(div);
219 parentDiv.appendChild(div);
221 for (i=0; i<this.grid.columns.length; i++) {
222 fldSpec=this.grid.columns[i].format;
223 if (!fldSpec) continue;
224 panelIdx=fldSpec.panelIdx || 0;
225 if (tables[panelIdx]) this.appendFormField(this.grid.columns[i],tables[panelIdx]);
226 if (typeof fldSpec.pattern=='string') {
227 switch (fldSpec.pattern) {
229 fldSpec.regexp=/^[_a-zA-Z0-9-]+(\.[_a-zA-Z0-9-]+)*@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.(([0-9]{1,3})|([a-zA-Z]{2,3})|(aero|coop|info|museum|name))$/;
231 case 'float-unsigned':
232 fldSpec.regexp=/^\d+(\.\d+)?$/;
235 fldSpec.regexp=/^[-+]?\d+(\.\d+)?$/;
238 fldSpec.regexp=/^\d+$/;
241 fldSpec.regexp=/^[-+]?\d+$/;
244 fldSpec.regexp=new RegExp(fldSpec.pattern);
251 createFormTable: function(div) {
252 var tab=document.createElement('table');
254 div.appendChild(tab);
258 appendHiddenField: function(name,value) {
259 var field=RicoUtil.createFormField(this.hiddenFields,'input','hidden',name,name);
264 appendFormField: function(column, table) {
265 var fmt=column.format;
266 if (!fmt.EntryType) return;
267 if (fmt.EntryType=="H") return;
268 if (fmt.FormView) return;
269 Rico.writeDebugMsg('appendFormField: '+column.displayName+' - '+fmt.EntryType);
270 var row = table.insertRow(-1);
271 var hdr = row.insertCell(-1);
272 column.formLabel=hdr;
273 if (hdr.noWrap) hdr.noWrap=true;
274 var entry = row.insertCell(-1);
275 if (entry.noWrap) entry.noWrap=true;
276 hdr.innerHTML=column.displayName;
277 hdr.id='lbl_'+fmt.FieldName;
280 hdr.className='ricoEditLabelWithHelp';
282 hdr.className='ricoEditLabel';
284 var field, name=fmt.FieldName;
285 switch (fmt.EntryType) {
288 field=RicoUtil.createFormField(entry,'textarea',null,name);
289 field.cols=fmt.TxtAreaCols;
290 field.rows=fmt.TxtAreaRows;
291 field.innerHTML=fmt.ColData;
292 hdr.style.verticalAlign='top';
296 field=RicoUtil.createFormField(entry,'div',null,name);
297 if (fmt.isNullable) this.addSelectNone(field);
298 this.selectValuesRequest(field,fmt);
301 field=RicoUtil.createFormField(entry,'select',null,name);
302 if (fmt.isNullable) this.addSelectNone(field);
303 field.onchange=this.checkSelectNew.bindAsEventListener(this);
304 this.selectValuesRequest(field,fmt);
305 field=document.createElement('span');
306 field.className='ricoEditLabel';
307 field.id='labelnew__'+fmt.FieldName;
308 field.innerHTML=' '+RicoTranslate.getPhraseById('formNewValue').replace(' ',' ');
309 entry.appendChild(field);
310 name='textnew__'+fmt.FieldName;
311 field=RicoUtil.createFormField(entry,'input','text',name,name);
315 field=RicoUtil.createFormField(entry,'select',null,name);
316 if (fmt.isNullable) this.addSelectNone(field);
317 this.selectValuesRequest(field,fmt);
320 if (!fmt.isNullable) fmt.required=true;
321 if (typeof fmt.min=='string') fmt.min=fmt.min.toISO8601Date() || new Date(fmt.min);
322 if (typeof fmt.max=='string') fmt.max=fmt.max.toISO8601Date() || new Date(fmt.max);
324 field=RicoUtil.createFormField(entry,'input','date',name,name);
325 field.required=fmt.required;
326 if (fmt.min) field.min=fmt.min.toISO8601String(3);
327 if (fmt.max) field.max=fmt.max.toISO8601String(3);
328 field.required=fmt.required;
329 fmt.SelectCtl=null; // use the WebForms calendar instead of the Rico calendar
331 field=RicoUtil.createFormField(entry,'input','text',name,name);
333 this.initField(field,fmt);
336 if (!fmt.isNullable) fmt.required=true;
337 if (!fmt.pattern) fmt.pattern='int-signed';
339 field=RicoUtil.createFormField(entry,'input','number',name,name);
340 field.required=fmt.required;
345 field=RicoUtil.createFormField(entry,'input','text',name,name);
347 if (typeof fmt.min=='string') fmt.min=parseInt(fmt.min,10);
348 if (typeof fmt.max=='string') fmt.max=parseInt(fmt.max,10);
349 this.initField(field,fmt);
352 if (!fmt.isNullable) fmt.required=true;
353 if (!fmt.pattern) fmt.pattern='float-signed';
354 field=RicoUtil.createFormField(entry,'input','text',name,name);
355 this.initField(field,fmt);
356 if (typeof fmt.min=='string') fmt.min=parseFloat(fmt.min);
357 if (typeof fmt.max=='string') fmt.max=parseFloat(fmt.max);
360 field=RicoUtil.createFormField(entry,'input','text',name,name);
361 if (!fmt.isNullable && fmt.EntryType!='T') fmt.required=true;
362 this.initField(field,fmt);
367 RicoEditControls.applyTo(column,field);
371 addSelectNone: function(field) {
372 this.addSelectOption(field,this.options.TableSelectNone,RicoTranslate.getPhraseById("selectNone"));
375 initField: function(field,fmt) {
377 field.maxLength=fmt.Length;
378 field.size=Math.min(fmt.Length, this.options.maxDisplayLen);
380 field.value=fmt.ColData;
383 checkSelectNew: function(e) {
384 this.updateSelectNew(Event.element(e));
387 updateSelectNew: function(SelObj) {
388 var vis=(SelObj.value==this.options.TableSelectNew) ? "" : "hidden";
389 $("labelnew__" + SelObj.id).style.visibility=vis;
390 $("textnew__" + SelObj.id).style.visibility=vis;
393 selectValuesRequest: function(elem,fldSpec) {
394 if (fldSpec.SelectValues) {
395 var valueList=fldSpec.SelectValues.split(',');
396 for (var i=0; i<valueList.length; i++)
397 this.addSelectOption(elem,valueList[i],valueList[i],i);
401 Object.extend(options, this.grid.buffer.ajaxOptions);
402 options.parameters = 'id='+fldSpec.FieldName+'&offset=0&page_size=-1';
403 options.onComplete = this.selectValuesUpdate.bind(this);
404 new Ajax.Request(this.grid.buffer.dataSource, options);
405 Rico.writeDebugMsg("selectValuesRequest: "+options.parameters);
409 selectValuesUpdate: function(request) {
410 var response = request.responseXML.getElementsByTagName("ajax-response");
411 Rico.writeDebugMsg("selectValuesUpdate: "+request.status);
412 if (response == null || response.length != 1) return;
413 response=response[0];
414 var error = response.getElementsByTagName('error');
415 if (error.length > 0) {
416 var errmsg=RicoUtil.getContentAsString(error[0],this.grid.buffer.isEncoded);
417 Rico.writeDebugMsg("Data provider returned an error:\n"+errmsg);
418 alert(RicoTranslate.getPhraseById("requestError",errmsg));
421 response=response.getElementsByTagName('response')[0];
422 var id = response.getAttribute("id").slice(0,-8);
423 var rowsElement = response.getElementsByTagName('rows')[0];
424 var rows = this.grid.buffer.dom2jstable(rowsElement);
426 //alert('selectValuesUpdate:'+id+' '+elem.tagName);
427 Rico.writeDebugMsg("selectValuesUpdate: id="+id+' rows='+rows.length);
428 for (var i=0; i<rows.length; i++) {
429 if (rows[i].length>0) {
431 var c1=(rows[i].length>1) ? rows[i][1] : c0;
432 this.addSelectOption(elem,c0,c1,i);
435 if ($('textnew__'+id))
436 this.addSelectOption(elem,this.options.TableSelectNew,RicoTranslate.getPhraseById("selectNewVal"));
438 setTimeout(this.initPanelGroup.bind(this),50);
441 addSelectOption: function(elem,value,text,idx) {
442 switch (elem.tagName.toLowerCase()) {
444 var opt=RicoUtil.createFormField(elem,'input','radio',elem.id+'_'+idx,elem.id);
446 var lbl=document.createElement('label');
449 elem.appendChild(lbl);
452 RicoUtil.addSelectOption(elem,value,text);
457 clearSaveMsg: function() {
458 if (this.saveMsg) this.saveMsg.innerHTML="";
461 addMenuItem: function(menuText,menuAction,enabled) {
462 this.extraMenuItems.push({menuText:menuText,menuAction:menuAction,enabled:enabled});
465 editMenu: function(grid,r,c,onBlankRow) {
467 if (this.grid.buffer.sessionExpired==true || this.grid.buffer.startPos<0) return false;
469 var elemTitle=$('pageTitle');
470 var pageTitle=elemTitle ? elemTitle.innerHTML : document.title;
471 this.menu.addMenuHeading(pageTitle);
472 for (var i=0; i<this.extraMenuItems.length; i++) {
473 this.menu.addMenuItem(this.extraMenuItems[i].menuText,this.extraMenuItems[i].menuAction,this.extraMenuItems[i].enabled);
476 if (onBlankRow==false) {
477 menutxt=RicoTranslate.getPhraseById("editRecord",this.options.RecordName);
478 this.menu.addMenuItem(menutxt,this.editRecord.bindAsEventListener(this),this.options.canEdit);
479 menutxt=RicoTranslate.getPhraseById("deleteRecord",this.options.RecordName);
480 this.menu.addMenuItem(menutxt,this.deleteRecord.bindAsEventListener(this),this.options.canDelete);
481 if (this.options.canClone) {
482 menutxt=RicoTranslate.getPhraseById("cloneRecord",this.options.RecordName);
483 this.menu.addMenuItem(menutxt,this.cloneRecord.bindAsEventListener(this),this.options.canAdd && this.options.canEdit);
486 menutxt=RicoTranslate.getPhraseById("addRecord",this.options.RecordName);
487 this.menu.addMenuItem(menutxt,this.addRecord.bindAsEventListener(this),this.options.canAdd);
491 cancelEdit: function(e) {
493 for (var i=0; i<this.grid.columns.length; i++) {
494 if (this.grid.columns[i].format && this.grid.columns[i].format.SelectCtl)
495 RicoEditControls.close(this.grid.columns[i].format.SelectCtl);
497 this.makeFormInvisible();
498 this.grid.highlightEnabled=true;
499 this.menu.cancelmenu();
503 setField: function(fldnum,fldvalue) {
504 var fldSpec=this.grid.columns[fldnum].format;
505 var e=$(fldSpec.FieldName);
506 var a,i,elems,fldcode,opts,txt;
508 Rico.writeDebugMsg('setField: '+fldSpec.FieldName+'='+fldvalue);
509 switch (e.tagName.toUpperCase()) {
511 elems=e.getElementsByTagName('INPUT');
512 fldcode=this.getLookupValue(fldvalue)[0];
513 for (i=0; i<elems.length; i++)
514 elems[i].checked=(elems[i].value==fldcode);
517 if (fldSpec.EntryType.charAt(1)=='L')
518 fldvalue=this.getLookupValue(fldvalue)[0];
519 if (fldSpec.EntryType=='D') {
520 // remove time data if it exists
521 a=fldvalue.split(/\s|T/);
528 fldcode=this.getLookupValue(fldvalue)[0];
529 //alert('setField SELECT: id='+e.id+'\nvalue='+fldcode+'\nopt cnt='+opts.length)
530 for (i=0; i<opts.length; i++) {
531 if (opts[i].value==fldcode) {
536 if (fldSpec.EntryType=='N') {
537 txt=$('textnew__'+e.id);
538 if (!txt) alert('Warning: unable to find id "textnew__'+e.id+'"');
540 if (e.selectedIndex!=i) e.selectedIndex=opts.length-1;
541 this.updateSelectNew(e);
546 if (fldSpec.EntryType=='tinyMCE' && typeof(tinyMCE)!='undefined' && this.initialized)
547 tinyMCE.updateContent(e.id);
552 getLookupValue: function(value) {
553 switch (typeof value) {
554 case 'number': return [value.toString(),value.toString()];
555 case 'string': return value.match(/<span\s+class=(['"]?)ricolookup\1>(.*)<\/span>/i) ? [RegExp.$2,RegExp.leftContext] : [value,value];
556 default: return ['',''];
560 // use with care: Prototype 1.5 does not include disabled fields in the post-back
561 setReadOnly: function(addFlag) {
562 for (var i=0; i<this.grid.columns.length; i++) {
563 var fldSpec=this.grid.columns[i].format;
564 if (!fldSpec) continue;
565 var e=$(fldSpec.FieldName);
567 var ro=!fldSpec.Writeable || fldSpec.ReadOnly || (fldSpec.InsertOnly && !addFlag) || (fldSpec.UpdateOnly && addFlag);
568 var color=ro ? this.options.readOnlyColor : '';
569 switch (e.tagName.toUpperCase()) {
571 var elems=e.getElementsByTagName('INPUT');
572 for (var j=0; j<elems.length; j++)
573 elems[j].disabled=ro;
576 if (fldSpec.EntryType=='N') {
577 var txt=$('textnew__'+e.id);
586 if (fldSpec.selectIcon) fldSpec.selectIcon.style.display=ro ? 'none' : '';
592 hideResponse: function(msg) {
593 this.responseDiv.innerHTML=msg;
594 this.responseDialog.style.display='none';
597 showResponse: function() {
598 var offset=Position.page(this.grid.outerDiv);
599 offset[1]+=RicoUtil.docScrollTop();
600 this.responseDialog.style.top=offset[1]+"px";
601 this.responseDialog.style.left=offset[0]+"px";
602 this.responseDialog.style.display='';
605 processResponse: function() {
606 var responseText,success=true;
607 var respNodes=Element.select(this.responseDiv,'.ricoFormResponse');
609 // generate a translated response
610 var phraseId=$w(respNodes[0].className)[1];
611 responseText=RicoTranslate.getPhraseById(phraseId,this.options.RecordName);
613 // present the response as sent from the server (untranslated)
614 var ch=this.responseDiv.childNodes;
615 for (var i=ch.length-1; i>=0; i--) {
616 if (ch[i].nodeType==1 && ch[i].nodeName!='P' && ch[i].nodeName!='DIV' && ch[i].nodeName!='BR')
617 this.responseDiv.removeChild(ch[i]);
619 responseText=this.responseDiv.innerHTML.stripTags();
620 success=(responseText.toLowerCase().indexOf('error')==-1);
622 if (success && this.options.showSaveMsg!='full') {
623 this.hideResponse('');
624 this.grid.resetContents();
625 this.grid.buffer.foundRowCount = false;
626 this.grid.buffer.fetch(this.grid.lastRowPos || 0);
627 if (this.saveMsg) this.saveMsg.innerHTML=' '+responseText+' ';
629 this.processCallback(this.options.onSubmitResponse);
632 processCallback: function(callback) {
633 switch (typeof callback) {
634 case 'string': eval(callback); break;
635 case 'function': callback(); break;
639 // called when ok pressed on error response message
640 ackResponse: function() {
641 this.hideResponse('');
642 this.grid.highlightEnabled=true;
645 cloneRecord: function() {
646 this.displayEditForm("ins");
649 editRecord: function() {
650 this.displayEditForm("upd");
653 displayEditForm: function(action) {
654 this.grid.highlightEnabled=false;
655 this.menu.cancelmenu();
656 this.hideResponse(RicoTranslate.getPhraseById('saving'));
657 this.grid.outerDiv.style.cursor = 'auto';
658 this.action.value=action;
659 for (var i=0; i<this.grid.columns.length; i++) {
660 if (this.grid.columns[i].format) {
661 var c=this.grid.columns[i];
662 var v=c.getValue(this.rowIdx);
664 if (c.format.selectDesc)
665 c.format.selectDesc.innerHTML=c._format(v);
666 if (c.format.SelectCtl)
667 RicoEditControls.displayClrImg(c, !c.format.InsertOnly);
670 this.setReadOnly(false);
671 this.key=this.getKey(this.rowIdx);
672 this.makeFormVisible(this.rowIdx);
675 addPrepare: function() {
676 this.hideResponse(RicoTranslate.getPhraseById('saving'));
677 this.setReadOnly(true);
679 this.action.value="ins";
680 for (var i=0; i<this.grid.columns.length; i++) {
681 if (this.grid.columns[i].format) {
682 this.setField(i,this.grid.columns[i].format.ColData);
683 if (this.grid.columns[i].format.SelectCtl)
684 RicoEditControls.resetValue(this.grid.columns[i]);
690 addRecord: function() {
691 this.menu.cancelmenu();
693 this.makeFormVisible(-1);
694 if (this.Accordion) this.Accordion.selectionSet.selectIndex(0);
697 drillDown: function(e,masterColNum,detailColNum) {
698 var cell=Event.element(e || window.event);
699 cell=RicoUtil.getParentByTagName(cell,'div','ricoLG_cell');
701 this.grid.unhighlight();
702 var idx=this.grid.winCellIndex(cell);
703 this.grid.menuIdx=idx; // ensures selection gets cleared when menu is displayed
704 this.grid.highlight(idx);
705 var drillValue=this.grid.columns[masterColNum].getValue(idx.row);
706 for (var i=3; i<arguments.length; i++)
707 arguments[i].setDetailFilter(detailColNum,drillValue);
711 // set filter on a detail grid that is in a master-detail relationship
712 setDetailFilter: function(colNumber,filterValue) {
713 this.grid.setDetailFilter(colNumber,filterValue);
716 makeFormVisible: function(row) {
717 var margin=8; // account for shadow
718 this.editDiv.style.display='block';
721 var editWi=this.editDiv.offsetWidth+margin;
722 var odOffset=Position.page(this.grid.outerDiv);
723 var winWi=RicoUtil.windowWidth();
724 if (editWi+odOffset[0] > winWi)
725 this.editDiv.style.left=(winWi-editWi)+'px';
727 this.editDiv.style.left=(odOffset[0]+1)+'px';
730 var scrTop=RicoUtil.docScrollTop();
731 var editHt=this.editDiv.offsetHeight+margin;
732 var newTop=odOffset[1]+this.grid.hdrHt+scrTop;
733 var bottom=RicoUtil.windowHeight()+scrTop;
735 newTop+=(row+1)*this.grid.rowHeight;
736 if (newTop+editHt>bottom) newTop-=(editHt+this.grid.rowHeight);
738 if (newTop+editHt>bottom) newTop=bottom-editHt;
741 this.processCallback(this.options.formOpen);
742 this.formPopup.openPopup(null,Math.max(newTop,scrTop));
743 this.editDiv.style.visibility='visible';
744 if (this.initialized) return;
747 for (i = 0; i < this.grid.columns.length; i++) {
748 spec=this.grid.columns[i].format;
749 if (!spec || !spec.EntryType || !spec.FieldName) continue;
750 switch (spec.EntryType) {
752 if (typeof tinyMCE!='undefined') tinyMCE.execCommand('mceAddControl', true, spec.FieldName);
757 if (!this.panelGroup) {
758 this.editDiv.style.width=(this.editDiv.offsetWidth-this.grid.options.scrollBarWidth+2)+"px";
759 this.editDiv.style.height=(this.editDiv.offsetHeight-this.grid.options.scrollBarWidth+2)+"px";
762 this.formPopup.openPopup(); // tinyMCE may have changed the dimensions of the form
763 this.initialized=true;
766 makeFormInvisible: function() {
767 this.editDiv.style.visibility='hidden';
768 this.formPopup.closePopup();
769 this.processCallback(this.options.formClose);
772 getConfirmDesc: function(rowIdx) {
773 var desc=this.grid.columns[this.options.ConfirmDeleteCol].cell(rowIdx).innerHTML;
774 desc=this.getLookupValue(desc)[1];
775 return desc.stripTags().unescapeHTML();
778 deleteRecord: function() {
779 this.menu.cancelmenu();
781 switch(this.options.ConfirmDeleteCol){
783 desc=RicoTranslate.getPhraseById("thisRecord",this.options.RecordName);
785 case -2 : // Use key/column header to identify the row
786 for (var k=0; k<this.keys.length; k++) {
788 var value=this.grid.columns[i].getValue(this.rowIdx);
789 value=this.getLookupValue(value)[0];
790 if (desc) desc+=', ';
791 desc+=this.grid.columns[i].displayName+" "+value;
795 desc='\"' + this.getConfirmDesc(this.rowIdx).truncate(50) + '\"';
798 if (!this.options.ConfirmDelete.valueOf || confirm(RicoTranslate.getPhraseById("confirmDelete",desc))) {
799 this.hideResponse(RicoTranslate.getPhraseById('deleting'));
801 var parms=this.action.name+"=del"+this.getKey(this.rowIdx);
802 new Ajax.Updater(this.responseDiv, this.options.updateURL, {parameters:parms,onComplete:this.processResponse.bind(this)});
804 this.menu.cancelmenu();
807 getKey: function(rowIdx) {
809 for (var k=0; k<this.keys.length; k++) {
811 var value=this.grid.columns[i].getValue(rowIdx);
812 value=this.getLookupValue(value)[0];
813 keyHash.set('_k'+i,value);
815 return '&'+keyHash.toQueryString();
818 validationMsg: function(elem,colnum,phraseId) {
819 var col=this.grid.columns[colnum];
820 if (this.Accordion) this.Accordion.openByIndex(col.format.panelIdx);
821 var msg=RicoTranslate.getPhraseById(phraseId," \"" + col.formLabel.innerHTML + "\"");
822 Rico.writeDebugMsg(' Validation error: '+msg);
823 if (col.format.Help) msg+="\n\n"+col.format.Help;
825 setTimeout(function() { try { elem.focus(); elem.select(); } catch(e) {}; }, 10);
829 TESubmit: function(e) {
830 var i,lbl,spec,elem,n;
832 Event.stop(e || event);
833 Rico.writeDebugMsg('Event: TESubmit called to validate input');
835 // check fields that are supposed to be non-blank
837 for (i = 0; i < this.grid.columns.length; i++) {
838 spec=this.grid.columns[i].format;
839 if (!spec || !spec.EntryType || !spec.FieldName) continue;
840 elem=$(spec.FieldName);
842 if (elem.tagName.toLowerCase()!='input') continue;
843 if (elem.type.toLowerCase()!='text') continue;
844 Rico.writeDebugMsg(' Validating field #'+i+' EntryType='+spec.EntryType+' ('+spec.FieldName+')');
847 if (elem.value.length == 0 && spec.required)
848 return this.validationMsg(elem,i,"formPleaseEnter");
851 if (elem.value.length > 0 && spec.regexp && !spec.regexp.test(elem.value))
852 return this.validationMsg(elem,i,"formInvalidFmt");
855 switch (spec.EntryType.charAt(0)) {
856 case 'I': n=parseInt(elem.value,10); break;
857 case 'F': n=parseFloat(elem.value); break;
858 case 'D': n=new Date(); n.setISO8601(elem.value); break;
859 default: n=NaN; break;
861 if (typeof spec.min!='undefined' && !isNaN(n) && n < spec.min)
862 return this.validationMsg(elem,i,"formOutOfRange");
863 if (typeof spec.max!='undefined' && !isNaN(n) && n > spec.max)
864 return this.validationMsg(elem,i,"formOutOfRange");
867 // update drop-down for any columns with entry type of N
869 for (i = 0; i < this.grid.columns.length; i++) {
870 spec=this.grid.columns[i].format;
871 if (!spec || !spec.EntryType || !spec.FieldName) continue;
872 if (spec.EntryType.charAt(0) != 'N') continue;
873 var SelObj=$(spec.FieldName);
874 if (!SelObj || SelObj.value!=this.options.TableSelectNew) continue;
875 var newtext=$("textnew__" + SelObj.id).value;
876 this.addSelectOption(SelObj,newtext,newtext);
879 if (typeof tinyMCE!='undefined') tinyMCE.triggerSave();
880 this.makeFormInvisible();
882 this.menu.cancelmenu();
886 sendForm: function() {
888 var parms=Form.serialize(this.form)+this.key;
889 Rico.writeDebugMsg("sendForm: "+parms);
890 new Ajax.Updater(this.responseDiv, this.options.updateURL, {parameters:parms,onComplete:this.responseHandler});
896 * @namespace Registers custom popup widgets to fill in a text box (e.g. ricoCalendar and ricoTree)
898 * Custom widget must implement:
899 * open() method (make control visible)
900 * close() method (hide control)
901 * container property (div element that contains the control)
902 * id property (uniquely identifies the widget class)
904 * widget calls returnValue method to return a value to the caller
906 * this object handles clicks on the control's icon and positions the control appropriately.
909 var RicoEditControls = {
912 clearImg : Rico.imgDir+'delete.gif',
914 register: function(widget, imgsrc) {
915 this.widgetList.set(widget.id, {imgsrc:imgsrc, widget:widget, currentEl:''});
916 widget.returnValue=this.setValue.bind(this,widget);
917 Rico.writeDebugMsg("RicoEditControls.register:"+widget.id);
921 this.widgetList.each(function(pair) { if (pair.value.widget.atLoad) pair.value.widget.atLoad(); });
924 applyTo: function(column,inputCtl) {
925 var wInfo=this.widgetList.get(column.format.SelectCtl);
927 Rico.writeDebugMsg('RicoEditControls.applyTo: '+column.displayName+' : '+column.format.SelectCtl);
928 var descSpan = document.createElement('span');
929 var newimg = document.createElement('img');
930 newimg.style.paddingLeft='4px';
931 newimg.style.cursor='pointer';
933 newimg.src=wInfo.imgsrc;
934 newimg.id=this.imgId(column.format.FieldName);
935 newimg.onclick=this.processClick.bindAsEventListener(this);
936 inputCtl.parentNode.appendChild(descSpan);
937 inputCtl.parentNode.appendChild(newimg);
938 inputCtl.style.display='none'; // comment out this line for debugging
939 if (column.format.isNullable) {
940 var clrimg = document.createElement('img');
941 clrimg.style.paddingLeft='4px';
942 clrimg.style.cursor='pointer';
944 clrimg.src=this.clearImg;
945 clrimg.id=newimg.id+'_clear';
946 clrimg.alt=RicoTranslate.getPhraseById('clear');
947 clrimg.onclick=this.processClear.bindAsEventListener(this);
948 inputCtl.parentNode.appendChild(clrimg);
950 this.elemList.set(newimg.id, {descSpan:descSpan, inputCtl:inputCtl, widget:wInfo.widget, listObj:wInfo, column:column, clrimg:clrimg});
951 column.format.selectIcon=newimg;
952 column.format.selectDesc=descSpan;
955 displayClrImg: function(column,bool) {
956 var el=this.elemList.get(this.imgId(column.format.FieldName));
957 if (el && el.clrimg) el.clrimg.style.display=bool ? '' : 'none';
960 processClear: function(e) {
961 var elem=Event.element(e);
962 var el=this.elemList.get(elem.id.slice(0,-6));
964 el.inputCtl.value='';
965 el.descSpan.innerHTML=el.column._format('');
968 processClick: function(e) {
969 var elem=Event.element(e);
970 var el=this.elemList.get(elem.id);
972 if (el.listObj.currentEl==elem.id && el.widget.container.style.display!='none') {
974 el.listObj.currentEl='';
976 el.listObj.currentEl=elem.id;
977 Rico.writeDebugMsg('RicoEditControls.processClick: '+el.widget.id+' : '+el.inputCtl.value);
978 el.widget.open(el.inputCtl.value); // this may change the size of the widget
979 RicoUtil.positionCtlOverIcon(el.widget.container,elem);
980 if (el.widget.move) el.widget.move(); // if widget is a Rico.Popup object, ensure shim and shadow follow
984 imgId: function(fieldname) {
985 return 'icon_'+fieldname;
988 resetValue: function(column) {
989 var el=this.elemList.get(this.imgId(column.format.FieldName));
991 el.inputCtl.value=column.format.ColData;
992 el.descSpan.innerHTML=column._format(column.format.ColData);
995 setValue: function(widget,newVal,newDesc) {
996 var wInfo=this.widgetList.get(widget.id);
997 if (!wInfo) return null;
998 var id=wInfo.currentEl;
999 if (!id) return null;
1000 var el=this.elemList.get(id);
1001 if (!el) return null;
1002 el.inputCtl.value=newVal;
1003 if (!newDesc) newDesc=el.column._format(newVal);
1004 el.descSpan.innerHTML=newDesc;
1005 //alert(widget.id+':'+id+':'+el.inputCtl.id+':'+el.inputCtl.value+':'+newDesc);
1008 close: function(id) {
1009 var wInfo=this.widgetList.get(id);
1011 if (wInfo.widget.container.style.display!='none')
1012 wInfo.widget.close();
1016 Rico.includeLoaded('ricoLiveGridForms.js');