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("LiveGrid requires the Rico JavaScript framework");
20 if (!Rico.Buffer) Rico.Buffer = {};
22 Rico.Buffer.Base = function(dataTable, options) {
23 this.initialize(dataTable, options);
25 /** @lends Rico.Buffer.Base# */
26 Rico.Buffer.Base.prototype = {
28 * @class Defines the static buffer class (no AJAX).
29 * Loads buffer with data that already exists in the document as an HTML table or passed via javascript.
30 * Also serves as a base class for AJAX-enabled buffers.
33 initialize: function(dataTable, options) {
35 this.updateInProgress = false;
37 this.rcvdRowCount = false; // true if an eof element was included in the last xml response
38 this.foundRowCount = false; // true if an xml response is ever received with eof true
40 this.rowcntContent = "";
44 canFilter : true, // does buffer object support filtering?
45 isEncoded : true, // is the data received via ajax html encoded?
46 acceptAttr : [] // attributes that can be copied from original/ajax data (e.g. className, style, id)
48 Rico.extend(this.options, options || {});
50 this.loadRowsFromTable(dataTable,this.options.fixedHdrRows);
56 registerGrid: function(liveGrid) {
57 this.liveGrid = liveGrid;
60 setTotalRows: function( newTotalRows ) {
61 if (typeof(newTotalRows)!='number') newTotalRows=this.size;
62 if (this.totalRows == newTotalRows) return;
63 this.totalRows = newTotalRows;
64 if (!this.liveGrid) return;
65 Rico.log("setTotalRows, newTotalRows="+newTotalRows);
66 switch (this.liveGrid.sizeTo) {
68 this.liveGrid.resizeWindow();
71 this.liveGrid.setPageSize(newTotalRows);
74 this.liveGrid.updateHeightDiv();
79 loadRowsFromTable: function(tableElement,firstRow) {
80 var newRows = new Array();
81 var trs = tableElement.getElementsByTagName("tr");
82 for ( var i=firstRow || 0; i < trs.length; i++ ) {
83 var row = new Array();
84 var cells = trs[i].getElementsByTagName("td");
85 for ( var j=0; j < cells.length ; j++ )
86 row[j]=cells[j].innerHTML;
89 this.loadRows(newRows);
92 loadRowsFromArray: function(array2D) {
93 for ( var i=0; i < array2D.length; i++ ) {
94 for ( var j=0; j < array2D[i].length ; j++ ) {
95 array2D[i][j]=array2D[i][j].toString();
98 this.loadRows(array2D);
101 loadRows: function(jstable) {
102 this.baseRows = jstable;
104 this.size = this.baseRows.length;
107 dom2jstable: function(rowsElement) {
108 Rico.log('dom2jstable: encoded='+this.options.isEncoded);
109 var newRows = new Array();
110 var trs = rowsElement.getElementsByTagName("tr");
111 for ( var i=0; i < trs.length; i++ ) {
112 var row = new Array();
113 var cells = trs[i].getElementsByTagName("td");
114 for ( var j=0; j < cells.length ; j++ )
115 row[j]=Rico.getContentAsString(cells[j],this.options.isEncoded);
121 dom2jstableAttr: function(rowsElement,firstRow) {
122 var acceptAttr=this.options.acceptAttr;
123 Rico.log("dom2jstableAttr start, # attr="+acceptAttr.length);
124 var newRows = new Array();
125 var trs = rowsElement.getElementsByTagName("tr");
126 for ( var i=firstRow || 0; i < trs.length; i++ ) {
127 var row = new Array();
128 var cells = trs[i].getElementsByTagName("td");
129 for ( var j=0; j < cells.length ; j++ ) {
131 for (var k=0; k<acceptAttr.length; k++)
132 row[j]['_'+acceptAttr[k]]=cells[j].getAttribute(acceptAttr[k]);
133 if (Rico.isIE) row[j]._class=cells[j].getAttribute('className');
137 Rico.log("dom2jstableAttr end");
141 _blankRow: function() {
143 for (var i=0; i<this.liveGrid.columns.length; i++) {
149 deleteRows: function(rowIndex,cnt) {
150 this.baseRows.splice(rowIndex,typeof(cnt)=='number' ? cnt : 1);
151 this.liveGrid.isPartialBlank=true;
152 this.size=this.baseRows.length;
155 insertRow: function(beforeRowIndex) {
156 var r=this._blankRow();
157 this.baseRows.splice(beforeRowIndex,0,r);
158 this.size=this.baseRows.length;
159 this.liveGrid.isPartialBlank=true;
160 if (this.startPos < 0) this.startPos=0;
164 appendRows: function(cnt) {
166 for (var i=0; i<cnt; i++) {
167 var r=this._blankRow();
168 this.baseRows.push(r);
171 this.size=this.baseRows.length;
172 this.liveGrid.isPartialBlank=true;
173 if (this.startPos < 0) this.startPos=0;
177 sortFunc: function(coltype) {
179 case 'number': return Rico.bind(this,'_sortNumeric');
180 case 'control':return Rico.bind(this,'_sortControl');
181 default: return Rico.bind(this,'_sortAlpha');
185 sortBuffer: function(colnum) {
186 if (!this.baseRows) {
187 this.delayedSortCol=colnum;
190 this.liveGrid.showMsg(Rico.getPhraseById("sorting"));
191 this.sortColumn=colnum;
192 var col=this.liveGrid.columns[colnum];
193 this.getValFunc=col._sortfunc;
194 this.baseRows.sort(this.sortFunc(col.format.type));
195 if (col.getSortDirection()=='DESC') this.baseRows.reverse();
198 _sortAlpha: function(a,b) {
199 var aa = this.sortColumn<a.length ? Rico.getInnerText(a[this.sortColumn]) : '';
200 var bb = this.sortColumn<b.length ? Rico.getInnerText(b[this.sortColumn]) : '';
201 if (aa==bb) return 0;
202 if (aa<bb) return -1;
206 _sortNumeric: function(a,b) {
207 var aa = this.sortColumn<a.length ? this.nan2zero(Rico.getInnerText(a[this.sortColumn])) : 0;
208 var bb = this.sortColumn<b.length ? this.nan2zero(Rico.getInnerText(b[this.sortColumn])) : 0;
212 nan2zero: function(n) {
213 if (typeof(n)=='string') n=parseFloat(n);
214 return isNaN(n) || typeof(n)=='undefined' ? 0 : n;
217 _sortControl: function(a,b) {
218 var aa = this.sortColumn<a.length ? Rico.getInnerText(a[this.sortColumn]) : '';
219 var bb = this.sortColumn<b.length ? Rico.getInnerText(b[this.sortColumn]) : '';
220 if (this.getValFunc) {
221 aa=this.getValFunc(aa);
222 bb=this.getValFunc(bb);
224 if (aa==bb) return 0;
225 if (aa<bb) return -1;
237 isInRange: function(position) {
238 var lastRow=Math.min(this.totalRows, position + this.liveGrid.pageSize);
239 return (position >= this.startPos) && (lastRow <= this.endPos()); // && (this.size != 0);
243 return this.startPos + this.rows.length;
246 fetch: function(offset) {
247 Rico.log('fetch '+this.liveGrid.tableId+': offset='+offset);
250 this.rcvdRowCount = true;
251 this.foundRowCount = true;
252 if (offset < 0) offset=0;
253 this.liveGrid.refreshContents(offset);
258 * @return a 2D array of buffer data representing the rows that are currently visible on the grid
260 visibleRows: function() {
261 return this.rows.slice(this.windowStart,this.windowEnd);
264 setWindow: function(startrow, endrow) {
265 this.windowStart = startrow - this.startPos; // position in the buffer of first visible row
266 Rico.log('setWindow '+this.liveGrid.tableId+': '+startrow+', '+endrow+', newstart='+this.windowStart);
267 this.windowEnd = Math.min(endrow,this.size); // position in the buffer of last visible row containing data+1
268 this.windowPos = startrow; // position in the dataset of first visible row
272 * @return true if bufRow is currently visible in the grid
274 isVisible: function(bufRow) {
275 return bufRow < this.rows.length && bufRow >= this.windowStart && bufRow < this.windowEnd;
279 * takes a window row index and returns the corresponding buffer row index
281 bufferRow: function(windowRow) {
282 return this.windowStart+windowRow;
286 * @return buffer cell at the specified visible row/col index
288 getWindowCell: function(windowRow,col) {
289 var bufrow=this.bufferRow(windowRow);
290 return this.isVisible(bufrow) && col < this.rows[bufrow].length ? this.rows[bufrow][col] : null;
293 getWindowAttr: function(windowRow,col) {
294 var bufrow=this.bufferRow(windowRow);
295 return this.attr && this.isVisible(bufrow) && col < this.attr[bufrow].length ? this.attr[bufrow][col] : null;
298 getWindowValue: function(windowRow,col) {
299 return this.getWindowCell(windowRow,col);
302 setWindowValue: function(windowRow,col,newval) {
303 var bufrow=this.bufferRow(windowRow);
304 if (bufrow >= this.windowEnd) return false;
305 return this.setValue(bufrow,col,newval);
308 getCell: function(bufRow,col) {
309 return bufRow < this.size ? this.rows[bufRow][col] : null;
312 getValue: function(bufRow,col) {
313 return this.getCell(bufRow,col);
316 setValue: function(bufRow,col,newval,newstyle) {
317 if (bufRow>=this.size) return false;
318 if (!this.rows[bufRow][col]) this.rows[bufRow][col]={};
319 this.rows[bufRow][col]=newval;
320 if (typeof newstyle=='string') this.rows[bufRow][col]._style=newstyle;
321 this.rows[bufRow][col].modified=true;
325 getRows: function(start, count) {
326 var begPos = start - this.startPos;
327 var endPos = Math.min(begPos + count,this.size);
328 var results = new Array();
329 for ( var i=begPos; i < endPos; i++ ) {
330 results.push(this.rows[i]);
335 applyFilters: function() {
336 var newRows=[],re=[];
337 var r,c,n,i,showRow,filtercnt;
338 var cols=this.liveGrid.columns;
339 for (n=0,filtercnt=0; n<cols.length; n++) {
341 if (c.filterType == Rico.ColumnConst.UNFILTERED) continue;
343 if (c.filterOp=='LIKE') re[n]=new RegExp(c.filterValues[0],'i');
345 Rico.log('applyFilters: # of filters='+filtercnt);
347 this.rows = this.baseRows;
349 for (r=0; r<this.baseRows.length; r++) {
351 for (n=0; n<cols.length && showRow; n++) {
353 if (c.filterType == Rico.ColumnConst.UNFILTERED) continue;
354 switch (c.filterOp) {
356 showRow=re[n].test(this.baseRows[r][n]);
359 showRow=this.baseRows[r][n]==c.filterValues[0];
362 for (i=0; i<c.filterValues.length && showRow; i++)
363 showRow=this.baseRows[r][n]!=c.filterValues[i];
366 if (c.format.type=='number')
367 showRow=this.nan2zero(this.baseRows[r][n])<=this.nan2zero(c.filterValues[0]);
369 showRow=this.baseRows[r][n]<=c.filterValues[0];
372 if (c.format.type=='number')
373 showRow=this.nan2zero(this.baseRows[r][n])>=this.nan2zero(c.filterValues[0]);
375 showRow=this.baseRows[r][n]>=c.filterValues[0];
378 showRow=this.baseRows[r][n]=='';
381 showRow=this.baseRows[r][n]!='';
385 if (showRow) newRows.push(this.baseRows[r]);
389 this.rowcntContent = this.size = this.rows.length;
392 printAll: function(exportType) {
393 this.liveGrid.showMsg(Rico.getPhraseById('exportInProgress'));
394 Rico.runLater(10,this,'_printAll',exportType); // allow message to paint
398 * Support function for printAll()
400 _printAll: function(exportType) {
401 this.liveGrid.exportStart();
402 this.exportBuffer(this.getRows(0,this.totalRows));
403 this.liveGrid.exportFinish(exportType);
407 * Copies visible rows to a new window as a simple html table.
409 printVisible: function(exportType) {
410 this.liveGrid.showMsg(Rico.getPhraseById('exportInProgress'));
411 Rico.runLater(10,this,'_printVisible',exportType); // allow message to paint
414 _printVisible: function(exportType) {
415 this.liveGrid.exportStart();
416 this.exportBuffer(this.visibleRows());
417 this.liveGrid.exportFinish(exportType);
421 * Send all rows to print/export window
423 exportBuffer: function(rows,startPos) {
424 var r,c,v,col,exportText;
425 Rico.log("exportBuffer: "+rows.length+" rows");
426 var exportStyles=this.liveGrid.getExportStyles(this.liveGrid.tbody[0]);
428 var totalcnt=startPos || 0;
429 var cols=this.liveGrid.columns;
430 for (c=0; c<cols.length; c++) {
431 if (cols[c].visible) tdstyle[c]=this.liveGrid.exportStyle(cols[c].cell(0),exportStyles); // assumes row 0 style applies to all rows
433 for(r=0; r < rows.length; r++) {
435 for (c=0; c<cols.length; c++) {
436 if (!cols[c].visible) continue;
438 col.expStyle=tdstyle[c];
439 v=col._export(rows[r][c],rows[r]);
440 if (v=='') v=' ';
441 exportText+="<td style='"+col.expStyle+"'>"+v+"</td>";
443 this.liveGrid.exportRows.push(exportText);
445 if (totalcnt % 10 == 0) window.status=Rico.getPhraseById('exportStatus',totalcnt);
452 // Rico.LiveGrid -----------------------------------------------------
454 Rico.LiveGrid = function(tableId, buffer, options) {
455 this.initialize(tableId, buffer, options);
459 * @lends Rico.LiveGrid#
460 * @property tableId id string for this grid
461 * @property options the options object passed to the constructor extended with defaults
462 * @property buffer the buffer object containing the data for this grid
463 * @property columns array of {@link Rico.LiveGridColumn} objects
465 Rico.LiveGrid.prototype = {
467 * @class Buffered LiveGrid component
468 * @extends Rico.GridCommon
471 initialize: function( tableId, buffer, options ) {
472 Rico.extend(this, Rico.GridCommon);
473 Rico.extend(this, Rico.LiveGridMethods);
475 this.tableId = tableId;
476 this.buffer = buffer;
477 this.actionId='_action_'+tableId;
478 Rico.setDebugArea(tableId+"_debugmsgs"); // if used, this should be a textarea
480 Rico.extend(this.options, {
481 visibleRows : -1, // -1 or 'window'=size grid to client window; -2 or 'data'=size grid to min(window,data); -3 or 'body'=size so body does not have a scrollbar; -4 or 'parent'=size to parent element (e.g. if grid is inside a div)
483 offset : 0, // first row to be displayed
484 prefetchBuffer : true, // load table on page load?
487 canSortDefault : true, // can be overridden in the column specs
488 canFilterDefault : buffer.options.canFilter, // can be overridden in the column specs
489 canHideDefault : true, // can be overridden in the column specs
491 // highlight & selection parameters
492 highlightElem : 'none',// what gets highlighted/selected (cursorRow, cursorCell, menuRow, menuCell, selection, or none)
493 highlightSection : 3, // which section gets highlighted (frozen=1, scrolling=2, all=3, none=0)
494 highlightMethod : 'class', // outline, class, both (outline is less CPU intensive on the client)
495 highlightClass : Rico.theme.gridHighlightClass || 'ricoLG_selection',
497 // export/print parameters
498 maxPrint : 1000, // max # of rows that can be printed/exported, 0=disable print/export feature
500 // heading parameters
501 headingSort : 'link', // link: make headings a link that will sort column, hover: make headings a hoverset, none: events on headings are disabled
502 hdrIconsFirst : true, // true: put sort & filter icons before header text, false: after
503 filterImg : 'filtercol.gif'
506 // sortCol: initial sort column
508 this.options.sortHandler = Rico.bind(this,'sortHandler');
509 this.options.filterHandler = Rico.bind(this,'filterHandler');
510 this.options.onRefreshComplete = Rico.bind(this,'bookmarkHandler');
511 this.options.rowOverHandler = Rico.eventHandle(this,'rowMouseOver');
512 this.options.mouseDownHandler = Rico.eventHandle(this,'selectMouseDown');
513 this.options.mouseOverHandler = Rico.eventHandle(this,'selectMouseOver');
514 this.options.mouseUpHandler = Rico.eventHandle(this,'selectMouseUp');
515 Rico.extend(this.options, options || {});
517 switch (typeof this.options.visibleRows) {
519 this.sizeTo=this.options.visibleRows;
520 switch (this.options.visibleRows) {
521 case 'data': this.options.visibleRows=-2; break;
522 case 'body': this.options.visibleRows=-3; break;
523 case 'parent': this.options.visibleRows=-4; break;
524 case 'datamax':this.options.visibleRows=-5; break;
525 default: this.options.visibleRows=-1; break;
529 switch (this.options.visibleRows) {
530 case -1: this.sizeTo='window'; break;
531 case -2: this.sizeTo='data'; break;
532 case -3: this.sizeTo='body'; break;
533 case -4: this.sizeTo='parent'; break;
534 case -5: this.sizeTo='datamax'; break;
535 default: this.sizeTo='fixed'; break;
539 this.sizeTo='window';
540 this.options.visibleRows=-1;
543 this.highlightEnabled=this.options.highlightSection>0;
546 if (this.headerColCnt==0) {
547 alert('ERROR: no columns found in "'+this.tableId+'"');
550 this.createColumnArray('LiveGridColumn');
551 if (this.options.headingSort=='hover')
552 this.createHoverSet();
554 this.bookmark=document.getElementById(this.tableId+"_bookmark");
556 var filterUIrow=this.buffer.options.canFilter ? this.options.FilterLocation : false;
557 if (typeof(filterUIrow)=='number' && filterUIrow<0)
558 filterUIrow=this.addHeadingRow('ricoLG_FilterRow');
559 this.createDataCells(this.options.visibleRows);
560 if (this.pageSize == 0) return;
561 this.buffer.registerGrid(this);
562 if (this.buffer.setBufferSize) this.buffer.setBufferSize(this.pageSize);
563 this.scrollTimeout = null;
564 this.lastScrollPos = 0;
565 this.attachMenuEvents();
567 // preload the images...
568 new Image().src = Rico.imgDir+this.options.filterImg;
569 Rico.log("images preloaded");
571 this.setSortUI( this.options.sortCol, this.options.sortDir );
573 if (this.listInvisible().length==this.columns.length)
574 this.columns[0].showColumn();
576 this.scrollDiv.style.display="";
577 if (this.buffer.totalRows>0)
578 this.updateHeightDiv();
579 if (this.options.prefetchBuffer) {
580 if (this.bookmark) this.bookmark.innerHTML = Rico.getPhraseById('bookmarkLoading');
581 if (this.options.canFilterDefault && this.options.getQueryParms)
582 this.checkForFilterParms();
583 this.scrollToRow(this.options.offset);
584 this.buffer.fetch(this.options.offset);
586 if (typeof(filterUIrow)=='number')
587 this.createFilters(filterUIrow);
588 this.scrollEventFunc=Rico.eventHandle(this,'handleScroll');
589 this.wheelEventFunc=Rico.eventHandle(this,'handleWheel');
590 this.wheelEvent=(Rico.isIE || Rico.isOpera || Rico.isWebKit) ? 'mousewheel' : 'DOMMouseScroll';
591 if (this.options.offset && this.options.offset < this.buffer.totalRows)
592 Rico.runLater(50,this,'scrollToRow',this.options.offset); // Safari requires a delay
594 this.setHorizontalScroll();
595 Rico.log("setHorizontalScroll done");
596 if (this.options.windowResize)
597 Rico.runLater(100,this,'pluginWindowResize');
598 Rico.log("initialize complete for "+this.tableId);
603 Rico.LiveGridMethods = {
604 /** @lends Rico.LiveGrid# */
606 createHoverSet: function() {
608 for( var c=0; c < this.headerColCnt; c++ ) {
609 if (this.columns[c].sortable) {
\r
610 hdrs.push(this.columns[c].hdrCellDiv);
613 this.hoverSet = new Rico.HoverSet(hdrs);
616 checkForFilterParms: function() {
617 var s=window.location.search;
618 if (s.charAt(0)=='?') s=s.substring(1);
619 var pairs = s.split('&');
620 for (var i=0; i<pairs.length; i++) {
621 if (pairs[i].match(/^f\[\d+\]/)) {
622 this.buffer.options.requestParameters.push(pairs[i]);
628 * Refreshes a detail grid from a master grid
629 * @returns row index on master table on success, -1 otherwise
631 drillDown: function(e,masterColNum,detailColNum) {
632 var cell=Rico.eventElement(e || window.event);
633 cell=Rico.getParentByTagName(cell,'div','ricoLG_cell');
634 if (!cell) return -1;
635 var idx=this.winCellIndex(cell);
636 if (idx.row >= this.buffer.totalRows) return -1
638 this.menuIdx=idx; // ensures selection gets cleared when menu is displayed
640 var drillValue=this.buffer.getWindowCell(idx.row,masterColNum);
641 for (var i=3; i<arguments.length; i++)
642 arguments[i].setDetailFilter(detailColNum,drillValue);
647 * set filter on a detail grid that is in a master-detail relationship
649 setDetailFilter: function(colNumber,filterValue) {
650 var c=this.columns[colNumber];
651 c.format.ColData=filterValue;
652 c.setSystemFilter('EQ',filterValue);
656 * Create one table for frozen columns and one for scrolling columns.
657 * Also create div's to contain them.
658 * @returns true on success
660 createTables: function() {
661 var insertloc,hdrSrc,i;
662 var table = document.getElementById(this.tableId) || document.getElementById(this.tableId+'_outerDiv');
663 if (!table) return false;
664 if (table.tagName.toLowerCase()=='table') {
665 var theads=table.getElementsByTagName("thead");
666 if (theads.length == 1) {
667 Rico.log("createTables: using thead section, id="+this.tableId);
668 if (this.options.PanelNamesOnTabHdr && this.options.panels) {
669 var r=theads[0].insertRow(0);
670 this.insertPanelNames(r, 0, this.options.frozenColumns, 'ricoFrozen');
671 this.insertPanelNames(r, this.options.frozenColumns, this.options.columnSpecs.length);
673 hdrSrc=theads[0].rows;
675 Rico.log("createTables: using tbody section, id="+this.tableId);
676 hdrSrc=new Array(table.rows[0]);
679 } else if (this.options.columnSpecs.length > 0) {
680 if (!table.id.match(/_outerDiv$/)) insertloc=table;
681 Rico.log("createTables: inserting at "+table.tagName+", id="+this.tableId);
683 alert("ERROR!\n\nUnable to initialize '"+this.tableId+"'\n\nLiveGrid terminated");
688 this.scrollTabs = this.createDiv("scrollTabs",this.innerDiv);
689 this.shadowDiv = this.createDiv("shadow",this.scrollDiv);
690 this.shadowDiv.style.direction='ltr'; // avoid FF bug
691 this.scrollDiv.style.display="none";
692 this.scrollDiv.scrollTop=0;
693 if (this.options.highlightMethod!='class') {
694 this.highlightDiv=[];
695 switch (this.options.highlightElem) {
698 this.highlightDiv[0] = this.createDiv("highlight",this.outerDiv);
699 this.highlightDiv[0].style.display="none";
703 for (i=0; i<2; i++) {
704 this.highlightDiv[i] = this.createDiv("highlight",i==0 ? this.frozenTabs : this.scrollTabs);
705 this.highlightDiv[i].style.display="none";
706 this.highlightDiv[i].id+=i;
710 // create one div for each side of the rectangle
711 var parentDiv=this.options.highlightSection==1 ? this.frozenTabs : this.scrollTabs;
712 for (i=0; i<4; i++) {
713 this.highlightDiv[i] = this.createDiv("highlight",parentDiv);
714 this.highlightDiv[i].style.display="none";
715 this.highlightDiv[i].style.overflow="hidden";
716 this.highlightDiv[i].id+=i;
717 this.highlightDiv[i].style[i % 2==0 ? 'height' : 'width']="0px";
724 for (i=0; i<2; i++) {
725 this.tabs[i] = document.createElement("table");
726 this.tabs[i].className = 'ricoLG_table';
727 this.tabs[i].border=0;
728 this.tabs[i].cellPadding=0;
729 this.tabs[i].cellSpacing=0;
730 this.tabs[i].id = this.tableId+"_tab"+i;
731 this.thead[i]=this.tabs[i].createTHead();
732 this.thead[i].className='ricoLG_top';
733 if (Rico.theme.gridheader) Rico.addClass(this.thead[i],Rico.theme.gridheader);
734 this.tbody[i]=Rico.getTBody(this.tabs[i]);
735 this.tbody[i].className='ricoLG_bottom';
736 if (Rico.theme.gridcontent) Rico.addClass(this.tbody[i],Rico.theme.gridcontent);
737 this.tbody[i].insertRow(-1);
739 this.frozenTabs.appendChild(this.tabs[0]);
740 this.scrollTabs.appendChild(this.tabs[1]);
741 if (insertloc) insertloc.parentNode.insertBefore(this.outerDiv,insertloc);
743 this.headerColCnt = this.getColumnInfo(hdrSrc);
744 this.loadHdrSrc(hdrSrc);
746 this.createHdr(0,0,this.options.frozenColumns);
747 this.createHdr(1,this.options.frozenColumns,this.options.columnSpecs.length);
748 if (this.options.PanelNamesOnTabHdr && this.options.panels) {
749 this.insertPanelNames(this.thead[0].insertRow(0), 0, this.options.frozenColumns);
750 this.insertPanelNames(this.thead[1].insertRow(0), this.options.frozenColumns, this.options.columnSpecs.length);
753 this.headerColCnt = this.getColumnInfo(this.thead[i].rows);
755 for( var c=0; c < this.headerColCnt; c++ )
756 this.tbody[c<this.options.frozenColumns ? 0 : 1].rows[0].insertCell(-1);
757 if (insertloc) table.parentNode.removeChild(table);
758 Rico.log('createTables end');
762 createDataCells: function(visibleRows) {
763 if (visibleRows < 0) {
764 for (var i=0; i<this.options.minPageRows; i++)
765 this.appendBlankRow();
767 this.autoAppendRows(this.remainingHt());
769 for( var r=0; r < visibleRows; r++ )
770 this.appendBlankRow();
772 var s=this.options.highlightSection;
773 if (s & 1) this.attachHighlightEvents(this.tbody[0]);
774 if (s & 2) this.attachHighlightEvents(this.tbody[1]);
778 * @param colnum column number
779 * @return id string for a filter element
781 filterId: function(colnum) {
782 return 'RicoFilter_'+this.tableId+'_'+colnum;
786 * Create filter elements in heading
787 * Reads this.columns[].filterUI to determine type of filter element for each column (t=text box, s=select list, c=custom)
788 * @param r heading row where filter elements will be placed
790 createFilters: function(r) {
791 for( var c=0; c < this.headerColCnt; c++ ) {
792 var col=this.columns[c];
794 if (typeof fmt.filterUI!='string') continue;
795 var cell=this.hdrCells[r][c].cell;
796 var field,name=this.filterId(c);
\r
797 var divs=cell.getElementsByTagName('div');
798 // copy text alignment from data cell
799 var align=Rico.getStyle(this.cell(0,c),'textAlign');
800 divs[1].style.textAlign=align;
801 switch (fmt.filterUI.charAt(0)) {
804 field=Rico.createFormField(divs[1],'input','text',name,name);
805 var size=fmt.filterUI.match(/\d+/);
806 field.maxLength=fmt.Length || 50;
\r
807 field.size=size ? parseInt(size,10) : 10;
808 divs[1].appendChild(Rico.clearButton(Rico.eventHandle(col,'filterClear')));
809 if (col.filterType==Rico.ColumnConst.USERFILTER && col.filterOp=='LIKE') {
810 var v=col.filterValues[0];
811 if (v.charAt(0)=='*') v=v.substr(1);
812 if (v.slice(-1)=='*') v=v.slice(0,-1);
816 Rico.eventBind(field,'keyup',Rico.eventHandle(col,'filterKeypress'),false);
817 col.filterField=field;
\r
823 field=Rico.createFormField(divs[1],'select',null,name);
\r
824 Rico.addSelectOption(field,this.options.FilterAllToken,Rico.getPhraseById("filterAll"));
\r
825 col.filterField=field;
827 Rico.extend(options, this.buffer.ajaxOptions);
828 var colnum=typeof(fmt.filterCol)=='number' ? fmt.filterCol : c;
829 options.parameters = {id: this.tableId, distinct:colnum};
830 options.parameters[this.actionId]="query";
831 options.onComplete = Rico.bind(this,'filterValuesUpdate',c);
832 new Rico.ajaxRequest(this.buffer.dataSource, options);
836 if (typeof col._createFilters == 'function')
837 col._createFilters(divs[1], name);
841 this.initFilterImage(r);
845 * update select list filter with values in AJAX response
846 * @returns true on success
848 filterValuesUpdate: function(colnum,request) {
849 var response = request.responseXML.getElementsByTagName("ajax-response");
850 Rico.log("filterValuesUpdate: "+request.status);
851 if (response == null || response.length != 1) return false;
852 response=response[0];
853 var error = response.getElementsByTagName('error');
854 if (error.length > 0) {
855 Rico.log("Data provider returned an error:\n"+Rico.getContentAsString(error[0],this.buffer.isEncoded));
856 alert(Rico.getPhraseById("requestError",Rico.getContentAsString(error[0],this.buffer.isEncoded)));
859 response=response.getElementsByTagName('response')[0];
\r
860 var rowsElement = response.getElementsByTagName('rows')[0];
\r
861 var col=this.columns[parseInt(colnum,10)];
862 var rows = this.buffer.dom2jstable(rowsElement);
\r
864 if (col.filterType==Rico.ColumnConst.USERFILTER && col.filterOp=='EQ') v=col.filterValues[0];
865 Rico.log('filterValuesUpdate: col='+colnum+' rows='+rows.length);
866 switch (col.format.filterUI.charAt(0)) {
869 col.mFilter = document.body.appendChild(document.createElement("div"));
870 col.mFilter.className = 'ricoLG_mFilter'
871 Rico.hide(col.mFilter);
872 var contentDiv = col.mFilter.appendChild(document.createElement("div"));
873 contentDiv.className = 'ricoLG_mFilter_content'
874 var buttonDiv = col.mFilter.appendChild(document.createElement("div"));
875 buttonDiv.className = 'ricoLG_mFilter_button'
876 col.mFilterButton=buttonDiv.appendChild(document.createElement("button"));
877 col.mFilterButton.innerHTML=Rico.getPhraseById("ok");
878 var eventName=Rico.isWebKit ? 'mousedown' : 'click';
879 Rico.eventBind(col.filterField,eventName,Rico.eventHandle(col,'mFilterSelectClick'));
880 Rico.eventBind(col.mFilterButton,'click',Rico.eventHandle(col,'mFilterFinish'));
881 //col.filterField.options[0].text=$('AllLabel').innerHTML;
882 tab = contentDiv.appendChild(document.createElement("table"));
886 //tbody=(tab.tBodies.length==0) ? tab.appendChild(document.createElement("tbody")) : tab.tBodies[0];
887 var baseId=this.filterId(colnum)+'_';
888 this.createMFilterItem(tab,this.options.FilterAllToken,Rico.getPhraseById("filterAll"),baseId+'all',Rico.eventHandle(col,'mFilterAllClick'));
889 var handle=Rico.eventHandle(col,'mFilterOtherClick')
890 for (var i=0; i<rows.length; i++) {
891 if (rows[i].length>0) {
893 this.createMFilterItem(tab,c,c || Rico.getPhraseById("filterBlank"),baseId+i,handle);
896 col.mFilterInputs=contentDiv.getElementsByTagName('input');
897 col.mFilterLabels=contentDiv.getElementsByTagName('label');
898 col.mFilterFocus=col.mFilterInputs.length ? col.mFilterInputs[0] : col.mFilterButton;
903 for (var i=0; i<rows.length; i++) {
904 if (rows[i].length>0) {
906 opt=Rico.addSelectOption(col.filterField,c,c || Rico.getPhraseById("filterBlank"));
907 if (col.filterType==Rico.ColumnConst.USERFILTER && c==v) opt.selected=true;
910 Rico.eventBind(col.filterField,'change',Rico.eventHandle(col,'filterChange'));
916 createMFilterItem: function(table,code,description,id,eventHandle) {
917 var tr=table.insertRow(-1);
919 if (tr.rowIndex % 2 == 1) tr.className='ricoLG_mFilter_oddrow';
920 var td1=tr.insertCell(-1)
921 var td2=tr.insertCell(-1)
922 var field=Rico.createFormField(td1,'input','checkbox',id);
925 var label = td2.appendChild(document.createElement("label"));
927 label.innerHTML=description;
928 Rico.eventBind(field,'click',eventHandle);
931 unplugHighlightEvents: function() {
932 var s=this.options.highlightSection;
933 if (s & 1) this.detachHighlightEvents(this.tbody[0]);
934 if (s & 2) this.detachHighlightEvents(this.tbody[1]);
938 * place panel names on first row of grid header (used by LiveGridForms)
940 insertPanelNames: function(r,start,limit,cellClass) {
941 Rico.log('insertPanelNames: start='+start+' limit='+limit);
942 r.className='ricoLG_hdg';
943 var lastIdx=-1, span, newCell=null, spanIdx=0;
944 for( var c=start; c < limit; c++ ) {
945 if (lastIdx == this.options.columnSpecs[c].panelIdx) {
948 if (newCell) newCell.colSpan=span;
949 newCell = r.insertCell(-1);
950 if (cellClass) newCell.className=cellClass;
952 lastIdx=this.options.columnSpecs[c].panelIdx;
953 newCell.innerHTML=this.options.panels[lastIdx];
956 if (newCell) newCell.colSpan=span;
960 * create grid header for table i (if none was provided)
962 createHdr: function(i,start,limit) {
963 Rico.log('createHdr: i='+i+' start='+start+' limit='+limit);
964 var mainRow = this.thead[i].insertRow(-1);
965 mainRow.id=this.tableId+'_tab'+i+'h_main';
966 mainRow.className='ricoLG_hdg';
967 for( var c=start; c < limit; c++ ) {
968 var newCell = mainRow.insertCell(-1);
969 newCell.innerHTML=this.options.columnSpecs[c].Hdg;
974 * move header cells in original table to grid
976 loadHdrSrc: function(hdrSrc) {
977 var i,h,c,r,newrow,cells;
978 Rico.log('loadHdrSrc start');
979 for (i=0; i<2; i++) {
980 for (r=0; r<hdrSrc.length; r++) {
981 newrow = this.thead[i].insertRow(-1);
982 newrow.className='ricoLG_hdg '+this.tableId+'_hdg'+r;
985 if (hdrSrc.length==1) {
986 cells=hdrSrc[0].cells;
987 for (c=0; cells.length > 0; c++)
988 this.thead[c<this.options.frozenColumns ? 0 : 1].rows[0].appendChild(cells[0]);
990 for (r=0; r<hdrSrc.length; r++) {
991 cells=hdrSrc[r].cells;
992 for (c=0,h=0; cells.length > 0; c++) {
993 if (cells[0].className=='ricoFrozen') {
994 if (r==this.headerRowIdx) this.options.frozenColumns=c+1;
998 this.thead[h].rows[r].appendChild(cells[0]);
1002 Rico.log('loadHdrSrc end');
1008 sizeDivs: function() {
1009 Rico.log('sizeDivs: '+this.tableId);
1010 //this.cancelMenu();
1012 this.baseSizeDivs();
1013 var firstVisible=this.firstVisible();
1014 if (this.pageSize == 0 || firstVisible < 0) return;
1015 var totRowHt=this.columns[firstVisible].dataColDiv.offsetHeight;
1016 this.rowHeight = Math.round(totRowHt/this.pageSize);
1017 var scrHt=this.dataHt;
1018 if (this.scrWi>0 || Rico.isIE || Rico.isWebKit)
1019 scrHt+=this.options.scrollBarWidth;
1020 this.scrollDiv.style.height=scrHt+'px';
1021 this.innerDiv.style.width=(this.scrWi-this.options.scrollBarWidth+1)+'px';
1022 this.resizeDiv.style.height=this.frozenTabs.style.height=this.innerDiv.style.height=(this.hdrHt+this.dataHt+1)+'px';
1023 Rico.log('sizeDivs scrHt='+scrHt+' innerHt='+this.innerDiv.style.height+' rowHt='+this.rowHeight+' pageSize='+this.pageSize);
1024 var pad=(this.scrWi-this.scrTabWi < this.options.scrollBarWidth) ? 2 : 0;
1025 this.shadowDiv.style.width=(this.scrTabWi+pad)+'px';
1026 this.outerDiv.style.height=(this.hdrHt+scrHt)+'px';
1027 this.setHorizontalScroll();
1030 setHorizontalScroll: function() {
1031 var scrleft=this.scrollDiv.scrollLeft;
1032 this.scrollTabs.style.left=(-scrleft)+'px';
1035 remainingHt: function() {
1037 var winHt=Rico.windowHeight();
1038 var margin=Rico.isIE ? 15 : 10;
1039 // if there is a horizontal scrollbar take it into account
1040 if (!Rico.isIE && window.frameElement && window.frameElement.scrolling=='yes' && this.sizeTo!='parent') margin+=this.options.scrollBarWidth;
1041 switch (this.sizeTo) {
1044 var divTop=Rico.cumulativeOffset(this.outerDiv).top;
1045 tabHt=Math.max(this.tabs[0].offsetHeight,this.tabs[1].offsetHeight);
1046 Rico.log("remainingHt, winHt="+winHt+' tabHt='+tabHt+' gridY='+divTop);
1047 return winHt-divTop-tabHt-this.options.scrollBarWidth-margin; // allow for scrollbar and some margin
1049 var offset=this.offsetFromParent(this.outerDiv);
1050 tabHt=Math.max(this.tabs[0].offsetHeight,this.tabs[1].offsetHeight);
1051 if (Rico.isIE) Rico.hide(this.outerDiv);
1052 var parentHt=this.outerDiv.parentNode.offsetHeight;
1053 if (Rico.isIE) Rico.show(this.outerDiv);
1054 Rico.log("remainingHt, parentHt="+parentHt+' gridY='+offset+' winHt='+winHt+' tabHt='+tabHt);
1055 return parentHt - tabHt - offset - this.options.scrollBarWidth;
1057 //Rico.log("remainingHt, document.height="+document.height);
1058 //Rico.log("remainingHt, body.offsetHeight="+document.body.offsetHeight);
1059 //Rico.log("remainingHt, body.scrollHeight="+document.body.scrollHeight);
1060 //Rico.log("remainingHt, documentElement.scrollHeight="+document.documentElement.scrollHeight);
1061 var bodyHt=Rico.isIE ? document.body.scrollHeight : document.body.offsetHeight;
1062 var remHt=winHt-bodyHt-margin;
1063 if (!Rico.isWebKit) remHt-=this.options.scrollBarWidth;
1064 Rico.log("remainingHt, winHt="+winHt+' pageHt='+bodyHt+' remHt='+remHt);
1067 tabHt=Math.max(this.tabs[0].offsetHeight,this.tabs[1].offsetHeight);
1068 Rico.log("remainingHt, winHt="+winHt+' tabHt='+tabHt);
1069 if (this.sizeTo.slice(-1)=='%') winHt*=parseFloat(this.sizeTo)/100.0;
1070 else if (this.sizeTo.slice(-2)=='px') winHt=parseInt(this.sizeTo,10);
1071 return winHt-tabHt-this.options.scrollBarWidth-margin; // allow for scrollbar and some margin
1075 offsetFromParent: function(element) {
1077 var elParent=element.parentNode;
1079 //Rico.log("offsetFromParent: "+element.tagName+' id='+element.id+' otop='+element.offsetTop);
1080 valueT += element.offsetTop || 0;
1081 element = element.offsetParent;
1082 if (!element || element==null) break;
1083 var p = Rico.getStyle(element, 'position');
1084 if (element.tagName=='BODY' || element.tagName=='HTML' || p=='absolute') return valueT-elParent.offsetTop;
1085 } while (element != elParent);
1089 adjustPageSize: function() {
1090 var remHt=this.remainingHt();
1091 Rico.log('adjustPageSize remHt='+remHt+' lastRow='+this.lastRowPos);
1092 if (remHt > this.rowHeight)
1093 this.autoAppendRows(remHt);
1094 else if (remHt < 0 || this.sizeTo=='data')
1095 this.autoRemoveRows(-remHt);
1098 setPageSize: function(newRowCount) {
1099 newRowCount=Math.min(newRowCount,this.options.maxPageRows);
1100 newRowCount=Math.max(newRowCount,this.options.minPageRows);
1101 this.sizeTo='fixed';
1102 var oldSize=this.pageSize;
1103 while (this.pageSize > newRowCount) {
1106 while (this.pageSize < newRowCount) {
1107 this.appendBlankRow();
1109 this.finishResize(oldSize);
1112 pluginWindowResize: function() {
1113 Rico.log("pluginWindowResize");
1114 this.resizeWindowHandler=Rico.eventHandle(this,'resizeWindow');
1115 Rico.eventBind(window, "resize", this.resizeWindowHandler, false);
1118 unplugWindowResize: function() {
1119 if (!this.resizeWindowHandler) return;
1120 Rico.eventUnbind(window,"resize", this.resizeWindowHandler, false);
1121 this.resizeWindowHandler=null;
1124 resizeWindow: function() {
1125 Rico.log('resizeWindow '+this.tableId+' lastRow='+this.lastRowPos);
1126 if (this.resizeState=='finish') {
1127 Rico.log('resizeWindow postponed');
1128 this.resizeState='resize';
1131 if (!this.sizeTo || this.sizeTo=='fixed') {
1135 if (this.sizeTo=='parent' && Rico.getStyle(this.outerDiv.parentNode,'display') == 'none') return;
1136 var oldSize=this.pageSize;
1137 this.adjustPageSize();
1138 this.finishResize(oldSize);
1141 finishResize: function(oldSize) {
1142 if (this.pageSize > oldSize && this.buffer.totalRows>0) {
1143 this.isPartialBlank=true;
1144 var adjStart=this.adjustRow(this.lastRowPos);
1145 this.buffer.fetch(adjStart);
1146 } else if (this.pageSize < oldSize) {
1147 if (this.options.onRefreshComplete) this.options.onRefreshComplete(this.contentStartPos,this.contentStartPos+this.pageSize-1); // update bookmark
1149 this.resizeState='finish';
1150 Rico.runLater(20,this,'finishResize2');
1151 Rico.log('Resize '+this.tableId+' complete. old size='+oldSize+' new size='+this.pageSize);
1154 finishResize2: function() {
1156 this.updateHeightDiv();
1157 if (this.resizeState=='resize') {
1158 this.resizeWindow();
1160 this.resizeState='';
1164 topOfLastPage: function() {
1165 return Math.max(this.buffer.totalRows-this.pageSize,0);
1168 updateHeightDiv: function() {
1169 var notdisp=this.topOfLastPage();
1170 var ht = notdisp ? this.scrollDiv.clientHeight + Math.floor(this.rowHeight * (notdisp + 0.4)) : 1;
1171 Rico.log("updateHeightDiv, ht="+ht+' scrollDiv.clientHeight='+this.scrollDiv.clientHeight+' rowsNotDisplayed='+notdisp);
1172 this.shadowDiv.style.height=ht+'px';
1175 autoRemoveRows: function(overage) {
1176 if (!this.rowHeight) return;
1177 var removeCnt=Math.ceil(overage / this.rowHeight);
1178 if (this.sizeTo=='data')
1179 removeCnt=Math.max(removeCnt,this.pageSize-this.buffer.totalRows);
1180 Rico.log("autoRemoveRows overage="+overage+" removeCnt="+removeCnt);
1181 for (var i=0; i<removeCnt; i++)
1185 removeRow: function() {
1186 if (this.pageSize <= this.options.minPageRows) return;
1188 for( var c=0; c < this.headerColCnt; c++ ) {
1189 var cell=this.columns[c].cell(this.pageSize);
1190 this.columns[c].dataColDiv.removeChild(cell);
1194 autoAppendRows: function(overage) {
1195 if (!this.rowHeight) return;
1196 var addCnt=Math.floor(overage / this.rowHeight);
1197 Rico.log("autoAppendRows overage="+overage+" cnt="+addCnt+" rowHt="+this.rowHeight);
1198 for (var i=0; i<addCnt; i++) {
1199 if (this.sizeTo=='data' && this.pageSize>=this.buffer.totalRows) break;
1200 this.appendBlankRow();
1205 * on older systems, this can be fairly slow
1207 appendBlankRow: function() {
1208 if (this.pageSize >= this.options.maxPageRows) return;
1209 Rico.log("appendBlankRow #"+this.pageSize);
1210 var cls=this.defaultRowClass(this.pageSize);
1211 for( var c=0; c < this.headerColCnt; c++ ) {
1212 var newdiv = document.createElement("div");
1213 newdiv.className = 'ricoLG_cell '+cls;
1214 newdiv.id=this.tableId+'_'+this.pageSize+'_'+c;
1215 this.columns[c].dataColDiv.appendChild(newdiv);
1216 if (this.columns[c].format.canDrag && Rico.registerDraggable)
1217 Rico.registerDraggable( new Rico.LiveGridDraggable(this, this.pageSize, c), this.options.dndMgrIdx );
1218 newdiv.innerHTML=' '; // this seems to be required by IE
1219 if (this.columns[c]._create) {
1220 this.columns[c]._create(newdiv,this.pageSize);
1226 defaultRowClass: function(rownum) {
1228 if (rownum % 2==0) {
1229 cls='ricoLG_evenRow';
1230 //if (Rico.theme.primary) cls+=' '+Rico.theme.primary;
1232 cls='ricoLG_oddRow';
1233 //if (Rico.theme.secondary) cls+=' '+Rico.theme.secondary;
1238 handleMenuClick: function(e) {
1239 if (!this.menu) return;
1241 this.unhighlight(); // in case highlighting was invoked externally
1243 var cell=Rico.eventElement(e);
1244 if (cell.className=='ricoLG_highlightDiv') {
1245 idx=this.highlightIdx;
1247 cell=Rico.getParentByTagName(cell,'div','ricoLG_cell');
1249 idx=this.winCellIndex(cell);
1250 if ((this.options.highlightSection & (idx.tabIdx+1))==0) return;
1252 this.highlight(idx);
1253 this.highlightEnabled=false;
1254 if (this.hideScroll) this.scrollDiv.style.overflow="hidden";
1256 if (!this.menu.div) this.menu.createDiv();
1257 this.menu.liveGrid=this;
1258 if (this.menu.buildGridMenu) {
1259 var showMenu=this.menu.buildGridMenu(idx.row, idx.column, idx.tabIdx);
1260 if (!showMenu) return;
1262 if (this.options.highlightElem=='selection' && !this.isSelected(idx.cell)) {
1263 this.selectCell(idx.cell);
1265 this.menu.showmenu(e,Rico.bind(this,'closeMenu'));
1268 closeMenu: function() {
1269 if (!this.menuIdx) return;
1270 if (this.hideScroll) this.scrollDiv.style.overflow="";
1271 //this.unhighlight();
1272 this.highlightEnabled=true;
1277 * @return index of cell within the window
1279 winCellIndex: function(cell) {
1280 var l=cell.id.lastIndexOf('_',cell.id.length);
1281 var l2=cell.id.lastIndexOf('_',l-1)+1;
1282 var c=parseInt(cell.id.substr(l+1));
1283 var r=parseInt(cell.id.substr(l2,l));
1284 return {row:r, column:c, tabIdx:this.columns[c].tabIdx, cell:cell};
1288 * @return index of cell within the dataset
1290 datasetIndex: function(cell) {
1291 var idx=this.winCellIndex(cell);
1292 idx.row+=this.buffer.windowPos;
1293 idx.onBlankRow=(idx.row >= this.buffer.endPos());
1297 attachHighlightEvents: function(tBody) {
1298 switch (this.options.highlightElem) {
1300 Rico.eventBind(tBody,"mousedown", this.options.mouseDownHandler, false);
1302 tBody.ondrag = function () { return false; };
1304 tBody.onselectstart = function () { return false; };
1308 Rico.eventBind(tBody,"mouseover", this.options.rowOverHandler, false);
1313 detachHighlightEvents: function(tBody) {
1314 switch (this.options.highlightElem) {
1316 Rico.eventUnbind(tBody,"mousedown", this.options.mouseDownHandler, false);
1317 tBody.ondrag = null;
1318 tBody.onselectstart = null;
1322 Rico.eventUnbind(tBody,"mouseover", this.options.rowOverHandler, false);
1328 * @return array of objects containing row/col indexes (index values are relative to the start of the window)
1330 getVisibleSelection: function() {
1332 if (this.SelectIdxStart && this.SelectIdxEnd) {
1333 var r1=Math.max(Math.min(this.SelectIdxEnd.row,this.SelectIdxStart.row)-this.buffer.startPos,this.buffer.windowStart);
1334 var r2=Math.min(Math.max(this.SelectIdxEnd.row,this.SelectIdxStart.row)-this.buffer.startPos,this.buffer.windowEnd-1);
1335 var c1=Math.min(this.SelectIdxEnd.column,this.SelectIdxStart.column);
1336 var c2=Math.max(this.SelectIdxEnd.column,this.SelectIdxStart.column);
1337 //Rico.log("getVisibleSelection "+r1+','+c1+' to '+r2+','+c2+' ('+this.SelectIdxStart.row+',startPos='+this.buffer.startPos+',windowPos='+this.buffer.windowPos+',windowEnd='+this.buffer.windowEnd+')');
1338 for (var r=r1; r<=r2; r++) {
1339 for (var c=c1; c<=c2; c++)
1340 cellList.push({row:r-this.buffer.windowStart,column:c});
1343 if (this.SelectCtrl) {
1344 for (var i=0; i<this.SelectCtrl.length; i++) {
1345 if (this.SelectCtrl[i].row>=this.buffer.windowStart && this.SelectCtrl[i].row<this.buffer.windowEnd)
1346 cellList.push({row:this.SelectCtrl[i].row-this.buffer.windowStart,column:this.SelectCtrl[i].column});
1352 updateSelectOutline: function() {
1353 if (!this.SelectIdxStart || !this.SelectIdxEnd) return;
1354 var r1=Math.max(Math.min(this.SelectIdxEnd.row,this.SelectIdxStart.row), this.buffer.windowStart);
1355 var r2=Math.min(Math.max(this.SelectIdxEnd.row,this.SelectIdxStart.row), this.buffer.windowEnd-1);
1357 this.HideSelection();
1360 var c1=Math.min(this.SelectIdxEnd.column,this.SelectIdxStart.column);
1361 var c2=Math.max(this.SelectIdxEnd.column,this.SelectIdxStart.column);
1362 var top1=this.columns[c1].cell(r1-this.buffer.windowStart).offsetTop;
1363 var cell2=this.columns[c1].cell(r2-this.buffer.windowStart);
1364 var bottom2=cell2.offsetTop+cell2.offsetHeight;
1365 var left1=this.columns[c1].dataCell.offsetLeft;
1366 var left2=this.columns[c2].dataCell.offsetLeft;
1367 var right2=left2+this.columns[c2].dataCell.offsetWidth;
1368 //window.status='updateSelectOutline: '+r1+' '+r2+' top='+top1+' bot='+bottom2;
1369 this.highlightDiv[0].style.top=this.highlightDiv[3].style.top=this.highlightDiv[1].style.top=(this.hdrHt+top1-1) + 'px';
1370 this.highlightDiv[2].style.top=(this.hdrHt+bottom2-1)+'px';
1371 this.highlightDiv[3].style.left=(left1-2)+'px';
1372 this.highlightDiv[0].style.left=this.highlightDiv[2].style.left=(left1-1)+'px';
1373 this.highlightDiv[1].style.left=(right2-1)+'px';
1374 this.highlightDiv[0].style.width=this.highlightDiv[2].style.width=(right2-left1-1) + 'px';
1375 this.highlightDiv[1].style.height=this.highlightDiv[3].style.height=(bottom2-top1) + 'px';
1376 //this.highlightDiv[0].style.right=this.highlightDiv[2].style.right=this.highlightDiv[1].style.right=()+'px';
1377 //this.highlightDiv[2].style.bottom=this.highlightDiv[3].style.bottom=this.highlightDiv[1].style.bottom=(this.hdrHt+bottom2) + 'px';
1378 for (var i=0; i<4; i++)
1379 this.highlightDiv[i].style.display='';
1382 HideSelection: function() {
1384 if (this.options.highlightMethod!='class') {
1385 for (i=0; i<this.highlightDiv.length; i++)
1386 this.highlightDiv[i].style.display='none';
1388 if (this.options.highlightMethod!='outline') {
1389 var cellList=this.getVisibleSelection();
1390 Rico.log("HideSelection "+cellList.length);
1391 for (i=0; i<cellList.length; i++)
1392 this.unhighlightCell(this.columns[cellList[i].column].cell(cellList[i].row));
1396 ShowSelection: function() {
1397 if (this.options.highlightMethod!='class')
1398 this.updateSelectOutline();
1399 if (this.options.highlightMethod!='outline') {
1400 var cellList=this.getVisibleSelection();
1401 for (var i=0; i<cellList.length; i++)
1402 this.highlightCell(this.columns[cellList[i].column].cell(cellList[i].row));
1406 ClearSelection: function() {
1407 Rico.log("ClearSelection");
1408 this.HideSelection();
1409 this.SelectIdxStart=null;
1410 this.SelectIdxEnd=null;
1414 selectCell: function(cell) {
1415 this.ClearSelection();
1416 this.SelectIdxStart=this.SelectIdxEnd=this.datasetIndex(cell);
1417 this.ShowSelection();
1420 AdjustSelection: function(cell) {
1421 var newIdx=this.datasetIndex(cell);
1422 if (this.SelectIdxStart.tabIdx != newIdx.tabIdx) return;
1423 this.HideSelection();
1424 this.SelectIdxEnd=newIdx;
1425 this.ShowSelection();
1428 RefreshSelection: function() {
1429 var cellList=this.getVisibleSelection();
1430 for (var i=0; i<cellList.length; i++) {
1431 this.columns[cellList[i].column].displayValue(cellList[i].row);
1435 FillSelection: function(newVal,newStyle) {
1436 if (this.SelectIdxStart && this.SelectIdxEnd) {
1437 var r1=Math.min(this.SelectIdxEnd.row,this.SelectIdxStart.row);
1438 var r2=Math.max(this.SelectIdxEnd.row,this.SelectIdxStart.row);
1439 var c1=Math.min(this.SelectIdxEnd.column,this.SelectIdxStart.column);
1440 var c2=Math.max(this.SelectIdxEnd.column,this.SelectIdxStart.column);
1441 for (var r=r1; r<=r2; r++) {
1442 for (var c=c1; c<=c2; c++) {
1443 this.buffer.setValue(r,c,newVal,newStyle);
1447 if (this.SelectCtrl) {
1448 for (var i=0; i<this.SelectCtrl.length; i++) {
1449 this.buffer.setValue(this.SelectCtrl[i].row,this.SelectCtrl[i].column,newVal,newStyle);
1452 this.RefreshSelection();
1456 * Process mouse down event
1457 * @param e event object
1459 selectMouseDown: function(e) {
1460 if (this.highlightEnabled==false) return true;
1462 var cell=Rico.eventElement(e);
1463 if (!Rico.eventLeftClick(e)) return true;
1464 cell=Rico.getParentByTagName(cell,'div','ricoLG_cell');
1465 if (!cell) return true;
1467 var newIdx=this.datasetIndex(cell);
1468 if (newIdx.onBlankRow) return true;
1469 Rico.log("selectMouseDown @"+newIdx.row+','+newIdx.column);
1471 if (!this.SelectIdxStart || this.options.highlightMethod!='class') return true;
1472 if (!this.isSelected(cell)) {
1473 this.highlightCell(cell);
1474 this.SelectCtrl.push(this.datasetIndex(cell));
1476 for (var i=0; i<this.SelectCtrl.length; i++) {
1477 if (this.SelectCtrl[i].row==newIdx.row && this.SelectCtrl[i].column==newIdx.column) {
1478 this.unhighlightCell(cell);
1479 this.SelectCtrl.splice(i,1);
1484 } else if (e.shiftKey) {
1485 if (!this.SelectIdxStart) return true;
1486 this.AdjustSelection(cell);
1488 this.selectCell(cell);
1489 this.pluginSelect();
1494 pluginSelect: function() {
1495 if (this.selectPluggedIn) return;
1496 var tBody=this.tbody[this.SelectIdxStart.tabIdx];
1497 Rico.eventBind(tBody,"mouseover", this.options.mouseOverHandler, false);
1498 Rico.eventBind(this.outerDiv,"mouseup", this.options.mouseUpHandler, false);
1499 this.selectPluggedIn=true;
1502 unplugSelect: function() {
1503 if (!this.selectPluggedIn) return;
1504 var tBody=this.tbody[this.SelectIdxStart.tabIdx];
1505 Rico.eventUnbind(tBody,"mouseover", this.options.mouseOverHandler , false);
1506 Rico.eventUnbind(this.outerDiv,"mouseup", this.options.mouseUpHandler , false);
1507 this.selectPluggedIn=false;
1510 selectMouseUp: function(e) {
1511 this.unplugSelect();
1512 var cell=Rico.eventElement(e);
1513 cell=Rico.getParentByTagName(cell,'div','ricoLG_cell');
1515 if (this.SelectIdxStart && this.SelectIdxEnd)
1516 this.AdjustSelection(cell);
1518 this.ClearSelection();
1521 selectMouseOver: function(e) {
1522 var cell=Rico.eventElement(e);
1523 cell=Rico.getParentByTagName(cell,'div','ricoLG_cell');
1525 this.AdjustSelection(cell);
1529 isSelected: function(cell) {
1530 if (this.options.highlightMethod!='outline') return Rico.hasClass(cell,this.options.highlightClass);
1531 if (!this.SelectIdxStart || !this.SelectIdxEnd) return false;
1532 var r1=Math.max(Math.min(this.SelectIdxEnd.row,this.SelectIdxStart.row), this.buffer.windowStart);
1533 var r2=Math.min(Math.max(this.SelectIdxEnd.row,this.SelectIdxStart.row), this.buffer.windowEnd-1);
1534 if (r1 > r2) return false;
1535 var c1=Math.min(this.SelectIdxEnd.column,this.SelectIdxStart.column);
1536 var c2=Math.max(this.SelectIdxEnd.column,this.SelectIdxStart.column);
1537 var curIdx=this.datasetIndex(cell);
1538 return (r1<=curIdx.row && curIdx.row<=r2 && c1<=curIdx.column && curIdx.column<=c2);
1541 highlightCell: function(cell) {
1542 Rico.addClass(cell,this.options.highlightClass);
1545 unhighlightCell: function(cell) {
1546 if (cell==null) return;
1547 Rico.removeClass(cell,this.options.highlightClass);
1550 selectRow: function(r) {
1551 for (var c=0; c<this.columns.length; c++)
1552 this.highlightCell(this.columns[c].cell(r));
1555 unselectRow: function(r) {
1556 for (var c=0; c<this.columns.length; c++)
1557 this.unhighlightCell(this.columns[c].cell(r));
1560 rowMouseOver: function(e) {
1561 if (!this.highlightEnabled) return;
1562 var cell=Rico.eventElement(e);
1563 cell=Rico.getParentByTagName(cell,'div','ricoLG_cell');
1565 var newIdx=this.winCellIndex(cell);
1566 if ((this.options.highlightSection & (newIdx.tabIdx+1))==0) return;
1567 this.highlight(newIdx);
1570 highlight: function(newIdx) {
1571 if (this.options.highlightMethod!='outline') this.cursorSetClass(newIdx);
1572 if (this.options.highlightMethod!='class') this.cursorOutline(newIdx);
1573 this.highlightIdx=newIdx;
1576 cursorSetClass: function(newIdx) {
1577 switch (this.options.highlightElem) {
1580 if (this.highlightIdx) this.unhighlightCell(this.highlightIdx.cell);
1581 this.highlightCell(newIdx.cell);
1585 if (this.highlightIdx) this.unselectRow(this.highlightIdx.row);
1586 var s1=this.options.highlightSection & 1;
1587 var s2=this.options.highlightSection & 2;
1588 var c0=s1 ? 0 : this.options.frozenColumns;
1589 var c1=s2 ? this.columns.length : this.options.frozenColumns;
1590 for (var c=c0; c<c1; c++)
1591 this.highlightCell(this.columns[c].cell(newIdx.row));
1597 cursorOutline: function(newIdx) {
1599 switch (this.options.highlightElem) {
1602 div=this.highlightDiv[newIdx.tabIdx];
1603 div.style.left=(this.columns[newIdx.column].dataCell.offsetLeft-1)+'px';
1604 div.style.width=this.columns[newIdx.column].colWidth;
1605 this.highlightDiv[1-newIdx.tabIdx].style.display='none';
1609 div=this.highlightDiv[0];
1610 var s1=this.options.highlightSection & 1;
1611 var s2=this.options.highlightSection & 2;
1612 div.style.left=s1 ? '0px' : this.frozenTabs.style.width;
1613 div.style.width=((s1 ? this.frozenTabs.offsetWidth : 0) + (s2 ? this.innerDiv.offsetWidth : 0) - 4)+'px';
1617 div.style.top=(this.hdrHt+newIdx.row*this.rowHeight-1)+'px';
1618 div.style.height=(this.rowHeight-1)+'px';
1619 div.style.display='';
1622 unhighlight: function() {
1623 switch (this.options.highlightElem) {
1625 //this.highlightIdx=this.menuIdx;
1628 if (this.highlightIdx) this.unhighlightCell(this.highlightIdx.cell);
1629 if (!this.highlightDiv) return;
1630 for (var i=0; i<2; i++)
1631 this.highlightDiv[i].style.display='none';
1634 //this.highlightIdx=this.menuIdx;
1637 if (this.highlightIdx) this.unselectRow(this.highlightIdx.row);
1638 if (this.highlightDiv) this.highlightDiv[0].style.display='none';
1643 resetContents: function() {
1644 Rico.log("resetContents");
1645 this.ClearSelection();
1646 this.buffer.clear();
1648 this.clearBookmark();
1651 setImages: function() {
1652 for (var n=0; n<this.columns.length; n++)
1653 this.columns[n].setImage();
1657 * @return column index, or -1 if there are no sorted columns
1659 findSortedColumn: function() {
1660 for (var n=0; n<this.columns.length; n++) {
1661 if (this.columns[n].isSorted()) return n;
1666 findColumnName: function(name) {
1667 for (var n=0; n<this.columns.length; n++) {
1668 if (this.columns[n].fieldName == name) return n;
1674 * Searches options.columnSpecs colAttr for matching colValue
1675 * @return array of matching column indexes
1677 findColumnsBySpec: function(colAttr, colValue) {
1679 for (var n=0; n<this.options.columnSpecs.length; n++) {
1680 if (this.options.columnSpecs[n][colAttr] == colValue) result.push(n);
1688 setSortUI: function( columnNameOrNum, sortDirection ) {
1689 Rico.log("setSortUI: "+columnNameOrNum+' '+sortDirection);
1690 var colnum=this.findSortedColumn();
1692 sortDirection=this.columns[colnum].getSortDirection();
1694 if (typeof sortDirection!='string') {
1695 sortDirection=Rico.ColumnConst.SORT_ASC;
1697 sortDirection=sortDirection.toUpperCase();
1698 if (sortDirection != Rico.ColumnConst.SORT_DESC) sortDirection=Rico.ColumnConst.SORT_ASC;
1700 switch (typeof columnNameOrNum) {
1702 colnum=this.findColumnName(columnNameOrNum);
1705 colnum=columnNameOrNum;
1709 if (typeof(colnum)!='number' || colnum < 0) return;
1711 this.columns[colnum].setSorted(sortDirection);
1712 this.buffer.sortBuffer(colnum);
1716 * clear sort flag on all columns
1718 clearSort: function() {
1719 for (var x=0;x<this.columns.length;x++)
1720 this.columns[x].setUnsorted();
1724 * clear filters on all columns
1726 clearFilters: function() {
1727 for (var x=0;x<this.columns.length;x++) {
1728 this.columns[x].setUnfiltered(true);
1730 if (this.options.filterHandler) {
1731 this.options.filterHandler();
1736 * returns number of columns with a user filter set
1738 filterCount: function() {
1739 for (var x=0,cnt=0;x<this.columns.length;x++) {
1740 if (this.columns[x].isFiltered()) cnt++;
1745 sortHandler: function() {
1747 this.ClearSelection();
1749 var n=this.findSortedColumn();
1751 Rico.log("sortHandler: sorting column "+n);
1752 this.buffer.sortBuffer(n);
1754 this.scrollDiv.scrollTop = 0;
1755 this.buffer.fetch(0);
1758 filterHandler: function() {
1759 Rico.log("filterHandler");
1761 if (this.buffer.processingRequest) {
1762 this.queueFilter=true;
1765 this.unplugScroll();
1766 this.ClearSelection();
1768 this.clearBookmark();
1770 this.buffer.fetch(-1);
1771 Rico.runLater(10,this,'pluginScroll'); // resetting ht div can cause a scroll event, triggering an extra fetch
1774 clearBookmark: function() {
1775 if (this.bookmark) this.bookmark.innerHTML=" ";
1778 bookmarkHandler: function(firstrow,lastrow) {
1780 if (isNaN(firstrow) || !this.bookmark) return;
1781 var totrows=this.buffer.totalRows;
1782 if (totrows < lastrow) lastrow=totrows;
1784 newhtml = Rico.getPhraseById('bookmarkNoMatch');
1785 } else if (lastrow<0) {
1786 newhtml = Rico.getPhraseById('bookmarkNoRec');
1787 } else if (this.buffer.foundRowCount) {
1788 newhtml = Rico.getPhraseById('bookmarkExact',firstrow,lastrow,totrows);
1790 newhtml = Rico.getPhraseById('bookmarkAbout',firstrow,lastrow,totrows);
1792 this.bookmark.innerHTML = newhtml;
1795 clearRows: function() {
1796 if (this.isBlank==true) return;
1797 for (var c=0; c < this.columns.length; c++)
1798 this.columns[c].clearColumn();
1799 this.isBlank = true;
1802 refreshContents: function(startPos) {
1803 Rico.log("refreshContents1 "+this.tableId+": startPos="+startPos+" lastRow="+this.lastRowPos+" PartBlank="+this.isPartialBlank+" pageSize="+this.pageSize);
1806 this.unhighlight(); // in case highlighting was manually invoked
1807 if (this.queueFilter) {
1808 Rico.log("refreshContents: cancelling refresh because filter has changed");
1809 this.queueFilter=false;
1810 this.filterHandler();
1813 this.highlightEnabled=this.options.highlightSection!='none';
1814 var viewPrecedesBuffer = this.buffer.startPos > startPos;
1815 var contentStartPos = viewPrecedesBuffer ? this.buffer.startPos: startPos;
1816 this.contentStartPos = contentStartPos+1;
1817 var contentEndPos = Math.min(this.buffer.startPos + this.buffer.size, startPos + this.pageSize);
1818 this.buffer.setWindow(contentStartPos, contentEndPos);
1819 Rico.log('refreshContents2 '+this.tableId+': cStartPos='+contentStartPos+' cEndPos='+contentEndPos+' vPrecedesBuf='+viewPrecedesBuffer+' b.startPos='+this.buffer.startPos);
1820 if (startPos == this.lastRowPos && !this.isPartialBlank && !this.isBlank) return;
1821 this.isBlank = false;
1822 var onRefreshComplete = this.options.onRefreshComplete;
1824 if ((startPos + this.pageSize < this.buffer.startPos) ||
1825 (this.buffer.startPos + this.buffer.size < startPos) ||
1826 (this.buffer.size == 0)) {
1828 if (onRefreshComplete) onRefreshComplete(this.contentStartPos,contentEndPos); // update bookmark
1832 Rico.log('refreshContents: contentStartPos='+contentStartPos+' contentEndPos='+contentEndPos+' viewPrecedesBuffer='+viewPrecedesBuffer);
1833 var rowSize = contentEndPos - contentStartPos;
1834 var blankSize = this.pageSize - rowSize;
1835 var blankOffset = viewPrecedesBuffer ? 0: rowSize;
1836 var contentOffset = viewPrecedesBuffer ? blankSize: 0;
1838 for (var r=0; r < rowSize; r++) { //initialize what we have
1839 for (var c=0; c < this.columns.length; c++)
1840 this.columns[c].displayValue(r + contentOffset);
1842 for (var i=0; i < blankSize; i++) // blank out the rest
1843 this.blankRow(i + blankOffset);
1844 if (this.options.highlightElem=='selection') this.ShowSelection();
1845 this.isPartialBlank = blankSize > 0;
1846 this.lastRowPos = startPos;
1847 Rico.log("refreshContents complete, startPos="+startPos);
1848 if (onRefreshComplete) onRefreshComplete(this.contentStartPos,contentEndPos); // update bookmark
1851 scrollToRow: function(rowOffset) {
1852 var p=this.rowToPixel(rowOffset);
1853 Rico.log("scrollToRow, rowOffset="+rowOffset+" pixel="+p);
1854 this.scrollDiv.scrollTop = p; // this causes a scroll event
1855 if ( this.options.onscroll )
1856 this.options.onscroll( this, rowOffset );
1859 scrollUp: function() {
1860 this.moveRelative(-1);
1863 scrollDown: function() {
1864 this.moveRelative(1);
1867 pageUp: function() {
1868 this.moveRelative(-this.pageSize);
1871 pageDown: function() {
1872 this.moveRelative(this.pageSize);
1875 adjustRow: function(rowOffset) {
1876 var notdisp=this.topOfLastPage();
1877 if (notdisp == 0 || !rowOffset) return 0;
1878 return Math.min(notdisp,rowOffset);
1881 rowToPixel: function(rowOffset) {
1882 return this.adjustRow(rowOffset) * this.rowHeight;
1886 * @returns row to display at top of scroll div
1888 pixeltorow: function(p) {
1889 var notdisp=this.topOfLastPage();
1890 if (notdisp == 0) return 0;
1891 var prow=parseInt(p/this.rowHeight,10);
1892 return Math.min(notdisp,prow);
1895 moveRelative: function(relOffset) {
1896 var newoffset=Math.max(this.scrollDiv.scrollTop+relOffset*this.rowHeight,0);
1897 newoffset=Math.min(newoffset,this.scrollDiv.scrollHeight);
1898 //Rico.log("moveRelative, newoffset="+newoffset);
1899 this.scrollDiv.scrollTop=newoffset;
1902 pluginScroll: function() {
1903 if (this.scrollPluggedIn) return;
1904 Rico.log("pluginScroll: wheelEvent="+this.wheelEvent);
1905 Rico.eventBind(this.scrollDiv,"scroll",this.scrollEventFunc, false);
1906 for (var t=0; t<2; t++)
1907 Rico.eventBind(this.tabs[t],this.wheelEvent,this.wheelEventFunc, false);
1908 this.scrollPluggedIn=true;
1911 unplugScroll: function() {
1912 if (!this.scrollPluggedIn) return;
1913 Rico.log("unplugScroll");
1914 Rico.eventUnbind(this.scrollDiv,"scroll", this.scrollEventFunc , false);
1915 for (var t=0; t<2; t++)
1916 Rico.eventUnbind(this.tabs[t],this.wheelEvent,this.wheelEventFunc, false);
1917 this.scrollPluggedIn=false;
1920 handleWheel: function(e) {
1924 delta = e.wheelDelta/120;
1925 else if (Rico.isWebKit)
1926 delta = -e.wheelDelta/12;
1928 delta = -e.wheelDelta/120;
1929 } else if (e.detail) {
1930 delta = e.detail/3; /* Mozilla/Gecko */
1932 if (delta) this.moveRelative(delta);
1937 handleScroll: function(e) {
1938 if ( this.scrollTimeout )
1939 clearTimeout( this.scrollTimeout );
1940 this.setHorizontalScroll();
1941 var scrtop=this.scrollDiv.scrollTop;
1942 var vscrollDiff = this.lastScrollPos-scrtop;
1943 if (vscrollDiff == 0.00) return;
1944 var newrow=this.pixeltorow(scrtop);
1945 if (newrow == this.lastRowPos && !this.isPartialBlank && !this.isBlank) return;
1946 var stamp1 = new Date();
1947 Rico.log("handleScroll, newrow="+newrow+" scrtop="+scrtop);
1948 if (this.options.highlightElem=='selection') this.HideSelection();
1949 this.buffer.fetch(newrow);
1950 if (this.options.onscroll) this.options.onscroll(this, newrow);
1951 this.scrollTimeout = Rico.runLater(1200,this,'scrollIdle');
1952 this.lastScrollPos = this.scrollDiv.scrollTop;
1953 var stamp2 = new Date();
1954 //Rico.log("handleScroll, time="+(stamp2.getTime()-stamp1.getTime()));
1957 scrollIdle: function() {
1958 if ( this.options.onscrollidle )
1959 this.options.onscrollidle();
1965 Rico.LiveGridColumn = function(grid,colIdx,hdrInfo,tabIdx) {
1966 this.initialize(grid,colIdx,hdrInfo,tabIdx);
1969 Rico.LiveGridColumn.prototype =
1970 /** @lends Rico.LiveGridColumn# */
1973 * Implements a LiveGrid column. Also contains static properties used by SimpleGrid columns.
1974 * @extends Rico.TableColumnBase
1977 initialize: function(liveGrid,colIdx,hdrInfo,tabIdx) {
1978 Rico.extend(this, new Rico.TableColumnBase());
1979 this.baseInit(liveGrid,colIdx,hdrInfo,tabIdx);
1980 this.buffer=liveGrid.buffer;
1981 if (typeof(this.format.type)!='string' || this.format.EntryType=='tinyMCE') this.format.type='raw';
1982 if (typeof this.isNullable!='boolean') this.isNullable = /number|date/.test(this.format.type);
1983 this.isText = /raw|text|showTags/.test(this.format.type);
1984 Rico.log(" sortable="+this.sortable+" filterable="+this.filterable+" hideable="+this.hideable+" isNullable="+this.isNullable+' isText='+this.isText);
1985 this.fixHeaders(this.liveGrid.tableId, this.options.hdrIconsFirst);
1986 if (this['format_'+this.format.type]) {
1987 this._format=this['format_'+this.format.type];
1988 //this._format=Rico.bind(this,'format_'+this.format.type);
1990 if (this.format.control) {
1991 // copy all properties/methods that start with '_'
1992 if (typeof this.format.control=='string') {
1993 this.format.control=eval(this.format.control);
1995 for (var property in this.format.control) {
1996 if (property.charAt(0)=='_') {
1997 Rico.log("Copying control property "+property+ ' to ' + this);
1998 this[property] = this.format.control[property];
2005 * Sorts the column in ascending order
2007 sortAsc: function() {
2008 this.setColumnSort(Rico.ColumnConst.SORT_ASC);
2012 * Sorts the column in descending order
2014 sortDesc: function() {
2015 this.setColumnSort(Rico.ColumnConst.SORT_DESC);
2019 * Sorts the column in the specified direction
2020 * @param direction must be one of Rico.ColumnConst.UNSORTED, .SORT_ASC, or .SORT_DESC
2022 setColumnSort: function(direction) {
2023 this.liveGrid.clearSort();
2024 this.setSorted(direction);
2025 if (this.liveGrid.options.saveColumnInfo.sort)
2026 this.liveGrid.setCookie();
2027 if (this.options.sortHandler)
2028 this.options.sortHandler();
2032 * @returns true if this column is allowed to be sorted
2034 isSortable: function() {
2035 return this.sortable;
2039 * @returns true if this column is currently sorted
2041 isSorted: function() {
2042 return this.currentSort != Rico.ColumnConst.UNSORTED;
2046 * @returns Rico.ColumnConst.UNSORTED, .SORT_ASC, or .SORT_DESC
2048 getSortDirection: function() {
2049 return this.currentSort;
2053 * toggle the sort sequence for this column
2055 toggleSort: function() {
2056 if (this.buffer && this.buffer.totalRows==0) return;
2057 if (this.currentSort == Rico.ColumnConst.SORT_ASC)
2064 * Flags that this column is not sorted
2066 setUnsorted: function() {
2067 this.setSorted(Rico.ColumnConst.UNSORTED);
2071 * Flags that this column is sorted, but doesn't actually carry out the sort
2072 * @param direction must be one of Rico.ColumnConst.UNSORTED, .SORT_ASC, or .SORT_DESC
2074 setSorted: function(direction) {
2075 this.currentSort = direction;
2079 * @returns true if this column is allowed to be filtered
2081 canFilter: function() {
2082 return this.filterable;
2086 * @returns a textual representation of how this column is filtered
2088 getFilterText: function() {
2090 for (var i=0; i<this.filterValues.length; i++) {
2091 var v=this.filterValues[i];
2092 vals.push(v=='' ? Rico.getPhraseById('filterBlank') : v);
2094 switch (this.filterOp) {
2095 case 'EQ': return '= '+vals.join(', ');
2096 case 'NE': return Rico.getPhraseById('filterNot',vals.join(', '));
2097 case 'LE': return '<= '+vals[0];
2098 case 'GE': return '>= '+vals[0];
2099 case 'LIKE': return Rico.getPhraseById('filterLike',vals[0]);
2100 case 'NULL': return Rico.getPhraseById('filterEmpty');
2101 case 'NOTNULL': return Rico.getPhraseById('filterNotEmpty');
2107 * @returns returns the query string representation of the filter
2109 getFilterQueryParm: function() {
2110 if (this.filterType == Rico.ColumnConst.UNFILTERED) return '';
2111 var retval='&f['+this.index+'][op]='+this.filterOp;
2112 retval+='&f['+this.index+'][len]='+this.filterValues.length;
2113 for (var i=0; i<this.filterValues.length; i++) {
2114 retval+='&f['+this.index+']['+i+']='+escape(this.filterValues[i]);
2120 * removes the filter from this column
2122 setUnfiltered: function(skipHandler) {
2123 this.filterType = Rico.ColumnConst.UNFILTERED;
2124 if (this.liveGrid.options.saveColumnInfo.filter)
2125 this.liveGrid.setCookie();
2126 if (this.removeFilterFunc)
2127 this.removeFilterFunc();
2128 if (this.options.filterHandler && !skipHandler)
2129 this.options.filterHandler();
2132 setFilterEQ: function() {
2133 this.setUserFilter('EQ');
2135 setFilterNE: function() {
2136 this.setUserFilter('NE');
2138 addFilterNE: function() {
2139 this.filterValues.push(this.userFilter);
2140 if (this.liveGrid.options.saveColumnInfo.filter)
2141 this.liveGrid.setCookie();
2142 if (this.options.filterHandler)
2143 this.options.filterHandler();
2145 setFilterGE: function() { this.setUserFilter('GE'); },
2146 setFilterLE: function() { this.setUserFilter('LE'); },
2147 setFilterKW: function(keyword) {
2148 if (keyword!='' && keyword!=null) {
2149 this.setFilter('LIKE',keyword,Rico.ColumnConst.USERFILTER);
2151 this.setUnfiltered(false);
2155 setUserFilter: function(relop) {
2156 this.setFilter(relop,this.userFilter,Rico.ColumnConst.USERFILTER);
2159 setSystemFilter: function(relop,filter) {
2160 this.setFilter(relop,filter,Rico.ColumnConst.SYSTEMFILTER);
2163 setFilter: function(relop,filter,type,removeFilterFunc) {
2164 this.filterValues = typeof(filter)=='object' ? filter : [filter];
2165 this.filterType = type;
2166 this.filterOp = relop;
2167 if (type == Rico.ColumnConst.USERFILTER && this.liveGrid.options.saveColumnInfo.filter)
2168 this.liveGrid.setCookie();
2169 this.removeFilterFunc=removeFilterFunc;
2170 if (this.options.filterHandler)
2171 this.options.filterHandler();
2174 isFiltered: function() {
2175 return this.filterType == Rico.ColumnConst.USERFILTER;
2178 filterChange: function(e) {
\r
2179 var selbox=Rico.eventElement(e);
2180 if (selbox.value==this.liveGrid.options.FilterAllToken)
\r
2181 this.setUnfiltered();
\r
2183 this.setFilter('EQ',selbox.value,Rico.ColumnConst.USERFILTER,function() {selbox.selectedIndex=0;});
\r
2186 filterClear: function(e) {
\r
2187 this.filterField.value='';
2188 this.setUnfiltered();
\r
2191 filterKeypress: function(e) {
\r
2192 var txtbox=Rico.eventElement(e);
2193 if (typeof this.lastKeyFilter != 'string') this.lastKeyFilter='';
\r
2194 if (this.lastKeyFilter==txtbox.value) return;
\r
2195 var v=txtbox.value;
\r
2196 Rico.log("filterKeypress: "+this.index+' '+v);
\r
2197 this.lastKeyFilter=v;
2198 if (v=='' || v=='*')
\r
2199 this.setUnfiltered();
\r
2201 this.setFilter('LIKE', v, Rico.ColumnConst.USERFILTER, function() {txtbox.value='';});
2205 mFilterSelectClick: function(e) {
2207 if (this.mFilter.style.display!='none') {
2208 this.mFilterFinish(e);
2209 if (Rico.isIE && Rico.ieVersion <= 6) {
2210 this.filterField.focus();
2212 this.filterField.blur();
2215 var offset=Rico.cumulativeOffset(this.filterField);
2216 this.mFilter.style.top=(offset.top+this.filterField.offsetHeight)+'px';
2217 this.mFilter.style.left=offset.left+'px';
2218 this.mFilter.style.width=Math.min(this.filterField.offsetWidth,parseInt(this.colWidth,10))+'px';
2219 Rico.show(this.mFilter);
2220 this.mFilterFocus.focus();
2224 mFilterFinish: function(e) {
2225 if (!this.mFilterChange) {
2226 Rico.hide(this.mFilter);
2229 if (this.mFilterInputs[0].checked) {
2230 this.mFilterReset();
2231 Rico.hide(this.mFilter);
2232 this.setUnfiltered();
2237 for (var i=1; i<this.mFilterInputs.length; i++) {
2238 if (this.mFilterInputs[i].checked) {
2239 newValues.push(this.mFilterInputs[i].value)
2240 newLabels.push(this.mFilterLabels[i].innerHTML)
2243 if (newValues.length > 0) {
2244 var newText=newLabels.join(', ');
2245 this.filterField.options[0].text=newText;
2246 this.filterField.title=newText;
2247 Rico.hide(this.mFilter);
2248 this.mFilterChange=false;
2249 this.setFilter('EQ',newValues,Rico.ColumnConst.USERFILTER,Rico.bind(this,'mFilterReset'));
2251 alert('Please select at least one value');
2255 mFilterReset: function() {
2256 var newText=this.mFilterLabels[0].innerHTML; // all
2257 this.filterField.options[0].text=newText;
2258 this.filterField.title=newText;
2261 mFilterAllClick: function(e) {
2262 var allChecked=this.mFilterInputs[0].checked;
2263 for (var i=1; i<this.mFilterInputs.length; i++) {
2264 this.mFilterInputs[i].checked=allChecked;
2266 this.mFilterChange=true;
2269 mFilterOtherClick: function(e) {
2270 this.mFilterInputs[0].checked=false;
2271 this.mFilterChange=true;
2274 format_text: function(v) {
2275 if (typeof v!='string')
2278 return Rico.stripTags(v);
2281 format_showTags: function(v) {
2282 if (typeof v!='string')
2285 return v.replace(/&/g, '&').replace(/</g,'<').replace(/>/g,'>');
2288 format_number: function(v) {
2289 if (typeof v=='undefined' || v=='' || v==null)
2292 return Rico.formatNumber(v,this.format);
2295 format_datetime: function(v) {
2296 if (typeof v=='undefined' || v=='' || v==null)
2299 var d=Rico.setISO8601(v);
2301 return (this.format.prefix || '')+Rico.formatDate(d,this.format.dateFmt || 'translateDateTime')+(this.format.suffix || '');
2305 // converts GMT/UTC to local time
2306 format_UTCasLocalTime: function(v) {
2307 if (typeof v=='undefined' || v=='' || v==null)
2311 var d=Rico.setISO8601(v,-tz.getTimezoneOffset());
2313 return (this.format.prefix || '')+Rico.formatDate(d,this.format.dateFmt || 'translateDateTime')+(this.format.suffix || '');
2317 format_date: function(v) {
2318 if (typeof v=='undefined' || v==null || v=='')
2321 var d=Rico.setISO8601(v);
2323 return (this.format.prefix || '')+Rico.formatDate(d,this.format.dateFmt || 'translateDate')+(this.format.suffix || '');
2327 fixHeaders: function(prefix, iconsfirst) {
2328 if (this.sortable) {
2329 var handler=Rico.eventHandle(this,'toggleSort');
2330 switch (this.options.headingSort) {
2332 var a=Rico.wrapChildren(this.hdrCellDiv,'ricoSort',undefined,'a');
2333 a.href = "javascript:void(0)";
2334 Rico.eventBind(a,"click", handler);
2337 Rico.eventBind(this.hdrCellDiv,"click", handler);
2341 this.imgFilter = document.createElement('img');
2342 this.imgFilter.style.display='none';
2343 this.imgFilter.src=Rico.imgDir+this.options.filterImg;
2344 this.imgFilter.className='ricoLG_HdrIcon';
2345 this.imgSort = document.createElement('span');
2346 this.imgSort.style.display='none';
2348 this.hdrCellDiv.insertBefore(this.imgSort,this.hdrCellDiv.firstChild);
2349 this.hdrCellDiv.insertBefore(this.imgFilter,this.hdrCellDiv.firstChild);
2351 this.hdrCellDiv.appendChild(this.imgFilter);
2352 this.hdrCellDiv.appendChild(this.imgSort);
2354 if (!this.format.filterUI) {
2355 Rico.eventBind(this.imgFilter, 'click', Rico.eventHandle(this,'filterClick'), false);
2359 filterClick: function(e) {
2360 if (this.filterType==Rico.ColumnConst.USERFILTER && this.filterOp=='LIKE') {
2361 this.liveGrid.openKeyword(this.index);
2365 getValue: function(windowRow) {
2366 return this.buffer.getWindowCell(windowRow,this.index);
2369 getBufferAttr: function(windowRow) {
2370 return this.buffer.getWindowAttr(windowRow,this.index);
2373 setValue: function(windowRow,newval) {
2374 this.buffer.setWindowValue(windowRow,this.index,newval);
2377 _format: function(v) {
2381 _display: function(v,gridCell) {
2382 gridCell.innerHTML=this._format(v);
2385 _export: function(v) {
2386 return this._format(v);
2389 displayValue: function(windowRow) {
2390 var bufval=this.getValue(windowRow);
2392 this.clearCell(windowRow);
2395 var gridCell=this.cell(windowRow);
2396 this._display(bufval,gridCell,windowRow);
2397 var acceptAttr=this.buffer.options.acceptAttr;
2398 if (acceptAttr.length==0) return;
2399 var bufAttr=this.getBufferAttr(windowRow);
2400 if (bufAttr==null) return;
2401 for (var k=0; k<acceptAttr.length; k++) {
2402 bufAttr=bufAttr['_'+acceptAttr[k]] || '';
2403 switch (acceptAttr[k]) {
2404 case 'style': gridCell.style.cssText=bufAttr; break;
2405 case 'class': gridCell.className=bufAttr; break;
2406 default: gridCell['_'+acceptAttr[k]]=bufAttr; break;
2413 Rico.includeLoaded('ricoLiveGrid.js');