2 * (c) 2005-2009 Richard Cowin (http://openrico.org)
3 * (c) 2005-2009 Matt Brown (http://dowdybrown.com)
5 * 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");
19 * Define methods that are common to both SimpleGrid and LiveGrid
23 baseInit: function() {
25 resizeBackground : 'resize.gif',
26 saveColumnInfo : {width:true, filter:false, sort:false}, // save info in cookies?
27 cookiePrefix : 'RicoGrid.',
28 allowColResize : true, // allow user to resize columns
29 windowResize : true, // Resize grid on window.resize event? Set to false when embedded in an accordian.
33 menuEvent : 'dblclick', // event that triggers menus - click, dblclick, contextmenu, or none (no menus)
34 defaultWidth : -1, // if -1, then use unformatted column width
35 scrollBarWidth : 19, // this is the value used in positioning calculations, it does not actually change the width of the scrollbar
36 minScrollWidth : 100, // min scroll area width when width of frozen columns exceeds window width
38 exportWindow : "height=400,width=500,scrollbars=1,menubar=1,resizable=1",
39 exportStyleList : ['background-color','color','text-align','font-weight','font-size','font-family'],
40 exportImgTags : false, // applies to grid header and to SimpleGrid cells (not LiveGrid cells)
41 exportFormFields : true,
42 FilterLocation : null, // heading row number to place filters. -1=add a new heading row.
43 FilterAllToken : '___ALL___', // select box value to use to indicate ALL
49 this.headerRowIdx=0; // row in header which gets resizers (no colspan's in this row)
50 this.tabs=new Array(2);
51 this.thead=new Array(2);
52 this.tbody=new Array(2);
55 attachMenuEvents: function() {
57 if (!this.options.menuEvent || this.options.menuEvent=='none') return;
58 this.hideScroll=navigator.userAgent.match(/Macintosh\b.*\b(Firefox|Camino)\b/i) || (Rico.isOpera && parseFloat(window.opera.version())<9.5);
59 this.options[this.options.menuEvent]=Rico.eventHandle(this,'handleMenuClick');
60 if (this.highlightDiv) {
61 switch (this.options.highlightElem) {
63 this.attachMenu(this.highlightDiv[0]);
67 this.attachMenu(this.highlightDiv[i]);
73 this.attachMenu(this.tbody[i]);
77 attachMenu: function(elem) {
78 if (this.options.click)
79 Rico.eventBind(elem, 'click', this.options.click, false);
80 if (this.options.dblclick) {
81 if (Rico.isWebKit || Rico.isOpera)
82 Rico.eventBind(elem, 'click', Rico.eventHandle(this,'handleDblClick'), false);
84 Rico.eventBind(elem, 'dblclick', this.options.dblclick, false);
86 if (this.options.contextmenu) {
87 if (Rico.isOpera || Rico.isKonqueror)
88 Rico.eventBind(elem, 'click', Rico.eventHandle(this,'handleContextMenu'), false);
90 Rico.eventBind(elem, 'contextmenu', this.options.contextmenu, false);
95 * implement double-click for browsers that don't support a double-click event (e.g. Safari)
97 handleDblClick: function(e) {
98 var elem=Rico.eventElement(e);
99 if (this.dblClickElem == elem) {
100 this.options.dblclick(e);
102 this.dblClickElem = elem;
103 this.safariTimer=Rico.runLater(300,this,'clearDblClick');
107 clearDblClick: function() {
108 this.dblClickElem=null;
112 * implement right-click for browsers that don't support contextmenu event (e.g. Opera, Konqueror)
113 * use control-click instead
115 handleContextMenu: function(e) {
117 if( typeof( e.which ) == 'number' )
118 b = e.which; //Netscape compatible
119 else if( typeof( e.button ) == 'number' )
123 if (b==1 && e.ctrlKey) {
124 this.options.contextmenu(e);
128 cancelMenu: function() {
129 if (this.menu) this.menu.cancelmenu();
133 * gather info from original headings
135 getColumnInfo: function(hdrSrc) {
136 Rico.log('getColumnInfo: len='+hdrSrc.length);
137 if (hdrSrc.length == 0) return 0;
138 this.headerRowCnt=hdrSrc.length;
140 for (r=0; r<this.headerRowCnt; r++) {
141 var headerRow = hdrSrc[r];
142 var headerCells=headerRow.cells;
143 if (r >= this.hdrCells.length) this.hdrCells[r]=[];
144 for (c=0; c<headerCells.length; c++) {
146 obj.cell=headerCells[c];
147 obj.colSpan=headerCells[c].colSpan || 1; // Safari & Konqueror return default colspan of 0
148 if (this.options.defaultWidth < 0) obj.initWidth=headerCells[c].offsetWidth;
149 this.hdrCells[r].push(obj);
151 if (headerRow.id.slice(-5)=='_main') {
152 colcnt=this.hdrCells[r].length;
157 this.headerRowIdx=this.headerRowCnt-1;
158 colcnt=this.hdrCells[this.headerRowIdx].length;
160 Rico.log("getColumnInfo: colcnt="+colcnt);
164 addHeadingRow: function(className) {
165 var r=this.headerRowCnt++;
167 for( var h=0; h < 2; h++ ) {
168 var row = this.thead[h].insertRow(-1);
169 var newClass='ricoLG_hdg '+this.tableId+'_hdg'+r;
170 if (className) newClass+=' '+className;
171 row.className=newClass;
172 var limit= h==0 ? this.options.frozenColumns : this.headerColCnt-this.options.frozenColumns;
173 for( var c=0; c < limit; c++ ) {
174 var hdrCell=row.insertCell(-1);
175 var colDiv=Rico.wrapChildren(hdrCell,'ricoLG_col');
176 Rico.wrapChildren(colDiv,'ricoLG_cell');
177 this.hdrCells[r].push({cell:hdrCell,colSpan:1});
184 * create column array
186 createColumnArray: function(columnType) {
187 this.direction=Rico.getStyle(this.outerDiv,'direction').toLowerCase(); // ltr or rtl
188 this.align=this.direction=='rtl' ? ['right','left'] : ['left','right'];
189 Rico.log('createColumnArray: dir='+this.direction);
191 for (var c=0; c < this.headerColCnt; c++) {
192 Rico.log("createColumnArray: c="+c);
193 var tabidx=c<this.options.frozenColumns ? 0 : 1;
194 var col=new Rico[columnType](this, c, this.hdrCells[this.headerRowIdx][c], tabidx);
195 this.columns.push(col);
196 if (c > 0) this.columns[c-1].next=col;
199 Rico.runLater(100,this,'insertResizers'); // avoids peek-a-boo bug in column 1 in IE6/7
203 * Insert resizing handles
205 insertResizers: function() {
206 if (!this.options.allowColResize) return;
207 for (var x=0;x<this.columns.length;x++) {
208 this.columns[x].insertResizer();
213 * Create div structure
215 createDivs: function() {
216 Rico.log("createDivs start");
217 this.outerDiv = this.createDiv("outer");
218 if (Rico.theme.widget) Rico.addClass(this.outerDiv,Rico.theme.widget);
219 if (this.outerDiv.firstChild && this.outerDiv.firstChild.tagName && this.outerDiv.firstChild.tagName.toUpperCase()=='TABLE') {
220 this.structTab=this.outerDiv.firstChild;
221 this.structTabLeft=this.structTab.rows[0].cells[0];
222 this.structTabUR=this.structTab.rows[0].cells[1];
223 this.structTabLR=this.structTab.rows[1].cells[0];
225 this.structTab = document.createElement("table");
226 this.structTab.border=0;
227 this.structTab.cellPadding=0;
228 this.structTab.cellSpacing=0;
229 var tr1=this.structTab.insertRow(-1);
231 this.structTabLeft=tr1.insertCell(-1);
232 this.structTabLeft.rowSpan=2;
233 var tr2=this.structTab.insertRow(-1);
235 this.structTabUR=tr1.insertCell(-1);
236 this.structTabLR=tr2.insertCell(-1);
237 this.outerDiv.appendChild(this.structTab);
239 //this.structTabLR.style.overflow='hidden';
240 //if (Rico.isOpera) this.outerDiv.style.overflow="hidden";
241 this.frozenTabs = this.createDiv("frozenTabs",this.structTabLeft);
242 this.innerDiv = this.createDiv("inner",this.structTabUR);
243 this.scrollDiv = this.createDiv("scroll",this.structTabLR);
244 this.resizeDiv = this.createDiv("resize",this.outerDiv,true);
245 this.exportDiv = this.createDiv("export",this.outerDiv,true);
247 this.messagePopup=new Rico.Popup();
248 this.messagePopup.createContainer({hideOnEscape:false, hideOnClick:false, parent:this.outerDiv});
249 this.messagePopup.content.className='ricoLG_messageDiv';
250 if (Rico.theme.gridMessage) Rico.addClass(this.messagePopup.content,Rico.theme.gridMessage);
252 this.keywordPopup=new Rico.Window('', {zIndex:-1, parent:this.outerDiv});
253 this.keywordPopup.container.className='ricoLG_keywordDiv';
254 var instructions=this.keywordPopup.contentDiv.appendChild(document.createElement("p"));
255 instructions.innerHTML=Rico.getPhraseById("keywordPrompt");
256 this.keywordBox=this.keywordPopup.contentDiv.appendChild(document.createElement("input"));
257 this.keywordBox.size=20;
258 Rico.eventBind(this.keywordBox,"keypress", Rico.eventHandle(this,'keywordKey'), false);
259 this.keywordPopup.contentDiv.appendChild(Rico.floatButton('Checkmark', Rico.eventHandle(this,'processKeyword')));
260 var s=this.keywordPopup.contentDiv.appendChild(document.createElement("p"));
261 Rico.setStyle(s,{clear:'both'});
263 //this.frozenTabs.style[this.align[0]]='0px';
264 //this.innerDiv.style[this.align[0]]='0px';
265 Rico.log("createDivs end");
268 keywordKey: function(e) {
269 switch (Rico.eventKey(e)) {
270 case 27: this.closeKeyword(); Rico.eventStop(e); return false;
271 case 13: this.processKeyword(); Rico.eventStop(e); return false;
276 openKeyword: function(colnum) {
277 this.keywordCol=colnum;
278 this.keywordBox.value='';
279 this.keywordPopup.setTitle(this.columns[colnum].displayName);
280 this.keywordPopup.centerPopup();
281 this.keywordBox.focus();
284 closeKeyword: function() {
285 this.keywordPopup.closePopup();
289 processKeyword: function() {
290 var keyword=this.keywordBox.value;
292 this.columns[this.keywordCol].setFilterKW(keyword);
296 * Create a div and give it a standardized id and class name.
297 * If the div already exists, then just assign the class name.
299 createDiv: function(elemName,elemParent,hidden) {
300 var id=this.tableId+"_"+elemName+"Div";
301 var newdiv=document.getElementById(id);
303 newdiv = document.createElement("div");
305 if (elemParent) elemParent.appendChild(newdiv);
307 newdiv.className = "ricoLG_"+elemName+"Div";
308 if (hidden) Rico.hide(newdiv);
313 * Common code used to size & position divs in both SimpleGrid & LiveGrid
315 baseSizeDivs: function() {
316 this.setOtherHdrCellWidths();
318 if (this.options.frozenColumns) {
319 Rico.show(this.tabs[0]);
320 Rico.show(this.frozenTabs);
321 // order of next 3 lines is critical in IE6
322 this.hdrHt=Math.max(Rico.nan2zero(this.thead[0].offsetHeight),this.thead[1].offsetHeight);
323 this.dataHt=Math.max(Rico.nan2zero(this.tbody[0].offsetHeight),this.tbody[1].offsetHeight);
324 this.frzWi=this.borderWidth(this.tabs[0]);
326 Rico.hide(this.tabs[0]);
327 Rico.hide(this.frozenTabs);
329 this.hdrHt=this.thead[1].offsetHeight;
330 this.dataHt=this.tbody[1].offsetHeight;
334 var borderWi=this.borderWidth(this.columns[0].dataCell);
335 Rico.log('baseSizeDivs '+this.tableId+': hdrHt='+this.hdrHt+' dataHt='+this.dataHt);
336 Rico.log(this.tableId+' frzWi='+this.frzWi+' borderWi='+borderWi);
337 for (i=0; i<this.options.frozenColumns; i++) {
338 if (this.columns[i].visible) this.frzWi+=parseInt(this.columns[i].colWidth,10)+borderWi;
340 this.scrTabWi=this.borderWidth(this.tabs[1]);
341 this.scrTabWi0=this.scrTabWi;
342 Rico.log('scrTabWi: '+this.scrTabWi);
343 for (i=this.options.frozenColumns; i<this.columns.length; i++) {
344 if (this.columns[i].visible) this.scrTabWi+=parseInt(this.columns[i].colWidth,10)+borderWi;
346 this.scrWi=this.scrTabWi+this.options.scrollBarWidth;
347 if (this.sizeTo=='parent') {
348 if (Rico.isIE) Rico.hide(this.outerDiv);
349 wiLimit=this.outerDiv.parentNode.offsetWidth;
350 if (Rico.isIE) Rico.show(this.outerDiv);
352 wiLimit=Rico.windowWidth()-this.options.scrollBarWidth-8;
354 if (this.outerDiv.parentNode.clientWidth > 0)
355 wiLimit=Math.min(this.outerDiv.parentNode.clientWidth, wiLimit);
356 var overage=this.frzWi+this.scrWi-wiLimit;
357 Rico.log('baseSizeDivs '+this.tableId+': scrWi='+this.scrWi+' wiLimit='+wiLimit+' overage='+overage+' clientWidth='+this.outerDiv.parentNode.clientWidth);
358 if (overage > 0 && this.options.frozenColumns < this.columns.length)
359 this.scrWi=Math.max(this.scrWi-overage, this.options.minScrollWidth);
360 this.scrollDiv.style.width=this.scrWi+'px';
361 //this.scrollDiv.style.top=this.hdrHt+'px';
362 //this.frozenTabs.style.width=this.scrollDiv.style[this.align[0]]=this.innerDiv.style[this.align[0]]=this.frzWi+'px';
363 this.frozenTabs.style.width=this.frzWi+'px';
364 this.outerDiv.style.width=(this.frzWi+this.scrWi)+'px';
368 * Returns the sum of the left & right border widths of an element
370 borderWidth: function(elem) {
371 var l=Rico.nan2zero(Rico.getStyle(elem,'borderLeftWidth'));
372 var r=Rico.nan2zero(Rico.getStyle(elem,'borderRightWidth'));
373 Rico.log((elem.id || elem.tagName)+' borderWidth: L='+l+', R='+r);
375 // return Rico.nan2zero(Rico.getStyle(elem,'borderLeftWidth')) + Rico.nan2zero(Rico.getStyle(elem,'borderRightWidth'));
378 setOtherHdrCellWidths: function() {
379 var c,i,j,r,w,hdrcell,cell,origSpan,newSpan,divs;
380 for (r=0; r<this.hdrCells.length; r++) {
381 if (r==this.headerRowIdx) continue;
382 Rico.log('setOtherHdrCellWidths: r='+r);
384 while (i<this.headerColCnt && c<this.hdrCells[r].length) {
385 hdrcell=this.hdrCells[r][c];
387 origSpan=newSpan=hdrcell.colSpan;
388 for (w=j=0; j<origSpan; j++, i++) {
389 if (this.columns[i].hdrCell.style.display=='none')
391 else if (this.columns[i].hdrColDiv.style.display!='none')
392 w+=parseInt(this.columns[i].colWidth,10);
394 if (!hdrcell.hdrColDiv || !hdrcell.hdrCellDiv) {
395 divs=cell.getElementsByTagName('div');
396 hdrcell.hdrColDiv=(divs.length<1) ? Rico.wrapChildren(cell,'ricoLG_col') : divs[0];
397 hdrcell.hdrCellDiv=(divs.length<2) ? Rico.wrapChildren(hdrcell.hdrColDiv,'ricoLG_cell') : divs[1];
400 cell.style.display='none';
402 hdrcell.hdrColDiv.style.display='none';
403 cell.colSpan=newSpan;
405 cell.style.display='';
406 hdrcell.hdrColDiv.style.display='';
407 cell.colSpan=newSpan;
408 hdrcell.hdrColDiv.style.width=w+'px';
415 initFilterImage: function(filterRowNum){
416 this.filterAnchor=document.getElementById(this.tableId+'_filterLink');
417 if (!this.filterAnchor) return;
418 this.filterRows=Rico.select('tr.'+this.tableId+'_hdg'+filterRowNum);
419 if (this.filterRows.length!=2) return;
420 for (var i=0, r=[]; i<2; i++) r[i]=Rico.select('.ricoLG_cell',this.filterRows[i]);
421 this.filterElements=r[0].concat(r[1]);
422 this.saveHeight = this.filterElements[0].offsetHeight;
423 var pt=Rico.getStyle(this.filterElements[0],'paddingTop');
424 var pb=Rico.getStyle(this.filterElements[0],'paddingBottom');
425 if (pt) this.saveHeight-=parseInt(pt,10);
426 if (pb) this.saveHeight-=parseInt(pb,10);
427 this.rowNum = filterRowNum;
428 this.setFilterImage(false);
429 //Rico.eventBind(this.filterAnchor, 'click', Rico.eventHandle(this,'toggleFilterRow'), false);
432 toggleFilterRow: function() {
433 if ( Rico.visible(this.filterRows[0]) )
434 this.slideFilterUp();
436 this.slideFilterDown();
439 setFilterImage: function(expandFlag) {
440 var altText=Rico.getPhraseById((expandFlag ? 'show' : 'hide')+'FilterRow');
441 this.filterAnchor.innerHTML = '<img src="'+Rico.imgDir+'tableFilter'+(expandFlag ? 'Expand' : 'Collapse')+'.gif" alt="'+altText+'" border="0">';
445 * Returns a div for the cell at the specified row and column index.
446 * In SimpleGrid, r can refer to any row in the grid.
447 * In LiveGrid, r refers to a visible row (row 0 is the first visible row).
449 cell: function(r,c) {
450 return (0<=c && c<this.columns.length && r>=0) ? this.columns[c].cell(r) : null;
454 * Returns the screen height available for a grid
456 availHt: function() {
457 var divPos=Rico.cumulativeOffset(this.outerDiv);
458 return Rico.windowHeight()-divPos.top-2*this.options.scrollBarWidth-15; // allow for scrollbar and some margin
461 setHorizontalScroll: function() {
462 var newLeft=(-this.scrollDiv.scrollLeft)+'px';
463 this.hdrTabs[1].style.marginLeft=newLeft;
466 pluginScroll: function() {
467 if (this.scrollPluggedIn) return;
468 Rico.eventBind(this.scrollDiv,"scroll",this.scrollEventFunc, false);
469 this.scrollPluggedIn=true;
472 unplugScroll: function() {
473 Rico.eventUnbind(this.scrollDiv,"scroll", this.scrollEventFunc , false);
474 this.scrollPluggedIn=false;
477 hideMsg: function() {
478 this.messagePopup.closePopup();
481 showMsg: function(msg) {
482 this.messagePopup.setContent(msg);
483 this.messagePopup.centerPopup();
484 Rico.log("showMsg: "+msg);
488 * @return array of column objects which have invisible status
490 listInvisible: function(attr) {
491 var hiddenColumns=[];
492 for (var x=0;x<this.columns.length;x++) {
493 if (!this.columns[x].visible)
494 hiddenColumns.push(attr ? this.columns[x][attr] : this.columns[x]);
496 return hiddenColumns;
500 * @return index of left-most visibile column, or -1 if there are no visible columns
502 firstVisible: function() {
503 for (var x=0;x<this.columns.length;x++) {
504 if (this.columns[x].visible) return x;
512 showAll: function() {
513 var invisible=this.listInvisible();
514 for (var x=0;x<invisible.length;x++)
515 invisible[x].showColumn();
518 chooseColumns: function() {
519 this.menu.cancelmenu();
520 var x,z,col,itemDiv,span,contentDiv;
\r
521 if (!this.columnChooser) {
522 Rico.log('creating columnChooser');
523 z=Rico.getStyle(this.outerDiv.offsetParent,'zIndex');
524 if (typeof z!='number') z=0;
525 this.columnChooser=new Rico.Window(Rico.getPhraseById('gridChooseCols'), {zIndex:z+2, parent:this.outerDiv});
526 this.columnChooser.container.className='ricoLG_chooserDiv';
527 contentDiv=this.columnChooser.contentDiv;
528 for (x=0;x<this.columns.length;x++) {
530 itemDiv=contentDiv.appendChild(document.createElement('div'));
531 col.ChooserBox=Rico.createFormField(itemDiv,'input','checkbox');
532 span=itemDiv.appendChild(document.createElement('span'));
533 span.innerHTML=col.displayName;
534 Rico.eventBind(col.ChooserBox, 'click', Rico.eventHandle(col,'chooseColumn'), false);
537 Rico.log('opening columnChooser');
538 this.columnChooser.openPopup(1,this.hdrHt);
539 for (x=0;x<this.columns.length;x++) {
540 this.columns[x].ChooserBox.checked=this.columns[x].visible;
541 this.columns[x].ChooserBox.disabled = !this.columns[x].canHideShow();
545 blankRow: function(r) {
546 for (var c=0; c < this.columns.length; c++) {
547 this.columns[c].clearCell(r);
551 getExportStyles: function(chkelem) {
552 var exportStyles=this.options.exportStyleList;
553 var bgImg=Rico.getStyle(chkelem,'backgroundImage');
554 if (!bgImg || bgImg=='none') return exportStyles;
555 for (var styles=[],i=0; i<exportStyles.length; i++)
556 if (exportStyles[i]!='background-color' && exportStyles[i]!='color') styles.push(exportStyles[i]);
561 * Support function for printVisible()
563 exportStart: function() {
564 var r,c,i,j,hdrcell,newSpan,divs,cell;
565 var exportStyles=this.getExportStyles(this.thead[0]);
566 //alert(exportStyles.join('\n'));
568 this.exportText="<table border='1' cellspacing='0'>";
\r
569 for (c=0; c<this.columns.length; c++) {
\r
570 if (this.columns[c].visible) this.exportText+="<col width='"+parseInt(this.columns[c].colWidth,10)+"'>";
572 this.exportText+="<thead style='display: table-header-group;'>";
573 if (this.exportHeader) this.exportText+=this.exportHeader;
574 for (r=0; r<this.hdrCells.length; r++) {
575 if (this.hdrCells[r].length==0 || !Rico.visible(this.hdrCells[r][0].cell.parentNode)) continue;
576 this.exportText+="<tr>";
577 for (c=0,i=0; c<this.hdrCells[r].length; c++) {
578 hdrcell=this.hdrCells[r][c];
579 newSpan=hdrcell.colSpan;
580 for (j=0; j<hdrcell.colSpan; j++, i++) {
581 if (!this.columns[i].visible) newSpan--;
584 divs=Rico.select('.ricoLG_cell',hdrcell.cell);
585 cell=divs && divs.length>0 ? divs[0] : hdrcell.cell;
586 this.exportText+="<td style='"+this.exportStyle(cell,exportStyles)+"'";
587 if (hdrcell.colSpan > 1) this.exportText+=" colspan='"+newSpan+"'";
588 this.exportText+=">"+Rico.getInnerText(cell,!this.options.exportImgTags, !this.options.exportFormFields, 'NoExport')+"</td>";
591 this.exportText+="</tr>";
593 this.exportText+="</thead><tbody>";
597 * Support function for printVisible().
598 * exportType is optional and defaults 'plain'; 'owc' can be used for IE users with Office Web Components.
600 exportFinish: function(exportType) {
601 if (this.hideMsg) this.hideMsg();
602 window.status=Rico.getPhraseById('exportComplete');
603 if (this.exportRows.length > 0) this.exportText+='<tr>'+this.exportRows.join('</tr><tr>')+'</tr>';
604 if (this.exportFooter) this.exportText+=this.exportFooter;
605 this.exportText+="</tbody></table>";
606 this.exportDiv.innerHTML=this.exportText;
607 this.exportText=undefined;
608 this.exportRows=undefined;
609 if (this.cancelMenu) this.cancelMenu();
610 var w=window.open(Rico.htmDir+'export-'+(exportType || 'plain')+'.html?'+this.exportDiv.id,'',this.options.exportWindow);
611 if (w == null) alert(Rico.getPhraseById('disableBlocker'));
615 * Support function for printVisible()
617 exportStyle: function(elem,styleList) {
618 for (var i=0,s=''; i < styleList.length; i++) {
620 var curstyle=Rico.getStyle(elem,styleList[i]);
621 if (curstyle) s+=styleList[i]+':'+curstyle+';';
628 * Gets the value of the grid cookie and interprets the contents.
629 * All information for a particular grid is stored in a single cookie.
630 * This may include column widths, column hide/show status, current sort, and any column filters.
632 getCookie: function() {
633 var c=Rico.getCookie(this.options.cookiePrefix+this.tableId);
635 var cookieVals=c.split(',');
636 for (var i=0; i<cookieVals.length; i++) {
637 var v=cookieVals[i].split(':');
638 if (v.length!=2) continue;
639 var colnum=parseInt(v[0].slice(1),10);
640 if (colnum < 0 || colnum >= this.columns.length) continue;
641 var col=this.columns[colnum];
642 switch (v[0].charAt(0)) {
644 col.setColWidth(v[1]);
645 col.customWidth=true;
648 if (v[1].toLowerCase()=='true')
649 col.hideshow(true,true);
651 col.hideshow(false,true);
654 if (!this.options.saveColumnInfo.sort || !col.sortable) break;
658 if (!this.options.saveColumnInfo.filter || !col.filterable) break;
659 var filterTemp=v[1].split('~');
660 col.filterOp=filterTemp.shift();
661 col.filterValues = [];
662 col.filterType = Rico.ColumnConst.USERFILTER;
663 for (var j=0; j<filterTemp.length; j++)
664 col.filterValues.push(unescape(filterTemp[j]));
671 * Sets the grid cookie.
672 * All information for a particular grid is stored in a single cookie.
673 * This may include column widths, column hide/show status, current sort, and any column filters.
675 setCookie: function() {
677 for (var i=0; i<this.columns.length; i++) {
678 var col=this.columns[i];
679 if (this.options.saveColumnInfo.width) {
680 if (col.customWidth) cookieVals.push('w'+i+':'+col.colWidth);
681 if (col.customVisible) cookieVals.push('h'+i+':'+col.visible);
683 if (this.options.saveColumnInfo.sort) {
684 if (col.currentSort != Rico.ColumnConst.UNSORTED)
685 cookieVals.push('s'+i+':'+col.currentSort);
687 if (this.options.saveColumnInfo.filter && col.filterType == Rico.ColumnConst.USERFILTER) {
688 var filterTemp=[col.filterOp];
689 for (var j=0; j<col.filterValues.length; j++)
690 filterTemp.push(escape(col.filterValues[j]));
691 cookieVals.push('f'+i+':'+filterTemp.join('~'));
694 Rico.setCookie(this.options.cookiePrefix+this.tableId, cookieVals.join(','), this.options.cookieDays, this.options.cookiePath, this.options.cookieDomain);
710 DOLLAR: {type:'number', prefix:'$', decPlaces:2, ClassName:'alignright'},
711 EURO: {type:'number', prefix:'€', decPlaces:2, ClassName:'alignright'},
712 PERCENT: {type:'number', suffix:'%', decPlaces:2, multiplier:100, ClassName:'alignright'},
713 QTY: {type:'number', decPlaces:0, ClassName:'alignright'},
714 DEFAULT: {type:"showTags"}
719 * @class Define methods that are common to columns in both SimpleGrid and LiveGrid
721 Rico.TableColumnBase = function() {};
723 Rico.TableColumnBase.prototype = {
726 * Common code used to initialize the column in both SimpleGrid & LiveGrid
728 baseInit: function(liveGrid,colIdx,hdrInfo,tabIdx) {
729 Rico.log("TableColumnBase.init index="+colIdx+" tabIdx="+tabIdx);
730 this.liveGrid = liveGrid;
732 this.hideWidth = Rico.isKonqueror || Rico.isWebKit || 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.
733 this.options = liveGrid.options;
734 this.tabIdx = tabIdx;
735 this.hdrCell = hdrInfo.cell;
736 this.body = document.getElementsByTagName("body")[0];
737 this.displayName = this.getDisplayName(this.hdrCell);
738 var divs=this.hdrCell.getElementsByTagName('div');
739 this.hdrColDiv=(divs.length<1) ? Rico.wrapChildren(this.hdrCell,'ricoLG_col') : divs[0];
740 this.hdrCellDiv=(divs.length<2) ? Rico.wrapChildren(this.hdrColDiv,'ricoLG_cell') : divs[1];
741 var sectionIndex= tabIdx==0 ? colIdx : colIdx-liveGrid.options.frozenColumns;
742 this.dataCell = liveGrid.tbody[tabIdx].rows[0].cells[sectionIndex];
743 divs=this.dataCell.getElementsByTagName('div');
744 this.dataColDiv=(divs.length<1) ? Rico.wrapChildren(this.dataCell,'ricoLG_col') : divs[0];
746 this.mouseDownHandler= Rico.eventHandle(this,'handleMouseDown');
747 this.mouseMoveHandler= Rico.eventHandle(this,'handleMouseMove');
748 this.mouseUpHandler = Rico.eventHandle(this,'handleMouseUp');
749 this.mouseOutHandler = Rico.eventHandle(this,'handleMouseOut');
751 this.fieldName = 'col'+this.index;
752 var spec = liveGrid.options.columnSpecs[colIdx];
753 this.format=Rico.extend( {}, Rico.ColumnConst.DEFAULT);
754 switch (typeof spec) {
756 if (typeof spec.format=='string') Rico.extend(this.format, Rico.ColumnConst[spec.format.toUpperCase()]);
757 Rico.extend(this.format, spec);
760 if (spec.slice(0,4)=='spec') spec=spec.slice(4).toUpperCase(); // for backwards compatibility
761 if (typeof Rico.ColumnConst[spec]=='object') Rico.extend(this.format, Rico.ColumnConst[spec]);
764 Rico.addClass(this.dataColDiv, this.colClassName());
766 if (typeof this.format.visible=='boolean') this.visible=this.format.visible;
767 Rico.log("TableColumn.init index="+colIdx+" fieldName="+this.fieldName);
768 this.sortable = typeof this.format.canSort=='boolean' ? this.format.canSort : liveGrid.options.canSortDefault;
769 this.currentSort = Rico.ColumnConst.UNSORTED;
770 this.filterable = typeof this.format.canFilter=='boolean' ? this.format.canFilter : liveGrid.options.canFilterDefault;
771 this.filterType = Rico.ColumnConst.UNFILTERED;
772 this.hideable = typeof this.format.canHide=='boolean' ? this.format.canHide : liveGrid.options.canHideDefault;
774 var wi=(typeof(this.format.width)=='number') ? this.format.width : hdrInfo.initWidth;
775 wi=(typeof(wi)=='number') ? Math.max(wi,Rico.ColumnConst.MINWIDTH) : liveGrid.options.defaultWidth;
776 this.setColWidth(wi);
777 if (!this.visible) this.setDisplay('none');
780 colClassName: function() {
781 return this.format.ClassName ? this.format.ClassName : this.liveGrid.tableId+'_col'+this.index;
784 insertResizer: function() {
785 //this.hdrCell.style.width='';
786 if (this.format.noResize) return;
787 var resizer=document.createElement('div');
788 resizer.className='ricoLG_Resize';
789 resizer.style[this.liveGrid.align[1]]='0px';
790 if (this.options.resizeBackground) {
791 var resizePath=Rico.imgDir+this.liveGrid.options.resizeBackground;
792 if (Rico.isIE && Rico.ieVersion < 8)
793 resizePath=location.protocol+resizePath; // IE6+7 only
794 resizer.style.backgroundImage='url('+resizePath+')';
796 this.hdrCellDiv.appendChild(resizer);
797 Rico.eventBind(resizer,"mousedown", this.mouseDownHandler, false);
801 * get the display name of a column
803 getDisplayName: function(el) {
804 var anchors=el.getElementsByTagName("A");
805 //Check the existance of A tags
806 if (anchors.length > 0)
807 return anchors[0].innerHTML;
809 return Rico.stripTags(el.innerHTML);
812 _clear: function(gridCell) {
813 gridCell.innerHTML=' ';
816 clearCell: function(rowIndex) {
817 var gridCell=this.cell(rowIndex);
818 this._clear(gridCell,rowIndex);
819 if (!this.liveGrid.buffer) return;
820 var acceptAttr=this.liveGrid.buffer.options.acceptAttr;
821 for (var k=0; k<acceptAttr.length; k++) {
822 switch (acceptAttr[k]) {
823 case 'style': gridCell.style.cssText=''; break;
824 case 'class': gridCell.className=''; break;
825 default: gridCell['_'+acceptAttr[k]]=''; break;
830 dataTable: function() {
831 return this.liveGrid.tabs[this.tabIdx];
834 numRows: function() {
835 return this.dataColDiv.childNodes.length;
838 clearColumn: function() {
839 var childCnt=this.numRows();
840 for (var r=0; r<childCnt; r++)
845 return this.dataColDiv.childNodes[r];
848 getFormattedValue: function(r,xImg,xForm,xClass) {
849 return Rico.getInnerText(this.cell(r),xImg,xForm,xClass);
852 setColWidth: function(wi) {
853 if (typeof wi=='number') {
855 if (wi < Rico.ColumnConst.MINWIDTH) return;
858 Rico.log('setColWidth '+this.index+': '+wi);
860 this.hdrColDiv.style.width=wi;
861 this.dataColDiv.style.width=wi;
864 pluginMouseEvents: function() {
865 if (this.mousePluggedIn==true) return;
866 Rico.eventBind(this.body,"mousemove", this.mouseMoveHandler, false);
867 Rico.eventBind(this.body,"mouseup", this.mouseUpHandler , false);
868 Rico.eventBind(this.body,"mouseout", this.mouseOutHandler , false);
869 this.mousePluggedIn=true;
872 unplugMouseEvents: function() {
873 Rico.eventUnbind(this.body,"mousemove", this.mouseMoveHandler, false);
874 Rico.eventUnbind(this.body,"mouseup", this.mouseUpHandler , false);
875 Rico.eventUnbind(this.body,"mouseout", this.mouseOutHandler , false);
876 this.mousePluggedIn=false;
879 handleMouseDown: function(e) {
880 this.resizeStart=Rico.eventClient(e).x;
881 this.origWidth=parseInt(this.colWidth,10);
882 var p=Rico.positionedOffset(this.hdrCell);
883 if (this.liveGrid.direction=='rtl') {
884 this.edge=p.left+this.liveGrid.options.scrollBarWidth;
885 switch (this.tabIdx) {
886 case 0: this.edge+=this.liveGrid.innerDiv.offsetWidth; break;
887 case 1: this.edge-=this.liveGrid.scrollDiv.scrollLeft; break;
890 this.edge=p.left+this.hdrCell.offsetWidth;
891 if (this.tabIdx>0) this.edge+=Rico.nan2zero(this.liveGrid.tabs[0].offsetWidth);
893 this.liveGrid.resizeDiv.style.left=this.edge+"px";
894 this.liveGrid.resizeDiv.style.display="";
895 this.liveGrid.outerDiv.style.cursor='e-resize';
896 this.tmpHighlight=this.liveGrid.highlightEnabled;
897 this.liveGrid.highlightEnabled=false;
898 this.pluginMouseEvents();
902 handleMouseMove: function(e) {
903 var delta=Rico.eventClient(e).x-this.resizeStart;
904 var newWidth=(this.liveGrid.direction=='rtl') ? this.origWidth-delta : this.origWidth+delta;
905 if (newWidth < Rico.ColumnConst.MINWIDTH) return;
906 this.liveGrid.resizeDiv.style.left=(this.edge+delta)+"px";
907 this.colWidth=newWidth;
911 handleMouseUp: function(e) {
912 this.unplugMouseEvents();
913 Rico.log('handleMouseUp '+this.liveGrid.tableId);
914 this.liveGrid.outerDiv.style.cursor='';
915 this.liveGrid.resizeDiv.style.display="none";
916 this.setColWidth(this.colWidth);
917 this.customWidth=true;
918 this.liveGrid.setCookie();
919 this.liveGrid.highlightEnabled=this.tmpHighlight;
920 this.liveGrid.sizeDivs();
924 handleMouseOut: function(e) {
925 var reltg = Rico.eventRelatedTarget(e) || e.toElement;
926 while (reltg != null && reltg.nodeName.toLowerCase() != 'body')
927 reltg=reltg.parentNode;
928 if (reltg!=null && reltg.nodeName.toLowerCase() == 'body') return true;
929 this.handleMouseUp(e);
933 setDisplay: function(d) {
934 this.hdrCell.style.display=d;
935 this.hdrColDiv.style.display=d;
936 this.dataCell.style.display=d;
937 this.dataColDiv.style.display=d;
940 hideshow: function(visible,noresize) {
941 this.setDisplay(visible ? '' : 'none');
942 this.liveGrid.cancelMenu();
943 this.visible=visible;
944 this.customVisible=true;
945 if (noresize) return;
946 this.liveGrid.setCookie();
947 this.liveGrid.sizeDivs();
950 hideColumn: function() {
951 Rico.log('hideColumn '+this.liveGrid.tableId);
952 this.hideshow(false,false);
955 showColumn: function() {
956 Rico.log('showColumn '+this.liveGrid.tableId);
957 this.hideshow(true,false);
960 chooseColumn: function(e) {
961 var elem=Rico.eventElement(e);
962 this.hideshow(elem.checked,false);
965 setImage: function() {
966 if ( this.currentSort == Rico.ColumnConst.SORT_ASC ) {
967 this.imgSort.style.display='inline-block';
968 this.imgSort.className=Rico.theme.sortAsc || 'ricoLG_sortAsc';
969 } else if ( this.currentSort == Rico.ColumnConst.SORT_DESC ) {
970 this.imgSort.style.display='inline-block';
971 this.imgSort.className=Rico.theme.sortDesc || 'ricoLG_sortDesc';
973 this.imgSort.style.display='none';
975 if (this.filterType == Rico.ColumnConst.USERFILTER) {
976 this.imgFilter.style.display='';
977 this.imgFilter.title=this.getFilterText();
979 this.imgFilter.style.display='none';
983 canHideShow: function() {
984 return this.hideable;
989 Rico.includeLoaded('ricoGridCommon.js');