2 * (c) 2005-2009 Richard Cowin (http://openrico.org)
3 * (c) 2005-2009 Matt Brown (http://dowdybrown.com)
5 * Rico is licensed under the Apache License, Version 2.0 (the "License"); you may not use this
6 * file except in compliance with the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software distributed under the
11 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
12 * either express or implied. See the License for the specific language governing permissions
13 * and limitations under the License.
16 if(typeof Rico=='undefined') throw("GridCommon requires the Rico JavaScript framework");
17 if(typeof RicoUtil=='undefined') throw("GridCommon requires the RicoUtil Library");
21 * @class Define methods that are common to both SimpleGrid and LiveGrid
23 Rico.GridCommon = function() {};
25 Rico.GridCommon.prototype = {
27 baseInit: function() {
29 resizeBackground : 'resize.gif',
30 saveColumnInfo : {width:true, filter:false, sort:false}, // save info in cookies?
31 cookiePrefix : 'RicoGrid.',
32 allowColResize : true, // allow user to resize columns
33 windowResize : true, // Resize grid on window.resize event? Set to false when embedded in an accordian.
37 useUnformattedColWidth : true,
38 menuEvent : 'dblclick', // event that triggers menus - click, dblclick, contextmenu, or none (no menus)
39 defaultWidth : 100, // in the absence of any other width info, columns will be this many pixels wide
40 scrollBarWidth : 19, // this is the value used in positioning calculations, it does not actually change the width of the scrollbar
41 minScrollWidth : 100, // min scroll area width when width of frozen columns exceeds window width
42 exportWindow : "height=400,width=500,scrollbars=1,menubar=1,resizable=1",
43 exportStyleList : ['background-color','color','text-align','font-weight','font-size','font-family'],
44 exportImgTags : false, // applies to grid header and to SimpleGrid cells (not LiveGrid cells)
45 exportFormFields : true,
46 FilterLocation : null, // heading row number to place filters. -1=add a new heading row.
47 FilterAllToken : '___ALL___', // select box value to use to indicate ALL
53 this.headerRowIdx=0; // row in header which gets resizers (no colspan's in this row)
54 this.tabs=new Array(2);
55 this.thead=new Array(2);
56 this.tbody=new Array(2);
59 attachMenuEvents: function() {
61 if (!this.options.menuEvent || this.options.menuEvent=='none') return;
62 this.hideScroll=navigator.userAgent.match(/Macintosh\b.*\b(Firefox|Camino)\b/i) || (Prototype.Browser.Opera && parseFloat(window.opera.version())<9.5);
63 this.options[this.options.menuEvent]=this.handleMenuClick.bindAsEventListener(this);
64 if (this.highlightDiv) {
65 switch (this.options.highlightElem) {
67 this.attachMenu(this.highlightDiv[0]);
71 this.attachMenu(this.highlightDiv[i]);
77 this.attachMenu(this.tbody[i]);
81 attachMenu: function(elem) {
82 if (this.options.click)
83 Event.observe(elem, 'click', this.options.click, false);
84 if (this.options.dblclick) {
85 if (Prototype.Browser.WebKit || Prototype.Browser.Opera)
86 Event.observe(elem, 'click', this.handleDblClick.bindAsEventListener(this), false);
88 Event.observe(elem, 'dblclick', this.options.dblclick, false);
90 if (this.options.contextmenu) {
91 if (Prototype.Browser.Opera || Rico.isKonqueror)
92 Event.observe(elem, 'click', this.handleContextMenu.bindAsEventListener(this), false);
94 Event.observe(elem, 'contextmenu', this.options.contextmenu, false);
99 * implement double-click for browsers that don't support a double-click event (e.g. Safari)
101 handleDblClick: function(e) {
102 var elem=Event.element(e);
103 if (this.dblClickElem == elem) {
104 this.options.dblclick(e);
106 this.dblClickElem = elem;
107 this.safariTimer=setTimeout(this.clearDblClick.bind(this),300);
111 clearDblClick: function() {
112 this.dblClickElem=null;
116 * implement right-click for browsers that don't support contextmenu event (e.g. Opera, Konqueror)
117 * use control-click instead
119 handleContextMenu: function(e) {
121 if( typeof( e.which ) == 'number' )
122 b = e.which; //Netscape compatible
123 else if( typeof( e.button ) == 'number' )
127 if (b==1 && e.ctrlKey) {
128 this.options.contextmenu(e);
132 cancelMenu: function() {
133 if (this.menu && this.menu.isVisible()) this.menu.cancelmenu();
137 * gather info from original headings
139 getColumnInfo: function(hdrSrc) {
140 Rico.writeDebugMsg('getColumnInfo: len='+hdrSrc.length);
141 if (hdrSrc.length == 0) return 0;
142 this.headerRowCnt=hdrSrc.length;
144 for (r=0; r<this.headerRowCnt; r++) {
145 var headerRow = hdrSrc[r];
146 var headerCells=headerRow.cells;
147 if (r >= this.hdrCells.length) this.hdrCells[r]=[];
148 for (c=0; c<headerCells.length; c++) {
150 obj.cell=headerCells[c];
151 obj.colSpan=headerCells[c].colSpan || 1; // Safari & Konqueror return default colspan of 0
152 if (this.options.useUnformattedColWidth) obj.initWidth=headerCells[c].offsetWidth;
153 this.hdrCells[r].push(obj);
155 if (headerRow.id.slice(-5)=='_main') {
156 colcnt=this.hdrCells[r].length;
161 this.headerRowIdx=this.headerRowCnt-1;
162 colcnt=this.hdrCells[this.headerRowIdx].length;
164 Rico.writeDebugMsg("getColumnInfo: colcnt="+colcnt);
168 addHeadingRow: function() {
169 var r=this.headerRowCnt++;
171 for( var h=0; h < 2; h++ ) {
172 var row = this.thead[h].insertRow(-1);
173 row.className='ricoLG_hdg '+this.tableId+'_hdg'+r;
174 var limit= h==0 ? this.options.frozenColumns : this.headerColCnt-this.options.frozenColumns;
175 for( var c=0; c < limit; c++ ) {
176 var hdrCell=row.insertCell(-1);
177 var colDiv=RicoUtil.wrapChildren(hdrCell,'ricoLG_col');
178 RicoUtil.wrapChildren(colDiv,'ricoLG_cell');
179 this.hdrCells[r].push({cell:hdrCell,colSpan:1});
186 * create column array
188 createColumnArray: function(columnType) {
189 this.direction=Element.getStyle(this.outerDiv,'direction').toLowerCase(); // ltr or rtl
190 this.align=this.direction=='rtl' ? ['right','left'] : ['left','right'];
191 Rico.writeDebugMsg('createColumnArray: dir='+this.direction);
193 for (var c=0 ; c < this.headerColCnt; c++) {
194 Rico.writeDebugMsg("createColumnArray: c="+c);
195 var tabidx=c<this.options.frozenColumns ? 0 : 1;
196 this.columns.push(new Rico[columnType](this, c, this.hdrCells[this.headerRowIdx][c], tabidx));
202 * Create div structure
204 createDivs: function() {
205 Rico.writeDebugMsg("createDivs start");
206 this.outerDiv = this.createDiv("outer");
207 if (Prototype.Browser.Opera) this.outerDiv.style.overflow="hidden";
208 this.scrollDiv = this.createDiv("scroll",this.outerDiv);
209 this.frozenTabs = this.createDiv("frozenTabs",this.outerDiv);
210 this.innerDiv = this.createDiv("inner",this.outerDiv);
211 this.resizeDiv = this.createDiv("resize",this.outerDiv);
212 this.resizeDiv.style.display="none";
213 this.exportDiv = this.createDiv("export",this.outerDiv);
214 this.exportDiv.style.display="none";
215 this.messageDiv = this.createDiv("message",this.outerDiv);
216 this.messageDiv.style.display="none";
217 this.messageShadow=new Rico.Shadow(this.messageDiv);
219 this.keywordDiv = this.createDiv("keyword",this.outerDiv);
220 this.keywordDiv.style.display="none";
221 this.keywordTitle=this.keywordDiv.appendChild(document.createElement("p"));
222 Element.addClassName(this.keywordTitle,'keywordTitle');
223 var instructions=this.keywordDiv.appendChild(document.createElement("p"));
224 instructions.innerHTML=RicoTranslate.getPhraseById("keywordPrompt");
225 this.keywordBox=this.keywordDiv.appendChild(document.createElement("input"));
226 this.keywordBox.size=20;
227 Event.observe(this.keywordBox,"keypress", this.keywordKey.bindAsEventListener(this), false);
229 var img=document.createElement("img");
230 img.src=Rico.imgDir+'checkmark.gif';
231 Event.observe(img,"click", this.processKeyword.bindAsEventListener(this), false);
232 this.keywordDiv.appendChild(img);
234 img=document.createElement("img");
235 img.src=Rico.imgDir+'delete.gif';
236 Event.observe(img,"click", this.closeKeyword.bindAsEventListener(this), false);
237 this.keywordDiv.appendChild(img);
239 //this.frozenTabs.style[this.align[0]]='0px';
240 //this.innerDiv.style[this.align[0]]='0px';
241 Rico.writeDebugMsg("createDivs end");
244 keywordKey: function(e) {
245 switch (RicoUtil.eventKey(e)) {
246 case 27: this.closeKeyword(); Event.stop(e); return false;
247 case 13: this.processKeyword(); Event.stop(e); return false;
252 openKeyword: function(colnum) {
253 this.keywordCol=colnum;
254 this.keywordBox.value='';
255 this.keywordTitle.innerHTML=this.columns[colnum].displayName;
256 this.centerMsg(this.keywordDiv);
257 this.keywordBox.focus();
260 closeKeyword: function() {
261 Element.hide(this.keywordDiv);
265 processKeyword: function() {
266 var keyword=this.keywordBox.value;
268 this.columns[this.keywordCol].setFilterKW(keyword);
272 * Create a div and give it a standardized id and class name.
273 * If the div already exists, then just assign the class name.
275 createDiv: function(elemName,elemParent) {
276 var id=this.tableId+"_"+elemName+"Div";
279 var newdiv = document.createElement("div");
281 if (elemParent) elemParent.appendChild(newdiv);
283 newdiv.className = "ricoLG_"+elemName+"Div";
288 * Common code used to size & position divs in both SimpleGrid & LiveGrid
290 baseSizeDivs: function() {
291 this.setOtherHdrCellWidths();
293 if (this.options.frozenColumns) {
294 Element.show(this.tabs[0]);
295 Element.show(this.frozenTabs);
296 // order of next 3 lines is critical in IE6
297 this.hdrHt=Math.max(RicoUtil.nan2zero(this.thead[0].offsetHeight),this.thead[1].offsetHeight);
298 this.dataHt=Math.max(RicoUtil.nan2zero(this.tbody[0].offsetHeight),this.tbody[1].offsetHeight);
299 this.frzWi=this.borderWidth(this.tabs[0]);
301 Element.hide(this.tabs[0]);
302 Element.hide(this.frozenTabs);
304 this.hdrHt=this.thead[1].offsetHeight;
305 this.dataHt=this.tbody[1].offsetHeight;
309 var borderWi=this.borderWidth(this.columns[0].dataCell);
310 Rico.writeDebugMsg('baseSizeDivs '+this.tableId+': hdrHt='+this.hdrHt+' dataHt='+this.dataHt);
311 //window.status=this.tableId+' frzWi='+this.frzWi+' borderWi='+borderWi;
312 for (i=0; i<this.options.frozenColumns; i++) {
313 if (this.columns[i].visible) this.frzWi+=parseInt(this.columns[i].colWidth,10)+borderWi;
315 this.scrTabWi=this.borderWidth(this.tabs[1]);
316 for (i=this.options.frozenColumns; i<this.columns.length; i++) {
317 if (this.columns[i].visible) this.scrTabWi+=parseInt(this.columns[i].colWidth,10)+borderWi;
319 this.scrWi=this.scrTabWi+this.options.scrollBarWidth;
320 if (this.sizeTo=='parent') {
321 if (Prototype.Browser.IE) Element.hide(this.outerDiv);
322 wiLimit=this.outerDiv.parentNode.offsetWidth;
323 if (Prototype.Browser.IE) Element.show(this.outerDiv);
325 wiLimit=RicoUtil.windowWidth()-this.options.scrollBarWidth-8;
327 if (this.outerDiv.parentNode.clientWidth > 0)
328 wiLimit=Math.min(this.outerDiv.parentNode.clientWidth, wiLimit);
329 var overage=this.frzWi+this.scrWi-wiLimit;
330 Rico.writeDebugMsg('baseSizeDivs '+this.tableId+': scrWi='+this.scrWi+' wiLimit='+wiLimit+' overage='+overage+' clientWidth='+this.outerDiv.parentNode.clientWidth);
331 if (overage > 0 && this.options.frozenColumns < this.columns.length)
332 this.scrWi=Math.max(this.scrWi-overage, this.options.minScrollWidth);
333 this.scrollDiv.style.width=this.scrWi+'px';
334 this.scrollDiv.style.top=this.hdrHt+'px';
335 this.frozenTabs.style.width=this.scrollDiv.style[this.align[0]]=this.innerDiv.style[this.align[0]]=this.frzWi+'px';
336 this.outerDiv.style.width=(this.frzWi+this.scrWi)+'px';
340 * Returns the sum of the left & right border widths of an element
342 borderWidth: function(elem) {
343 return RicoUtil.nan2zero(Element.getStyle(elem,'border-left-width')) + RicoUtil.nan2zero(Element.getStyle(elem,'border-right-width'));
346 setOtherHdrCellWidths: function() {
347 var c,i,j,r,w,hdrcell,cell,origSpan,newSpan,divs;
348 for (r=0; r<this.hdrCells.length; r++) {
349 if (r==this.headerRowIdx) continue;
350 Rico.writeDebugMsg('setOtherHdrCellWidths: r='+r);
352 while (i<this.headerColCnt && c<this.hdrCells[r].length) {
353 hdrcell=this.hdrCells[r][c];
355 origSpan=newSpan=hdrcell.colSpan;
356 for (w=j=0; j<origSpan; j++, i++) {
357 if (this.columns[i].hdrCell.style.display=='none')
359 else if (this.columns[i].hdrColDiv.style.display!='none')
360 w+=parseInt(this.columns[i].colWidth,10);
362 if (!hdrcell.hdrColDiv || !hdrcell.hdrCellDiv) {
363 divs=cell.getElementsByTagName('div');
364 hdrcell.hdrColDiv=(divs.length<1) ? RicoUtil.wrapChildren(cell,'ricoLG_col') : divs[0];
365 hdrcell.hdrCellDiv=(divs.length<2) ? RicoUtil.wrapChildren(hdrcell.hdrColDiv,'ricoLG_cell') : divs[1];
368 cell.style.display='none';
370 hdrcell.hdrColDiv.style.display='none';
371 cell.colSpan=newSpan;
373 cell.style.display='';
374 hdrcell.hdrColDiv.style.display='';
375 cell.colSpan=newSpan;
376 hdrcell.hdrColDiv.style.width=w+'px';
383 initFilterImage: function(filterRowNum){
384 this.filterAnchor=$(this.tableId+'_filterLink');
385 if (!this.filterAnchor) return;
386 this.filterRows=$$('tr.'+this.tableId+'_hdg'+filterRowNum);
387 if (this.filterRows.length!=2) return;
388 for (var i=0, r=[]; i<2; i++) r[i]=Element.select(this.filterRows[i],'.ricoLG_cell');
389 this.filterElements=r[0].concat(r[1]);
390 this.saveHeight = this.filterElements[0].offsetHeight;
391 var pt=Element.getStyle(this.filterElements[0],'padding-top');
392 var pb=Element.getStyle(this.filterElements[0],'padding-bottom');
393 if (pt) this.saveHeight-=parseInt(pt,10);
394 if (pb) this.saveHeight-=parseInt(pb,10);
395 this.rowNum = filterRowNum;
396 this.setFilterImage(false);
397 Event.observe(this.filterAnchor, 'click', this.toggleFilterRow.bindAsEventListener(this), false);
400 toggleFilterRow: function() {
401 if ( this.filterRows[0].visible() )
402 this.slideFilterUp();
404 this.slideFilterDown();
407 slideFilterUp: function() {
408 for (var i=0; i<2; i++) this.filterRows[i].makeClipping();
409 Rico.animate( new Rico.Effect.Height(this.filterElements, 0), {onFinish: function(){ for (var i=0; i<2; i++) this.filterRows[i].hide(); this.resizeWindow();}.bind(this)});
410 this.setFilterImage(true);
413 slideFilterDown: function() {
414 for (var i=0; i<2; i++) this.filterRows[i].show();
415 Rico.animate(new Rico.Effect.Height( this.filterElements, this.saveHeight), {onFinish: function() { for (var i=0; i<2; i++) this.filterRows[i].undoClipping(); this.resizeWindow();}.bind(this)});
416 this.setFilterImage(false);
419 setFilterImage: function(expandFlag) {
420 var altText=RicoTranslate.getPhraseById((expandFlag ? 'show' : 'hide')+'FilterRow');
421 this.filterAnchor.innerHTML = '<img src="'+Rico.imgDir+'tableFilter'+(expandFlag ? 'Expand' : 'Collapse')+'.gif" alt="'+altText+'" border="0">';
425 * Returns a div for the cell at the specified row and column index.
426 * In SimpleGrid, r can refer to any row in the grid.
427 * In LiveGrid, r refers to a visible row (row 0 is the first visible row).
429 cell: function(r,c) {
430 return (0<=c && c<this.columns.length && r>=0) ? this.columns[c].cell(r) : null;
434 * Returns the screen height available for a grid
436 availHt: function() {
437 var divPos=Position.page(this.outerDiv);
438 return RicoUtil.windowHeight()-divPos[1]-2*this.options.scrollBarWidth-15; // allow for scrollbar and some margin
441 setHorizontalScroll: function() {
442 var newLeft=(-this.scrollDiv.scrollLeft)+'px';
443 this.hdrTabs[1].style.left=newLeft;
446 pluginScroll: function() {
447 if (this.scrollPluggedIn) return;
448 Event.observe(this.scrollDiv,"scroll",this.scrollEventFunc, false);
449 this.scrollPluggedIn=true;
452 unplugScroll: function() {
453 Event.stopObserving(this.scrollDiv,"scroll", this.scrollEventFunc , false);
454 this.scrollPluggedIn=false;
457 hideMsg: function() {
458 if (this.messageDiv.style.display=="none") return;
459 this.messageDiv.style.display="none";
460 this.messageShadow.hide();
463 showMsg: function(msg) {
464 this.messageDiv.innerHTML=msg;
465 this.centerMsg(this.messageDiv);
466 this.messageShadow.show();
467 Rico.writeDebugMsg("showMsg: "+msg);
470 centerMsg: function(div) {
472 var msgWidth=div.offsetWidth;
473 var msgHeight=div.offsetHeight;
474 var divwi=this.outerDiv.offsetWidth;
475 var divht=this.outerDiv.offsetHeight;
476 div.style.top=parseInt((divht-msgHeight)/2,10)+'px';
477 div.style.left=parseInt((divwi-msgWidth)/2,10)+'px';
481 * @return array of column objects which have invisible status
483 listInvisible: function() {
484 var hiddenColumns=[];
485 for (var x=0;x<this.columns.length;x++) {
486 if (this.columns[x].visible==false)
487 hiddenColumns.push(this.columns[x]);
489 return hiddenColumns;
493 * @return index of left-most visibile column, or -1 if there are no visible columns
495 firstVisible: function() {
496 for (var x=0;x<this.columns.length;x++) {
497 if (this.columns[x].visible) return x;
505 showAll: function() {
506 var invisible=this.listInvisible();
507 for (var x=0;x<invisible.length;x++)
508 invisible[x].showColumn();
511 chooseColumns: function(e) {
513 this.menu.cancelmenu();
514 var x,z,col,itemDiv,span,chooserDiv;
515 if (!this.columnChooser) {
516 z=Element.getStyle(this.outerDiv.offsetParent,'z-index');
517 if (typeof z!='number') z=0;
518 this.columnChooser=new Rico.Popup({canDragFunc:true, zIndex:z+2});
519 this.columnChooser.createWindow(RicoTranslate.getPhraseById('gridChooseCols'),'','150px','200px','ricoLG_chooser');
520 chooserDiv=this.columnChooser.contentDiv;
521 for (x=0;x<this.columns.length;x++) {
523 itemDiv=chooserDiv.appendChild(document.createElement('div'));
524 col.ChooserBox=RicoUtil.createFormField(itemDiv,'input','checkbox');
525 span=itemDiv.appendChild(document.createElement('span'));
526 span.innerHTML=col.displayName;
527 Event.observe(col.ChooserBox, 'click', col.chooseColumn.bindAsEventListener(col), false);
530 var divPos=Position.page(this.outerDiv);
531 var divTop=divPos[1]+this.hdrHt+RicoUtil.docScrollTop();
532 this.columnChooser.openPopup(divPos[0]+1,divTop);
533 for (x=0;x<this.columns.length;x++) {
534 this.columns[x].ChooserBox.checked=this.columns[x].visible;
535 this.columns[x].ChooserBox.disabled = !this.columns[x].canHideShow();
539 blankRow: function(r) {
540 for (var c=0; c < this.columns.length; c++) {
541 this.columns[c].clearCell(r);
546 * Copies all rows (SimpleGrid) or visible rows (LiveGrid) to a new window as a simple html table.
548 printVisible: function(exportType) {
549 this.showMsg(RicoTranslate.getPhraseById('exportInProgress'));
550 setTimeout(this._printVisible.bind(this,exportType),10); // allow message to paint
554 * Support function for printVisible()
556 exportStart: function() {
557 var r,c,i,j,hdrcell,newSpan,divs,cell;
559 this.exportText="<table border='1' cellspacing='0'>";
560 for (c=0; c<this.columns.length; c++) {
561 if (this.columns[c].visible) this.exportText+="<col width='"+parseInt(this.columns[c].colWidth,10)+"'>";
563 this.exportText+="<thead style='display: table-header-group;'>";
564 if (this.exportHeader) this.exportText+=this.exportHeader;
565 for (r=0; r<this.hdrCells.length; r++) {
566 if (this.hdrCells[r].length==0 || Element.getStyle(this.hdrCells[r][0].cell.parentNode,'display')=='none') continue;
567 this.exportText+="<tr>";
568 for (c=0,i=0; c<this.hdrCells[r].length; c++) {
569 hdrcell=this.hdrCells[r][c];
570 newSpan=hdrcell.colSpan;
571 for (j=0; j<hdrcell.colSpan; j++, i++) {
572 if (!this.columns[i].visible) newSpan--;
575 divs=Element.select(hdrcell.cell,'.ricoLG_cell');
576 cell=divs && divs.length>0 ? divs[0] : hdrcell.cell;
577 this.exportText+="<td style='"+this.exportStyle(cell)+"'";
578 if (hdrcell.colSpan > 1) this.exportText+=" colspan='"+newSpan+"'";
579 this.exportText+=">"+RicoUtil.getInnerText(cell,!this.options.exportImgTags, !this.options.exportFormFields, 'NoExport')+"</td>";
582 this.exportText+="</tr>";
584 this.exportText+="</thead><tbody>";
588 * Support function for printVisible().
589 * exportType is optional and defaults 'plain'; 'owc' can be used for IE users with Office Web Components.
591 exportFinish: function(exportType) {
592 if (this.hideMsg) this.hideMsg();
593 window.status=RicoTranslate.getPhraseById('exportComplete');
594 if (this.exportRows.length > 0) this.exportText+='<tr>'+this.exportRows.join('</tr><tr>')+'</tr>';
595 if (this.exportFooter) this.exportText+=this.exportFooter;
596 this.exportText+="</tbody></table>";
597 this.exportDiv.innerHTML=this.exportText;
598 this.exportText=undefined;
599 this.exportRows=undefined;
600 if (this.cancelMenu) this.cancelMenu();
601 var w=window.open(Rico.htmDir+'export-'+(exportType || 'plain')+'.html?'+this.exportDiv.id,'',this.options.exportWindow);
602 if (w == null) alert(RicoTranslate.getPhraseById('disableBlocker'));
606 * Support function for printVisible()
608 exportStyle: function(elem) {
609 var styleList=this.options.exportStyleList;
610 for (var i=0,s=''; i < styleList.length; i++) {
612 var curstyle=Element.getStyle(elem,styleList[i]);
613 if (curstyle) s+=styleList[i]+':'+curstyle+';';
620 * Gets the value of the grid cookie and interprets the contents.
621 * All information for a particular grid is stored in a single cookie.
622 * This may include column widths, column hide/show status, current sort, and any column filters.
624 getCookie: function() {
625 var c=RicoUtil.getCookie(this.options.cookiePrefix+this.tableId);
627 var cookieVals=c.split(',');
628 for (var i=0; i<cookieVals.length; i++) {
629 var v=cookieVals[i].split(':');
630 if (v.length!=2) continue;
631 var colnum=parseInt(v[0].slice(1),10);
632 if (colnum < 0 || colnum >= this.columns.length) continue;
633 var col=this.columns[colnum];
634 switch (v[0].charAt(0)) {
636 col.setColWidth(v[1]);
637 col.customWidth=true;
640 if (v[1].toLowerCase()=='true')
641 col.hideshow(true,true);
643 col.hideshow(false,true);
646 if (!this.options.saveColumnInfo.sort || !col.sortable) break;
650 if (!this.options.saveColumnInfo.filter || !col.filterable) break;
651 var filterTemp=v[1].split('~');
652 col.filterOp=filterTemp.shift();
653 col.filterValues = [];
654 col.filterType = Rico.TableColumn.USERFILTER;
655 for (var j=0; j<filterTemp.length; j++)
656 col.filterValues.push(unescape(filterTemp[j]));
663 * Sets the grid cookie.
664 * All information for a particular grid is stored in a single cookie.
665 * This may include column widths, column hide/show status, current sort, and any column filters.
667 setCookie: function() {
669 for (var i=0; i<this.columns.length; i++) {
670 var col=this.columns[i];
671 if (this.options.saveColumnInfo.width) {
672 if (col.customWidth) cookieVals.push('w'+i+':'+col.colWidth);
673 if (col.customVisible) cookieVals.push('h'+i+':'+col.visible);
675 if (this.options.saveColumnInfo.sort) {
676 if (col.currentSort != Rico.TableColumn.UNSORTED)
677 cookieVals.push('s'+i+':'+col.currentSort);
679 if (this.options.saveColumnInfo.filter && col.filterType == Rico.TableColumn.USERFILTER) {
680 var filterTemp=[col.filterOp];
681 for (var j=0; j<col.filterValues.length; j++)
682 filterTemp.push(escape(col.filterValues[j]));
683 cookieVals.push('f'+i+':'+filterTemp.join('~'));
686 RicoUtil.setCookie(this.options.cookiePrefix+this.tableId, cookieVals.join(','), this.options.cookieDays, this.options.cookiePath, this.options.cookieDomain);
691 Rico.TableColumn = Class.create();
694 Rico.TableColumn.UNFILTERED = 0;
696 Rico.TableColumn.SYSTEMFILTER = 1;
698 Rico.TableColumn.USERFILTER = 2;
701 Rico.TableColumn.UNSORTED = 0;
703 Rico.TableColumn.SORT_ASC = "ASC";
705 Rico.TableColumn.SORT_DESC = "DESC";
708 Rico.TableColumn.MINWIDTH = 10;
710 Rico.TableColumn.DOLLAR = {type:'number', prefix:'$', decPlaces:2, ClassName:'alignright'};
712 Rico.TableColumn.EURO = {type:'number', prefix:'€', decPlaces:2, ClassName:'alignright'};
714 Rico.TableColumn.PERCENT = {type:'number', suffix:'%', decPlaces:2, multiplier:100, ClassName:'alignright'};
716 Rico.TableColumn.QTY = {type:'number', decPlaces:0, ClassName:'alignright'};
718 Rico.TableColumn.DEFAULT = {type:"raw"};
722 * @class Define methods that are common to columns in both SimpleGrid and LiveGrid
724 Rico.TableColumnBase = function() {};
726 Rico.TableColumnBase.prototype = {
729 * Common code used to initialize the column in both SimpleGrid & LiveGrid
731 baseInit: function(liveGrid,colIdx,hdrInfo,tabIdx) {
732 Rico.writeDebugMsg("TableColumnBase.init index="+colIdx+" tabIdx="+tabIdx);
733 this.liveGrid = liveGrid;
735 this.hideWidth = Rico.isKonqueror || Prototype.Browser.WebKit || liveGrid.headerRowCnt>1 ? 5 : 2; // column width used for "hidden" columns. Anything less than 5 causes problems with Konqueror. Best to keep this greater than padding used inside cell.
736 this.options = liveGrid.options;
737 this.tabIdx = tabIdx;
738 this.hdrCell = hdrInfo.cell;
739 this.body = document.getElementsByTagName("body")[0]; // work around FireFox bug (document.body doesn't exist after XSLT)
740 this.displayName = this.getDisplayName(this.hdrCell);
741 var divs=this.hdrCell.getElementsByTagName('div');
742 this.hdrColDiv=(divs.length<1) ? RicoUtil.wrapChildren(this.hdrCell,'ricoLG_col') : divs[0];
743 this.hdrCellDiv=(divs.length<2) ? RicoUtil.wrapChildren(this.hdrColDiv,'ricoLG_cell') : divs[1];
744 var sectionIndex= tabIdx==0 ? colIdx : colIdx-liveGrid.options.frozenColumns;
745 this.dataCell = liveGrid.tbody[tabIdx].rows[0].cells[sectionIndex];
746 divs=this.dataCell.getElementsByTagName('div');
747 this.dataColDiv=(divs.length<1) ? RicoUtil.wrapChildren(this.dataCell,'ricoLG_col') : divs[0];
749 this.mouseDownHandler= this.handleMouseDown.bindAsEventListener(this);
750 this.mouseMoveHandler= this.handleMouseMove.bindAsEventListener(this);
751 this.mouseUpHandler = this.handleMouseUp.bindAsEventListener(this);
752 this.mouseOutHandler = this.handleMouseOut.bindAsEventListener(this);
754 this.fieldName = 'col'+this.index;
755 var spec = liveGrid.options.columnSpecs[colIdx];
756 this.format=Object.extend( {}, Rico.TableColumn.DEFAULT);
757 switch (typeof spec) {
759 if (typeof spec.format=='string') Object.extend(this.format, Rico.TableColumn[spec.format.toUpperCase()]);
760 Object.extend(this.format, spec);
763 if (spec.slice(0,4)=='spec') spec=spec.slice(4).toUpperCase(); // for backwards compatibility
764 this.format=typeof Rico.TableColumn[spec]=='object' ? Rico.TableColumn[spec] : Rico.TableColumn.DEFAULT;
767 Element.addClassName(this.dataColDiv, this.colClassName());
769 if (typeof this.format.visible=='boolean') this.visible=this.format.visible;
770 Rico.writeDebugMsg("TableColumn.init index="+colIdx+" fieldName="+this.fieldName);
771 this.sortable = typeof this.format.canSort=='boolean' ? this.format.canSort : liveGrid.options.canSortDefault;
772 this.currentSort = Rico.TableColumn.UNSORTED;
773 this.filterable = typeof this.format.canFilter=='boolean' ? this.format.canFilter : liveGrid.options.canFilterDefault;
774 this.filterType = Rico.TableColumn.UNFILTERED;
775 this.hideable = typeof this.format.canHide=='boolean' ? this.format.canHide : liveGrid.options.canHideDefault;
777 var wi=(typeof(this.format.width)=='number') ? this.format.width : hdrInfo.initWidth;
778 wi=(typeof(wi)=='number') ? Math.max(wi,Rico.TableColumn.MINWIDTH) : liveGrid.options.defaultWidth;
779 this.setColWidth(wi);
780 if (!this.visible) this.setDisplay('none');
781 if (this.options.allowColResize && !this.format.noResize) this.insertResizer();
784 colClassName: function() {
785 return this.format.ClassName ? this.format.ClassName : this.liveGrid.tableId+'_col'+this.index;
788 insertResizer: function() {
789 this.hdrCell.style.width='';
790 var resizer=this.hdrCellDiv.appendChild(document.createElement('div'));
791 resizer.className='ricoLG_Resize';
792 resizer.style[this.liveGrid.align[1]]='0px';
793 if (this.options.resizeBackground) {
794 var resizePath=Rico.imgDir+this.options.resizeBackground;
795 if (Prototype.Browser.IE && typeof(XDomainRequest)=='undefined') resizePath=location.protocol+resizePath;
796 resizer.style.backgroundImage='url('+resizePath+')';
798 Event.observe(resizer,"mousedown", this.mouseDownHandler, false);
802 * get the display name of a column
804 getDisplayName: function(el) {
805 var anchors=el.getElementsByTagName("A");
806 //Check the existance of A tags
807 if (anchors.length > 0)
808 return anchors[0].innerHTML;
810 return el.innerHTML.stripTags();
813 _clear: function(gridCell) {
814 gridCell.innerHTML=' ';
817 clearCell: function(rowIndex) {
818 var gridCell=this.cell(rowIndex);
819 this._clear(gridCell,rowIndex);
820 if (!this.liveGrid.buffer) return;
821 var acceptAttr=this.liveGrid.buffer.options.acceptAttr;
822 for (var k=0; k<acceptAttr.length; k++) {
823 switch (acceptAttr[k]) {
824 case 'style': gridCell.style.cssText=''; break;
825 case 'class': gridCell.className=''; break;
826 default: gridCell['_'+acceptAttr[k]]=''; break;
831 dataTable: function() {
832 return this.liveGrid.tabs[this.tabIdx];
835 numRows: function() {
836 return this.dataColDiv.childNodes.length;
839 clearColumn: function() {
840 var childCnt=this.numRows();
841 for (var r=0; r<childCnt; r++)
846 return this.dataColDiv.childNodes[r];
849 getFormattedValue: function(r,xImg,xForm,xClass) {
850 return RicoUtil.getInnerText(this.cell(r),xImg,xForm,xClass);
853 setColWidth: function(wi) {
854 if (typeof wi=='number') {
856 if (wi < Rico.TableColumn.MINWIDTH) return;
859 Rico.writeDebugMsg('setColWidth '+this.index+': '+wi);
861 this.hdrColDiv.style.width=wi;
862 this.dataColDiv.style.width=wi;
865 pluginMouseEvents: function() {
866 if (this.mousePluggedIn==true) return;
867 Event.observe(this.body,"mousemove", this.mouseMoveHandler, false);
868 Event.observe(this.body,"mouseup", this.mouseUpHandler , false);
869 Event.observe(this.body,"mouseout", this.mouseOutHandler , false);
870 this.mousePluggedIn=true;
873 unplugMouseEvents: function() {
874 Event.stopObserving(this.body,"mousemove", this.mouseMoveHandler, false);
875 Event.stopObserving(this.body,"mouseup", this.mouseUpHandler , false);
876 Event.stopObserving(this.body,"mouseout", this.mouseOutHandler , false);
877 this.mousePluggedIn=false;
880 handleMouseDown: function(e) {
881 this.resizeStart=e.clientX;
882 this.origWidth=parseInt(this.colWidth,10);
883 var p=Position.positionedOffset(this.hdrCell);
884 if (this.liveGrid.direction=='rtl') {
885 this.edge=p[0]+this.liveGrid.options.scrollBarWidth;
886 switch (this.tabIdx) {
887 case 0: this.edge+=this.liveGrid.innerDiv.offsetWidth; break;
888 case 1: this.edge-=this.liveGrid.scrollDiv.scrollLeft; break;
891 this.edge=p[0]+this.hdrCell.offsetWidth;
892 if (this.tabIdx>0) this.edge+=RicoUtil.nan2zero(this.liveGrid.tabs[0].offsetWidth)-this.liveGrid.scrollDiv.scrollLeft;
894 this.liveGrid.resizeDiv.style.left=this.edge+"px";
895 this.liveGrid.resizeDiv.style.display="";
896 this.liveGrid.outerDiv.style.cursor='e-resize';
897 this.tmpHighlight=this.liveGrid.highlightEnabled;
898 this.liveGrid.highlightEnabled=false;
899 this.pluginMouseEvents();
903 handleMouseMove: function(e) {
904 var delta=e.clientX-this.resizeStart;
905 var newWidth=(this.liveGrid.direction=='rtl') ? this.origWidth-delta : this.origWidth+delta;
906 if (newWidth < Rico.TableColumn.MINWIDTH) return;
907 this.liveGrid.resizeDiv.style.left=(this.edge+delta)+"px";
908 this.colWidth=newWidth;
912 handleMouseUp: function(e) {
913 this.unplugMouseEvents();
914 Rico.writeDebugMsg('handleMouseUp '+this.liveGrid.tableId);
915 this.liveGrid.outerDiv.style.cursor='';
916 this.liveGrid.resizeDiv.style.display="none";
917 this.setColWidth(this.colWidth);
918 this.customWidth=true;
919 this.liveGrid.setCookie();
920 this.liveGrid.highlightEnabled=this.tmpHighlight;
921 this.liveGrid.sizeDivs();
925 handleMouseOut: function(e) {
926 var reltg = (e.relatedTarget) ? e.relatedTarget : e.toElement;
927 while (reltg != null && reltg.nodeName.toLowerCase() != 'body')
928 reltg=reltg.parentNode;
929 if (reltg!=null && reltg.nodeName.toLowerCase() == 'body') return true;
930 this.handleMouseUp(e);
934 setDisplay: function(d) {
935 this.hdrCell.style.display=d;
936 this.hdrColDiv.style.display=d;
937 this.dataCell.style.display=d;
938 this.dataColDiv.style.display=d;
941 hideshow: function(visible,noresize) {
942 this.setDisplay(visible ? '' : 'none');
943 this.liveGrid.cancelMenu();
944 this.visible=visible;
945 this.customVisible=true;
946 if (noresize) return;
947 this.liveGrid.setCookie();
948 this.liveGrid.sizeDivs();
951 hideColumn: function() {
952 Rico.writeDebugMsg('hideColumn '+this.liveGrid.tableId);
953 this.hideshow(false,false);
956 showColumn: function() {
957 Rico.writeDebugMsg('showColumn '+this.liveGrid.tableId);
958 this.hideshow(true,false);
961 chooseColumn: function(e) {
962 var elem=Event.element(e);
963 this.hideshow(elem.checked,false);
966 setImage: function() {
967 if ( this.currentSort == Rico.TableColumn.SORT_ASC ) {
968 this.imgSort.style.display='';
969 this.imgSort.src=Rico.imgDir+this.options.sortAscendImg;
970 } else if ( this.currentSort == Rico.TableColumn.SORT_DESC ) {
971 this.imgSort.style.display='';
972 this.imgSort.src=Rico.imgDir+this.options.sortDescendImg;
974 this.imgSort.style.display='none';
976 if (this.filterType == Rico.TableColumn.USERFILTER) {
977 this.imgFilter.style.display='';
978 this.imgFilter.title=this.getFilterText();
980 this.imgFilter.style.display='none';
984 canHideShow: function() {
985 return this.hideable;
990 Rico.includeLoaded('ricoGridCommon.js');