2 * (c) 2005-2009 Richard Cowin (http://openrico.org)
3 * (c) 2005-2009 Matt Brown (http://dowdybrown.com)
5 * Rico is licensed under the Apache License, Version 2.0 (the "License"); you may not use this
6 * file except in compliance with the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software distributed under the
11 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
12 * either express or implied. See the License for the specific language governing permissions
13 * and limitations under the License.
16 if(typeof Rico=='undefined') throw("LiveGrid requires the Rico JavaScript framework");
17 if(typeof RicoUtil=='undefined') throw("LiveGrid requires the RicoUtil Library");
18 if(typeof RicoTranslate=='undefined') throw("LiveGrid requires the RicoTranslate Library");
19 if(typeof Rico.TableColumn=='undefined') throw("LiveGrid requires ricoGridCommon.js");
25 Rico.Buffer.Base = Class.create(
26 /** @lends Rico.Buffer.Base# */
29 * @class Defines the static buffer class (no AJAX).
30 * Loads buffer with data that already exists in the document as an HTML table or passed via javascript.
31 * Also serves as a base class for AJAX-enabled buffers.
34 initialize: function(dataTable, options) {
36 this.updateInProgress = false;
38 this.rcvdRowCount = false; // true if an eof element was included in the last xml response
39 this.foundRowCount = false; // true if an xml response is ever received with eof true
41 this.rowcntContent = "";
45 canFilter : true, // does buffer object support filtering?
46 isEncoded : true, // is the data received via ajax html encoded?
47 acceptAttr : [] // attributes that can be copied from original/ajax data (e.g. className, style, id)
49 Object.extend(this.options, options || {});
51 this.loadRowsFromTable(dataTable,this.options.fixedHdrRows);
57 registerGrid: function(liveGrid) {
58 this.liveGrid = liveGrid;
61 setTotalRows: function( newTotalRows ) {
62 if (typeof(newTotalRows)!='number') newTotalRows=this.size;
63 if (this.totalRows == newTotalRows) return;
64 this.totalRows = newTotalRows;
66 Rico.writeDebugMsg("setTotalRows, newTotalRows="+newTotalRows);
67 if (this.liveGrid.sizeTo=='data') this.liveGrid.resizeWindow();
68 this.liveGrid.updateHeightDiv();
72 loadRowsFromTable: function(tableElement,firstRow) {
73 var newRows = new Array();
74 var trs = tableElement.getElementsByTagName("tr");
75 for ( var i=firstRow || 0; i < trs.length; i++ ) {
76 var row = new Array();
77 var cells = trs[i].getElementsByTagName("td");
78 for ( var j=0; j < cells.length ; j++ )
79 row[j]=cells[j].innerHTML;
82 this.loadRows(newRows);
85 loadRowsFromArray: function(array2D) {
86 for ( var i=0; i < array2D.length; i++ ) {
87 for ( var j=0; j < array2D[i].length ; j++ ) {
88 array2D[i][j]=array2D[i][j].toString();
91 this.loadRows(array2D);
94 loadRows: function(jstable) {
95 this.baseRows = jstable;
97 this.size = this.baseRows.length;
100 dom2jstable: function(rowsElement) {
101 Rico.writeDebugMsg('dom2jstable: encoded='+this.options.isEncoded);
102 var newRows = new Array();
103 var trs = rowsElement.getElementsByTagName("tr");
104 for ( var i=0; i < trs.length; i++ ) {
105 var row = new Array();
106 var cells = trs[i].getElementsByTagName("td");
107 for ( var j=0; j < cells.length ; j++ )
108 row[j]=RicoUtil.getContentAsString(cells[j],this.options.isEncoded);
114 dom2jstableAttr: function(rowsElement,firstRow) {
115 var acceptAttr=this.options.acceptAttr;
116 Rico.writeDebugMsg("dom2jstableAttr start, # attr="+acceptAttr.length);
117 var newRows = new Array();
118 var trs = rowsElement.getElementsByTagName("tr");
119 for ( var i=firstRow || 0; i < trs.length; i++ ) {
120 var row = new Array();
121 var cells = trs[i].getElementsByTagName("td");
122 for ( var j=0; j < cells.length ; j++ ) {
124 for (var k=0; k<acceptAttr.length; k++)
125 row[j]['_'+acceptAttr[k]]=cells[j].getAttribute(acceptAttr[k]);
126 if (Prototype.Browser.IE) row[j]._class=cells[j].getAttribute('className');
130 Rico.writeDebugMsg("dom2jstableAttr end");
134 _blankRow: function() {
136 for (var i=0; i<this.liveGrid.columns.length; i++) {
142 deleteRows: function(rowIndex,cnt) {
143 this.baseRows.splice(rowIndex,typeof(cnt)=='number' ? cnt : 1);
144 this.liveGrid.isPartialBlank=true;
145 this.size=this.baseRows.length;
148 insertRow: function(beforeRowIndex) {
149 var r=this._blankRow();
150 this.baseRows.splice(beforeRowIndex,0,r);
151 this.size=this.baseRows.length;
152 this.liveGrid.isPartialBlank=true;
153 if (this.startPos < 0) this.startPos=0;
157 appendRows: function(cnt) {
159 for (var i=0; i<cnt; i++) {
160 var r=this._blankRow();
161 this.baseRows.push(r);
164 this.size=this.baseRows.length;
165 this.liveGrid.isPartialBlank=true;
166 if (this.startPos < 0) this.startPos=0;
170 sortFunc: function(coltype) {
172 case 'number': return this._sortNumeric.bind(this);
173 case 'control':return this._sortControl.bind(this);
174 default: return this._sortAlpha.bind(this);
178 sortBuffer: function(colnum) {
179 if (!this.baseRows) {
180 this.delayedSortCol=colnum;
183 this.liveGrid.showMsg(RicoTranslate.getPhraseById("sorting"));
184 this.sortColumn=colnum;
185 var col=this.liveGrid.columns[colnum];
186 this.getValFunc=col._sortfunc;
187 this.baseRows.sort(this.sortFunc(col.format.type));
188 if (col.getSortDirection()=='DESC') this.baseRows.reverse();
191 _sortAlpha: function(a,b) {
192 var aa = this.sortColumn<a.length ? RicoUtil.getInnerText(a[this.sortColumn]) : '';
193 var bb = this.sortColumn<b.length ? RicoUtil.getInnerText(b[this.sortColumn]) : '';
194 if (aa==bb) return 0;
195 if (aa<bb) return -1;
199 _sortNumeric: function(a,b) {
200 var aa = this.sortColumn<a.length ? this.nan2zero(RicoUtil.getInnerText(a[this.sortColumn])) : 0;
201 var bb = this.sortColumn<b.length ? this.nan2zero(RicoUtil.getInnerText(b[this.sortColumn])) : 0;
205 nan2zero: function(n) {
206 if (typeof(n)=='string') n=parseFloat(n);
207 return isNaN(n) || typeof(n)=='undefined' ? 0 : n;
210 _sortControl: function(a,b) {
211 var aa = this.sortColumn<a.length ? RicoUtil.getInnerText(a[this.sortColumn]) : '';
212 var bb = this.sortColumn<b.length ? RicoUtil.getInnerText(b[this.sortColumn]) : '';
213 if (this.getValFunc) {
214 aa=this.getValFunc(aa);
215 bb=this.getValFunc(bb);
217 if (aa==bb) return 0;
218 if (aa<bb) return -1;
230 isInRange: function(position) {
231 var lastRow=Math.min(this.totalRows, position + this.liveGrid.pageSize);
232 return (position >= this.startPos) && (lastRow <= this.endPos()); // && (this.size != 0);
236 return this.startPos + this.rows.length;
239 fetch: function(offset) {
240 Rico.writeDebugMsg('fetch '+this.liveGrid.tableId+': offset='+offset);
243 this.rcvdRowCount = true;
244 this.foundRowCount = true;
245 if (offset < 0) offset=0;
246 this.liveGrid.refreshContents(offset);
250 exportAllRows: function(populate,finish) {
251 populate(this.getRows(0,this.totalRows));
256 * @return a 2D array of buffer data representing the rows that are currently visible on the grid
258 visibleRows: function() {
259 return this.rows.slice(this.windowStart,this.windowEnd);
262 setWindow: function(start, count) {
263 this.windowStart = start - this.startPos; // position in the buffer of first visible row
264 this.windowEnd = Math.min(this.windowStart + count,this.size); // position in the buffer of last visible row containing data+1
265 this.windowPos = start; // position in the dataset of first visible row
269 * @return true if bufRow is currently visible in the grid
271 isVisible: function(bufRow) {
272 return bufRow < this.rows.length && bufRow >= this.windowStart && bufRow < this.windowEnd;
276 * takes a window row index and returns the corresponding buffer row index
278 bufferRow: function(windowRow) {
279 return this.windowStart+windowRow;
283 * @return buffer cell at the specified visible row/col index
285 getWindowCell: function(windowRow,col) {
286 var bufrow=this.bufferRow(windowRow);
287 return this.isVisible(bufrow) && col < this.rows[bufrow].length ? this.rows[bufrow][col] : null;
290 getWindowAttr: function(windowRow,col) {
291 var bufrow=this.bufferRow(windowRow);
292 return this.attr && this.isVisible(bufrow) && col < this.attr[bufrow].length ? this.attr[bufrow][col] : null;
295 getWindowValue: function(windowRow,col) {
296 return this.getWindowCell(windowRow,col);
299 setWindowValue: function(windowRow,col,newval) {
300 var bufrow=this.bufferRow(windowRow);
301 if (bufrow >= this.windowEnd) return false;
302 return this.setValue(bufrow,col,newval);
305 getCell: function(bufRow,col) {
306 return bufRow < this.size ? this.rows[bufRow][col] : null;
309 getValue: function(bufRow,col) {
310 return this.getCell(bufRow,col);
313 setValue: function(bufRow,col,newval,newstyle) {
314 if (bufRow>=this.size) return false;
315 if (!this.rows[bufRow][col]) this.rows[bufRow][col]={};
316 this.rows[bufRow][col]=newval;
317 if (typeof newstyle=='string') this.rows[bufRow][col]._style=newstyle;
318 this.rows[bufRow][col].modified=true;
322 getRows: function(start, count) {
323 var begPos = start - this.startPos;
324 var endPos = Math.min(begPos + count,this.size);
325 var results = new Array();
326 for ( var i=begPos; i < endPos; i++ ) {
327 results.push(this.rows[i]);
332 applyFilters: function() {
333 var newRows=[],re=[];
334 var r,c,n,i,showRow,filtercnt;
335 var cols=this.liveGrid.columns;
336 for (n=0,filtercnt=0; n<cols.length; n++) {
338 if (c.filterType == Rico.TableColumn.UNFILTERED) continue;
340 if (c.filterOp=='LIKE') re[n]=new RegExp(c.filterValues[0],'i');
342 Rico.writeDebugMsg('applyFilters: # of filters='+filtercnt);
344 this.rows = this.baseRows;
346 for (r=0; r<this.baseRows.length; r++) {
348 for (n=0; n<cols.length && showRow; n++) {
350 if (c.filterType == Rico.TableColumn.UNFILTERED) continue;
351 switch (c.filterOp) {
353 showRow=re[n].test(this.baseRows[r][n]);
356 showRow=this.baseRows[r][n]==c.filterValues[0];
359 for (i=0; i<c.filterValues.length && showRow; i++)
360 showRow=this.baseRows[r][n]!=c.filterValues[i];
363 if (c.format.type=='number')
364 showRow=this.nan2zero(this.baseRows[r][n])<=this.nan2zero(c.filterValues[0]);
366 showRow=this.baseRows[r][n]<=c.filterValues[0];
369 if (c.format.type=='number')
370 showRow=this.nan2zero(this.baseRows[r][n])>=this.nan2zero(c.filterValues[0]);
372 showRow=this.baseRows[r][n]>=c.filterValues[0];
375 showRow=this.baseRows[r][n]=='';
378 showRow=this.baseRows[r][n]!='';
382 if (showRow) newRows.push(this.baseRows[r]);
386 this.rowcntContent = this.size = this.rows.length;
392 // Rico.LiveGrid -----------------------------------------------------
394 Rico.LiveGrid = Class.create(
396 * @lends Rico.LiveGrid#
397 * @property tableId id string for this grid
398 * @property options the options object passed to the constructor extended with defaults
399 * @property buffer the buffer object containing the data for this grid
400 * @property columns array of {@link Rico.TableColumn} objects
404 * @class Buffered LiveGrid component
405 * @extends Rico.GridCommon
408 initialize: function( tableId, buffer, options ) {
409 Object.extend(this, new Rico.GridCommon);
410 Object.extend(this, new Rico.LiveGridMethods);
412 this.tableId = tableId;
413 this.buffer = buffer;
414 Rico.setDebugArea(tableId+"_debugmsgs"); // if used, this should be a textarea
416 Object.extend(this.options, {
417 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)
419 offset : 0, // first row to be displayed
420 prefetchBuffer : true, // load table on page load?
423 canSortDefault : true, // can be overridden in the column specs
424 canFilterDefault : buffer.options.canFilter, // can be overridden in the column specs
425 canHideDefault : true, // can be overridden in the column specs
427 // highlight & selection parameters
428 highlightElem : 'none',// what gets highlighted/selected (cursorRow, cursorCell, menuRow, menuCell, selection, or none)
429 highlightSection : 3, // which section gets highlighted (frozen=1, scrolling=2, all=3, none=0)
430 highlightMethod : 'class', // outline, class, both (outline is less CPU intensive on the client)
431 highlightClass : 'ricoLG_selection',
433 // export/print parameters
434 maxPrint : 1000, // max # of rows that can be printed/exported, 0=disable print/export feature
436 // heading parameters
437 headingSort : 'link', // link: make headings a link that will sort column, hover: make headings a hoverset, none: events on headings are disabled
438 hdrIconsFirst : true, // true: put sort & filter icons before header text, false: after
439 sortAscendImg : 'sort_asc.gif',
440 sortDescendImg : 'sort_desc.gif',
441 filterImg : 'filtercol.gif'
444 // sortCol: initial sort column
446 this.options.sortHandler = this.sortHandler.bind(this);
447 this.options.filterHandler = this.filterHandler.bind(this);
448 this.options.onRefreshComplete = this.bookmarkHandler.bind(this);
449 this.options.rowOverHandler = this.rowMouseOver.bindAsEventListener(this);
450 this.options.mouseDownHandler = this.selectMouseDown.bindAsEventListener(this);
451 this.options.mouseOverHandler = this.selectMouseOver.bindAsEventListener(this);
452 this.options.mouseUpHandler = this.selectMouseUp.bindAsEventListener(this);
453 Object.extend(this.options, options || {});
455 switch (typeof this.options.visibleRows) {
457 this.sizeTo=this.options.visibleRows;
458 switch (this.options.visibleRows) {
459 case 'data': this.options.visibleRows=-2; break;
460 case 'body': this.options.visibleRows=-3; break;
461 case 'parent': this.options.visibleRows=-4; break;
462 default: this.options.visibleRows=-1; break;
466 switch (this.options.visibleRows) {
467 case -1: this.sizeTo='window'; break;
468 case -2: this.sizeTo='data'; break;
469 case -3: this.sizeTo='body'; break;
470 case -4: this.sizeTo='parent'; break;
471 default: this.sizeTo='fixed'; break;
475 this.sizeTo='window';
476 this.options.visibleRows=-1;
479 this.highlightEnabled=this.options.highlightSection>0;
482 if (this.headerColCnt==0) {
483 alert('ERROR: no columns found in "'+this.tableId+'"');
486 this.createColumnArray('TableColumn');
487 if (this.options.headingSort=='hover')
488 this.createHoverSet();
490 this.bookmark=$(this.tableId+"_bookmark");
492 var filterUIrow=this.buffer.options.canFilter ? this.options.FilterLocation : false;
493 if (typeof(filterUIrow)=='number' && filterUIrow<0)
494 filterUIrow=this.addHeadingRow();
495 this.createDataCells(this.options.visibleRows);
496 if (this.pageSize == 0) return;
497 this.buffer.registerGrid(this);
498 if (this.buffer.setBufferSize) this.buffer.setBufferSize(this.pageSize);
499 this.scrollTimeout = null;
500 this.lastScrollPos = 0;
501 this.attachMenuEvents();
503 // preload the images...
504 new Image().src = Rico.imgDir+this.options.filterImg;
505 new Image().src = Rico.imgDir+this.options.sortAscendImg;
506 new Image().src = Rico.imgDir+this.options.sortDescendImg;
507 Rico.writeDebugMsg("images preloaded");
509 this.setSortUI( this.options.sortCol, this.options.sortDir );
511 if (this.listInvisible().length==this.columns.length)
512 this.columns[0].showColumn();
514 this.scrollDiv.style.display="";
515 if (this.buffer.totalRows>0)
516 this.updateHeightDiv();
517 if (this.options.prefetchBuffer) {
518 if (this.bookmark) this.bookmark.innerHTML = RicoTranslate.getPhraseById('bookmarkLoading');
519 if (this.options.canFilterDefault && this.options.getQueryParms)
520 this.checkForFilterParms();
521 this.buffer.fetch(this.options.offset);
523 if (typeof(filterUIrow)=='number')
524 this.createFilters(filterUIrow);
525 this.scrollEventFunc=this.handleScroll.bindAsEventListener(this);
526 this.wheelEventFunc=this.handleWheel.bindAsEventListener(this);
527 this.wheelEvent=(Prototype.Browser.IE || Prototype.Browser.Opera || Prototype.Browser.WebKit) ? 'mousewheel' : 'DOMMouseScroll';
528 if (this.options.offset && this.options.offset < this.buffer.totalRows)
529 setTimeout(this.scrollToRow.bind(this,this.options.offset),50); // Safari requires a delay
531 this.setHorizontalScroll();
532 if (this.options.windowResize)
533 setTimeout(this.pluginWindowResize.bind(this),100);
537 Rico.LiveGridMethods = function() {};
539 Rico.LiveGridMethods.prototype = {
540 /** @lends Rico.LiveGrid# */
542 createHoverSet: function() {
544 for( var c=0; c < this.headerColCnt; c++ ) {
545 if (this.columns[c].sortable) {
546 hdrs.push(this.columns[c].hdrCellDiv);
549 this.hoverSet = new Rico.HoverSet(hdrs);
552 checkForFilterParms: function() {
553 var s=window.location.search;
554 if (s.charAt(0)=='?') s=s.substring(1);
555 var pairs = s.split('&');
556 for (var i=0; i<pairs.length; i++) {
557 if (pairs[i].match(/^f\[\d+\]/)) {
558 this.buffer.options.requestParameters.push(pairs[i]);
564 * set filter on a detail grid that is in a master-detail relationship
566 setDetailFilter: function(colNumber,filterValue) {
567 var c=this.columns[colNumber];
568 c.format.ColData=filterValue;
569 c.setSystemFilter('EQ',filterValue);
573 * Create one table for frozen columns and one for scrolling columns.
574 * Also create div's to contain them.
575 * @returns true on success
577 createTables: function() {
578 var insertloc,hdrSrc,i;
579 var table = $(this.tableId) || $(this.tableId+'_outerDiv');
580 if (!table) return false;
581 if (table.tagName.toLowerCase()=='table') {
582 var theads=table.getElementsByTagName("thead");
583 if (theads.length == 1) {
584 Rico.writeDebugMsg("createTables: using thead section, id="+this.tableId);
585 if (this.options.PanelNamesOnTabHdr && this.options.panels) {
586 var r=theads[0].insertRow(0);
587 this.insertPanelNames(r, 0, this.options.frozenColumns, 'ricoFrozen');
588 this.insertPanelNames(r, this.options.frozenColumns, this.options.columnSpecs.length);
590 hdrSrc=theads[0].rows;
592 Rico.writeDebugMsg("createTables: using tbody section, id="+this.tableId);
593 hdrSrc=new Array(table.rows[0]);
596 } else if (this.options.columnSpecs.length > 0) {
597 if (!table.id.match(/_outerDiv$/)) insertloc=table;
598 Rico.writeDebugMsg("createTables: inserting at "+table.tagName+", id="+this.tableId);
600 alert("ERROR!\n\nUnable to initialize '"+this.tableId+"'\n\nLiveGrid terminated");
605 this.scrollTabs = this.createDiv("scrollTabs",this.innerDiv);
606 this.shadowDiv = this.createDiv("shadow",this.scrollDiv);
607 this.shadowDiv.style.direction='ltr'; // avoid FF bug
608 this.scrollDiv.style.display="none";
609 this.scrollDiv.scrollTop=0;
610 if (this.options.highlightMethod!='class') {
611 this.highlightDiv=[];
612 switch (this.options.highlightElem) {
615 this.highlightDiv[0] = this.createDiv("highlight",this.outerDiv);
616 this.highlightDiv[0].style.display="none";
620 for (i=0; i<2; i++) {
621 this.highlightDiv[i] = this.createDiv("highlight",i==0 ? this.frozenTabs : this.scrollTabs);
622 this.highlightDiv[i].style.display="none";
623 this.highlightDiv[i].id+=i;
627 // create one div for each side of the rectangle
628 var parentDiv=this.options.highlightSection==1 ? this.frozenTabs : this.scrollTabs;
629 for (i=0; i<4; i++) {
630 this.highlightDiv[i] = this.createDiv("highlight",parentDiv);
631 this.highlightDiv[i].style.display="none";
632 this.highlightDiv[i].style.overflow="hidden";
633 this.highlightDiv[i].id+=i;
634 this.highlightDiv[i].style[i % 2==0 ? 'height' : 'width']="0px";
641 for (i=0; i<2; i++) {
642 this.tabs[i] = document.createElement("table");
643 this.tabs[i].className = 'ricoLG_table';
644 this.tabs[i].border=0;
645 this.tabs[i].cellPadding=0;
646 this.tabs[i].cellSpacing=0;
647 this.tabs[i].id = this.tableId+"_tab"+i;
648 this.thead[i]=this.tabs[i].createTHead();
649 this.thead[i].className='ricoLG_top';
650 if (this.tabs[i].tBodies.length==0)
651 this.tbody[i]=this.tabs[i].appendChild(document.createElement("tbody"));
653 this.tbody[i]=this.tabs[i].tBodies[0];
654 this.tbody[i].className='ricoLG_bottom';
655 this.tbody[i].insertRow(-1);
657 this.frozenTabs.appendChild(this.tabs[0]);
658 this.scrollTabs.appendChild(this.tabs[1]);
659 if (insertloc) insertloc.parentNode.insertBefore(this.outerDiv,insertloc);
661 this.headerColCnt = this.getColumnInfo(hdrSrc);
662 this.loadHdrSrc(hdrSrc);
664 this.createHdr(0,0,this.options.frozenColumns);
665 this.createHdr(1,this.options.frozenColumns,this.options.columnSpecs.length);
666 if (this.options.PanelNamesOnTabHdr && this.options.panels) {
667 this.insertPanelNames(this.thead[0].insertRow(0), 0, this.options.frozenColumns);
668 this.insertPanelNames(this.thead[1].insertRow(0), this.options.frozenColumns, this.options.columnSpecs.length);
671 this.headerColCnt = this.getColumnInfo(this.thead[i].rows);
673 for( var c=0; c < this.headerColCnt; c++ )
674 this.tbody[c<this.options.frozenColumns ? 0 : 1].rows[0].insertCell(-1);
675 if (insertloc) table.parentNode.removeChild(table);
676 Rico.writeDebugMsg('createTables end');
680 createDataCells: function(visibleRows) {
681 if (visibleRows < 0) {
682 for (var i=0; i<this.options.minPageRows; i++)
683 this.appendBlankRow();
685 this.autoAppendRows(this.remainingHt());
687 for( var r=0; r < visibleRows; r++ )
688 this.appendBlankRow();
690 var s=this.options.highlightSection;
691 if (s & 1) this.attachHighlightEvents(this.tbody[0]);
692 if (s & 2) this.attachHighlightEvents(this.tbody[1]);
696 * @param colnum column number
697 * @return id string for a filter element
699 filterId: function(colnum) {
700 return 'RicoFilter_'+this.tableId+'_'+colnum;
704 * Create filter elements in heading
705 * Reads this.columns[].filterUI to determine type of filter element for each column (t=text box, s=select list, c=custom)
706 * @param r heading row where filter elements will be placed
708 createFilters: function(r) {
709 for( var c=0; c < this.headerColCnt; c++ ) {
710 var col=this.columns[c];
712 if (typeof fmt.filterUI!='string') continue;
713 var cell=this.hdrCells[r][c].cell;
714 var field,name=this.filterId(c);
715 var divs=cell.getElementsByTagName('div');
716 // copy text alignment from data cell
717 var align=Element.getStyle(this.cell(0,c),'textAlign');
718 Element.setStyle(divs[1], { textAlign: align });
719 switch (fmt.filterUI.charAt(0)) {
722 field=RicoUtil.createFormField(divs[1],'input','text',name,name);
723 var size=fmt.filterUI.match(/\d+/);
724 field.maxLength=fmt.Length || 50;
725 field.size=size ? parseInt(size,10) : 10;
726 var clrimg = document.createElement('img');
727 clrimg.style.paddingLeft='4px';
728 clrimg.style.cursor='pointer';
730 clrimg.src=Rico.imgDir+'delete.gif';
731 clrimg.alt=RicoTranslate.getPhraseById('clear');
732 divs[1].appendChild(clrimg);
733 Event.observe(clrimg,'click',col.filterClear.bindAsEventListener(col,field),false);
734 if (col.filterType==Rico.TableColumn.USERFILTER && col.filterOp=='LIKE') {
735 var v=col.filterValues[0];
736 if (v.charAt(0)=='*') v=v.substr(1);
737 if (v.slice(-1)=='*') v=v.slice(0,-1);
741 Event.observe(field,'keyup',col.filterKeypress.bindAsEventListener(col),false);
742 Event.observe(field,'change',col.filterKeypress.bindAsEventListener(col),false);
746 field=RicoUtil.createFormField(divs[1],'select',null,name);
747 RicoUtil.addSelectOption(field,this.options.FilterAllToken,RicoTranslate.getPhraseById("filterAll"));
749 Object.extend(options, this.buffer.ajaxOptions);
750 var colnum=typeof(fmt.filterCol)=='number' ? fmt.filterCol : c;
751 options.parameters = 'id='+this.tableId+'&distinct='+colnum;
752 options.onComplete = this.filterValuesUpdate.bind(this,c);
753 new Ajax.Request(this.buffer.dataSource, options);
757 if (typeof col._createFilters == 'function')
758 col._createFilters(divs[1], name);
762 this.initFilterImage(r);
766 * update select list filter with values in AJAX response
767 * @returns true on success
769 filterValuesUpdate: function(colnum,request) {
770 var response = request.responseXML.getElementsByTagName("ajax-response");
771 Rico.writeDebugMsg("filterValuesUpdate: "+request.status);
772 if (response == null || response.length != 1) return false;
773 response=response[0];
774 var error = response.getElementsByTagName('error');
775 if (error.length > 0) {
776 Rico.writeDebugMsg("Data provider returned an error:\n"+RicoUtil.getContentAsString(error[0],this.buffer.isEncoded));
777 alert(RicoTranslate.getPhraseById("requestError",RicoUtil.getContentAsString(error[0],this.buffer.isEncoded)));
780 response=response.getElementsByTagName('response')[0];
781 var rowsElement = response.getElementsByTagName('rows')[0];
782 //var colnum = rowsElement.getAttribute("distinct");
783 var col=this.columns[parseInt(colnum,10)];
784 var rows = this.buffer.dom2jstable(rowsElement);
785 var c0,c1,opt,v, field=$(this.filterId(colnum));
786 if (col.filterType==Rico.TableColumn.USERFILTER && col.filterOp=='EQ') v=col.filterValues[0];
787 Rico.writeDebugMsg('filterValuesUpdate: col='+colnum+' rows='+rows.length);
788 for (var i=0; i<rows.length; i++) {
789 if (rows[i].length>0) {
791 if (c0.match(/<span\s+class=(['"]?)ricolookup\1>(.*)<\/span>/i)) {
792 c1=RegExp.leftContext;
794 if (col._getdesc) c1 = col._getdesc(c1);
795 opt=RicoUtil.addSelectOption(field,c0,c1 || RicoTranslate.getPhraseById("filterBlank"));
796 if (col.filterType==Rico.TableColumn.USERFILTER && c0==v) opt.selected=true;
799 Event.observe(field,'change',col.filterChange.bindAsEventListener(col),false);
803 unplugHighlightEvents: function() {
804 var s=this.options.highlightSection;
805 if (s & 1) this.detachHighlightEvents(this.tbody[0]);
806 if (s & 2) this.detachHighlightEvents(this.tbody[1]);
810 * place panel names on first row of grid header (used by LiveGridForms)
812 insertPanelNames: function(r,start,limit,cellClass) {
813 Rico.writeDebugMsg('insertPanelNames: start='+start+' limit='+limit);
814 r.className='ricoLG_hdg';
815 var lastIdx=-1, span, newCell=null, spanIdx=0;
816 for( var c=start; c < limit; c++ ) {
817 if (lastIdx == this.options.columnSpecs[c].panelIdx) {
820 if (newCell) newCell.colSpan=span;
821 newCell = r.insertCell(-1);
822 if (cellClass) newCell.className=cellClass;
824 lastIdx=this.options.columnSpecs[c].panelIdx;
825 newCell.innerHTML=this.options.panels[lastIdx];
828 if (newCell) newCell.colSpan=span;
832 * create grid header for table i (if none was provided)
834 createHdr: function(i,start,limit) {
835 Rico.writeDebugMsg('createHdr: i='+i+' start='+start+' limit='+limit);
836 var mainRow = this.thead[i].insertRow(-1);
837 mainRow.id=this.tableId+'_tab'+i+'h_main';
838 mainRow.className='ricoLG_hdg';
839 for( var c=start; c < limit; c++ ) {
840 var newCell = mainRow.insertCell(-1);
841 newCell.innerHTML=this.options.columnSpecs[c].Hdg;
846 * move header cells in original table to grid
848 loadHdrSrc: function(hdrSrc) {
849 var i,h,c,r,newrow,cells;
850 Rico.writeDebugMsg('loadHdrSrc start');
851 for (i=0; i<2; i++) {
852 for (r=0; r<hdrSrc.length; r++) {
853 newrow = this.thead[i].insertRow(-1);
854 newrow.className='ricoLG_hdg '+this.tableId+'_hdg'+r;
857 if (hdrSrc.length==1) {
858 cells=hdrSrc[0].cells;
859 for (c=0; cells.length > 0; c++)
860 this.thead[c<this.options.frozenColumns ? 0 : 1].rows[0].appendChild(cells[0]);
862 for (r=0; r<hdrSrc.length; r++) {
863 cells=hdrSrc[r].cells;
864 for (c=0,h=0; cells.length > 0; c++) {
865 if (cells[0].className=='ricoFrozen') {
866 if (r==this.headerRowIdx) this.options.frozenColumns=c+1;
870 this.thead[h].rows[r].appendChild(cells[0]);
874 Rico.writeDebugMsg('loadHdrSrc end');
880 sizeDivs: function() {
881 Rico.writeDebugMsg('sizeDivs: '+this.tableId);
885 var firstVisible=this.firstVisible();
886 if (this.pageSize == 0 || firstVisible < 0) return;
887 var totRowHt=this.columns[firstVisible].dataColDiv.offsetHeight;
888 this.rowHeight = Math.round(totRowHt/this.pageSize);
889 var scrHt=this.dataHt;
890 if (this.scrWi>0 || Prototype.Browser.IE || Prototype.Browser.WebKit)
891 scrHt+=this.options.scrollBarWidth;
892 this.scrollDiv.style.height=scrHt+'px';
893 this.innerDiv.style.width=(this.scrWi-this.options.scrollBarWidth+1)+'px';
894 this.resizeDiv.style.height=this.frozenTabs.style.height=this.innerDiv.style.height=(this.hdrHt+this.dataHt+1)+'px';
895 Rico.writeDebugMsg('sizeDivs scrHt='+scrHt+' innerHt='+this.innerDiv.style.height+' rowHt='+this.rowHeight+' pageSize='+this.pageSize);
896 var pad=(this.scrWi-this.scrTabWi < this.options.scrollBarWidth) ? 2 : 0;
897 this.shadowDiv.style.width=(this.scrTabWi+pad)+'px';
898 this.outerDiv.style.height=(this.hdrHt+scrHt)+'px';
899 this.setHorizontalScroll();
902 setHorizontalScroll: function() {
903 var scrleft=this.scrollDiv.scrollLeft;
904 this.scrollTabs.style.left=(-scrleft)+'px';
907 remainingHt: function() {
909 var winHt=RicoUtil.windowHeight();
910 var margin=Prototype.Browser.IE ? 15 : 10;
911 // if there is a horizontal scrollbar take it into account
912 if (!Prototype.Browser.IE && window.frameElement && window.frameElement.scrolling=='yes' && this.sizeTo!='parent') margin+=this.options.scrollBarWidth;
913 switch (this.sizeTo) {
916 var divPos=Position.page(this.outerDiv);
917 tabHt=Math.max(this.tabs[0].offsetHeight,this.tabs[1].offsetHeight);
918 Rico.writeDebugMsg("remainingHt, winHt="+winHt+' tabHt='+tabHt+' gridY='+divPos[1]);
919 return winHt-divPos[1]-tabHt-this.options.scrollBarWidth-margin; // allow for scrollbar and some margin
921 var offset=this.offsetFromParent(this.outerDiv);
922 tabHt=Math.max(this.tabs[0].offsetHeight,this.tabs[1].offsetHeight);
923 if (Prototype.Browser.IE) Element.hide(this.outerDiv);
924 var parentHt=this.outerDiv.parentNode.offsetHeight;
925 if (Prototype.Browser.IE) Element.show(this.outerDiv);
926 Rico.writeDebugMsg("remainingHt, parentHt="+parentHt+' gridY='+offset+' winHt='+winHt+' tabHt='+tabHt);
927 return parentHt - tabHt - offset - this.options.scrollBarWidth;
929 //Rico.writeDebugMsg("remainingHt, document.height="+document.height);
930 //Rico.writeDebugMsg("remainingHt, body.offsetHeight="+document.body.offsetHeight);
931 //Rico.writeDebugMsg("remainingHt, body.scrollHeight="+document.body.scrollHeight);
932 //Rico.writeDebugMsg("remainingHt, documentElement.scrollHeight="+document.documentElement.scrollHeight);
933 var bodyHt=Prototype.Browser.IE ? document.body.scrollHeight : document.body.offsetHeight;
934 var remHt=winHt-bodyHt-margin;
935 if (!Prototype.Browser.WebKit) remHt-=this.options.scrollBarWidth;
936 Rico.writeDebugMsg("remainingHt, winHt="+winHt+' pageHt='+bodyHt+' remHt='+remHt);
939 tabHt=Math.max(this.tabs[0].offsetHeight,this.tabs[1].offsetHeight);
940 Rico.writeDebugMsg("remainingHt, winHt="+winHt+' tabHt='+tabHt);
941 if (this.sizeTo.slice(-1)=='%') winHt*=parseFloat(this.sizeTo)/100.0;
942 else if (this.sizeTo.slice(-2)=='px') winHt=parseInt(this.sizeTo,10);
943 return winHt-tabHt-this.options.scrollBarWidth-margin; // allow for scrollbar and some margin
947 offsetFromParent: function(element) {
949 var elParent=element.parentNode;
951 //Rico.writeDebugMsg("offsetFromParent: "+element.tagName+' id='+element.id+' otop='+element.offsetTop);
952 valueT += element.offsetTop || 0;
953 element = element.offsetParent;
954 if (!element || element==null) break;
955 var p = Element.getStyle(element, 'position');
956 if (element.tagName=='BODY' || element.tagName=='HTML' || p=='absolute') return valueT-elParent.offsetTop;
957 } while (element != elParent);
961 adjustPageSize: function() {
962 var remHt=this.remainingHt();
963 Rico.writeDebugMsg('adjustPageSize remHt='+remHt+' lastRow='+this.lastRowPos);
964 if (remHt > this.rowHeight)
965 this.autoAppendRows(remHt);
966 else if (remHt < 0 || this.sizeTo=='data')
967 this.autoRemoveRows(-remHt);
970 pluginWindowResize: function() {
971 this.resizeWindowHandler=this.resizeWindow.bindAsEventListener(this);
972 Event.observe(window, "resize", this.resizeWindowHandler, false);
975 unplugWindowResize: function() {
976 if (!this.resizeWindowHandler) return;
977 Event.stopObserving(window,"resize", this.resizeWindowHandler, false);
978 this.resizeWindowHandler=null;
981 resizeWindow: function() {
982 Rico.writeDebugMsg('resizeWindow '+this.tableId+' lastRow='+this.lastRowPos);
983 if (this.resizeState=='finish') {
984 Rico.writeDebugMsg('resizeWindow postponed');
985 this.resizeState='resize';
988 if (!this.sizeTo || this.sizeTo=='fixed') {
992 if (this.sizeTo=='parent' && Element.getStyle(this.outerDiv.parentNode,'display') == 'none') return;
993 var oldSize=this.pageSize;
994 this.adjustPageSize();
995 if (this.pageSize > oldSize && this.buffer.totalRows>0) {
996 this.isPartialBlank=true;
997 var adjStart=this.adjustRow(this.lastRowPos);
998 this.buffer.fetch(adjStart);
999 } else if (this.pageSize < oldSize) {
1000 if (this.options.onRefreshComplete) this.options.onRefreshComplete(this.contentStartPos,this.contentStartPos+this.pageSize-1); // update bookmark
1002 this.resizeState='finish';
1003 setTimeout(this.finishResize.bind(this),20);
1004 Rico.writeDebugMsg('resizeWindow '+this.tableId+' complete. old size='+oldSize+' new size='+this.pageSize);
1007 finishResize: function() {
1009 this.updateHeightDiv();
1010 if (this.resizeState=='resize') {
1011 this.resizeWindow();
1013 this.resizeState='';
1017 topOfLastPage: function() {
1018 return Math.max(this.buffer.totalRows-this.pageSize,0);
1021 updateHeightDiv: function() {
1022 var notdisp=this.topOfLastPage();
1023 var ht = this.scrollDiv.clientHeight + this.rowHeight * notdisp;
1024 Rico.writeDebugMsg("updateHeightDiv, ht="+ht+' scrollDiv.clientHeight='+this.scrollDiv.clientHeight+' rowsNotDisplayed='+notdisp);
1025 this.shadowDiv.style.height=ht+'px';
1028 autoRemoveRows: function(overage) {
1029 if (!this.rowHeight) return;
1030 var removeCnt=Math.ceil(overage / this.rowHeight);
1031 if (this.sizeTo=='data')
1032 removeCnt=Math.max(removeCnt,this.pageSize-this.buffer.totalRows);
1033 Rico.writeDebugMsg("autoRemoveRows overage="+overage+" removeCnt="+removeCnt);
1034 for (var i=0; i<removeCnt; i++)
1038 removeRow: function() {
1039 if (this.pageSize <= this.options.minPageRows) return;
1041 for( var c=0; c < this.headerColCnt; c++ ) {
1042 var cell=this.columns[c].cell(this.pageSize);
1043 this.columns[c].dataColDiv.removeChild(cell);
1047 autoAppendRows: function(overage) {
1048 if (!this.rowHeight) return;
1049 var addCnt=Math.floor(overage / this.rowHeight);
1050 Rico.writeDebugMsg("autoAppendRows overage="+overage+" cnt="+addCnt+" rowHt="+this.rowHeight);
1051 for (var i=0; i<addCnt; i++) {
1052 if (this.sizeTo=='data' && this.pageSize>=this.buffer.totalRows) break;
1053 this.appendBlankRow();
1058 * on older systems, this can be fairly slow
1060 appendBlankRow: function() {
1061 if (this.pageSize >= this.options.maxPageRows) return;
1062 Rico.writeDebugMsg("appendBlankRow #"+this.pageSize);
1063 var cls=this.defaultRowClass(this.pageSize);
1064 for( var c=0; c < this.headerColCnt; c++ ) {
1065 var newdiv = document.createElement("div");
1066 newdiv.className = 'ricoLG_cell '+cls;
1067 newdiv.id=this.tableId+'_'+this.pageSize+'_'+c;
1068 this.columns[c].dataColDiv.appendChild(newdiv);
1069 newdiv.innerHTML=' ';
1070 if (this.columns[c].format.canDrag && dndMgr)
1071 dndMgr.registerDraggable( new Rico.LiveGridDraggable(this, this.pageSize, c) );
1072 if (this.columns[c]._create)
1073 this.columns[c]._create(newdiv,this.pageSize);
1078 defaultRowClass: function(rownum) {
1079 return (rownum % 2==0) ? 'ricoLG_evenRow' : 'ricoLG_oddRow';
1082 handleMenuClick: function(e) {
1083 if (!this.menu) return;
1085 this.unhighlight(); // in case highlighting was invoked externally
1087 var cell=Event.element(e);
1088 if (cell.className=='ricoLG_highlightDiv') {
1089 idx=this.highlightIdx;
1091 cell=RicoUtil.getParentByTagName(cell,'div','ricoLG_cell');
1093 idx=this.winCellIndex(cell);
1094 if ((this.options.highlightSection & (idx.tabIdx+1))==0) return;
1096 this.highlight(idx);
1097 this.highlightEnabled=false;
1098 if (this.hideScroll) this.scrollDiv.style.overflow="hidden";
1100 if (!this.menu.div) this.menu.createDiv();
1101 this.menu.liveGrid=this;
1102 if (this.menu.buildGridMenu) {
1103 var showMenu=this.menu.buildGridMenu(idx.row, idx.column, idx.tabIdx);
1104 if (!showMenu) return;
1106 if (this.options.highlightElem=='selection' && !this.isSelected(idx.cell)) {
1107 this.selectCell(idx.cell);
1109 this.menu.showmenu(e,this.closeMenu.bind(this));
1112 closeMenu: function() {
1113 if (!this.menuIdx) return;
1114 if (this.hideScroll) this.scrollDiv.style.overflow="";
1116 this.highlightEnabled=true;
1121 * @return index of cell within the window
1123 winCellIndex: function(cell) {
1124 var a=cell.id.split(/_/);
1126 var r=parseInt(a[l-2],10);
1127 var c=parseInt(a[l-1],10);
1128 return {row:r, column:c, tabIdx:this.columns[c].tabIdx, cell:cell};
1132 * @return index of cell within the dataset
1134 datasetIndex: function(cell) {
1135 var idx=this.winCellIndex(cell);
1136 idx.row+=this.buffer.windowPos;
1137 idx.onBlankRow=(idx.row >= this.buffer.endPos());
1141 attachHighlightEvents: function(tBody) {
1142 switch (this.options.highlightElem) {
1144 Event.observe(tBody,"mousedown", this.options.mouseDownHandler, false);
1146 tBody.ondrag = function () { return false; };
1148 tBody.onselectstart = function () { return false; };
1152 Event.observe(tBody,"mouseover", this.options.rowOverHandler, false);
1157 detachHighlightEvents: function(tBody) {
1158 switch (this.options.highlightElem) {
1160 Event.stopObserving(tBody,"mousedown", this.options.mouseDownHandler, false);
1161 tBody.ondrag = null;
1162 tBody.onselectstart = null;
1166 Event.stopObserving(tBody,"mouseover", this.options.rowOverHandler, false);
1172 * @return array of objects containing row/col indexes (index values are relative to the start of the window)
1174 getVisibleSelection: function() {
1176 if (this.SelectIdxStart && this.SelectIdxEnd) {
1177 var r1=Math.max(Math.min(this.SelectIdxEnd.row,this.SelectIdxStart.row)-this.buffer.startPos,this.buffer.windowStart);
1178 var r2=Math.min(Math.max(this.SelectIdxEnd.row,this.SelectIdxStart.row)-this.buffer.startPos,this.buffer.windowEnd-1);
1179 var c1=Math.min(this.SelectIdxEnd.column,this.SelectIdxStart.column);
1180 var c2=Math.max(this.SelectIdxEnd.column,this.SelectIdxStart.column);
1181 //Rico.writeDebugMsg("getVisibleSelection "+r1+','+c1+' to '+r2+','+c2+' ('+this.SelectIdxStart.row+',startPos='+this.buffer.startPos+',windowPos='+this.buffer.windowPos+',windowEnd='+this.buffer.windowEnd+')');
1182 for (var r=r1; r<=r2; r++) {
1183 for (var c=c1; c<=c2; c++)
1184 cellList.push({row:r-this.buffer.windowStart,column:c});
1187 if (this.SelectCtrl) {
1188 for (var i=0; i<this.SelectCtrl.length; i++) {
1189 if (this.SelectCtrl[i].row>=this.buffer.windowStart && this.SelectCtrl[i].row<this.buffer.windowEnd)
1190 cellList.push({row:this.SelectCtrl[i].row-this.buffer.windowStart,column:this.SelectCtrl[i].column});
1196 updateSelectOutline: function() {
1197 if (!this.SelectIdxStart || !this.SelectIdxEnd) return;
1198 var r1=Math.max(Math.min(this.SelectIdxEnd.row,this.SelectIdxStart.row), this.buffer.windowStart);
1199 var r2=Math.min(Math.max(this.SelectIdxEnd.row,this.SelectIdxStart.row), this.buffer.windowEnd-1);
1201 this.HideSelection();
1204 var c1=Math.min(this.SelectIdxEnd.column,this.SelectIdxStart.column);
1205 var c2=Math.max(this.SelectIdxEnd.column,this.SelectIdxStart.column);
1206 var top1=this.columns[c1].cell(r1-this.buffer.windowStart).offsetTop;
1207 var cell2=this.columns[c1].cell(r2-this.buffer.windowStart);
1208 var bottom2=cell2.offsetTop+cell2.offsetHeight;
1209 var left1=this.columns[c1].dataCell.offsetLeft;
1210 var left2=this.columns[c2].dataCell.offsetLeft;
1211 var right2=left2+this.columns[c2].dataCell.offsetWidth;
1212 //window.status='updateSelectOutline: '+r1+' '+r2+' top='+top1+' bot='+bottom2;
1213 this.highlightDiv[0].style.top=this.highlightDiv[3].style.top=this.highlightDiv[1].style.top=(this.hdrHt+top1-1) + 'px';
1214 this.highlightDiv[2].style.top=(this.hdrHt+bottom2-1)+'px';
1215 this.highlightDiv[3].style.left=(left1-2)+'px';
1216 this.highlightDiv[0].style.left=this.highlightDiv[2].style.left=(left1-1)+'px';
1217 this.highlightDiv[1].style.left=(right2-1)+'px';
1218 this.highlightDiv[0].style.width=this.highlightDiv[2].style.width=(right2-left1-1) + 'px';
1219 this.highlightDiv[1].style.height=this.highlightDiv[3].style.height=(bottom2-top1) + 'px';
1220 //this.highlightDiv[0].style.right=this.highlightDiv[2].style.right=this.highlightDiv[1].style.right=()+'px';
1221 //this.highlightDiv[2].style.bottom=this.highlightDiv[3].style.bottom=this.highlightDiv[1].style.bottom=(this.hdrHt+bottom2) + 'px';
1222 for (var i=0; i<4; i++)
1223 this.highlightDiv[i].style.display='';
1226 HideSelection: function() {
1228 if (this.options.highlightMethod!='class') {
1229 for (i=0; i<this.highlightDiv.length; i++)
1230 this.highlightDiv[i].style.display='none';
1232 if (this.options.highlightMethod!='outline') {
1233 var cellList=this.getVisibleSelection();
1234 Rico.writeDebugMsg("HideSelection "+cellList.length);
1235 for (i=0; i<cellList.length; i++)
1236 this.unhighlightCell(this.columns[cellList[i].column].cell(cellList[i].row));
1240 ShowSelection: function() {
1241 if (this.options.highlightMethod!='class')
1242 this.updateSelectOutline();
1243 if (this.options.highlightMethod!='outline') {
1244 var cellList=this.getVisibleSelection();
1245 for (var i=0; i<cellList.length; i++)
1246 this.highlightCell(this.columns[cellList[i].column].cell(cellList[i].row));
1250 ClearSelection: function() {
1251 Rico.writeDebugMsg("ClearSelection");
1252 this.HideSelection();
1253 this.SelectIdxStart=null;
1254 this.SelectIdxEnd=null;
1258 selectCell: function(cell) {
1259 this.ClearSelection();
1260 this.SelectIdxStart=this.SelectIdxEnd=this.datasetIndex(cell);
1261 this.ShowSelection();
1264 AdjustSelection: function(cell) {
1265 var newIdx=this.datasetIndex(cell);
1266 if (this.SelectIdxStart.tabIdx != newIdx.tabIdx) return;
1267 this.HideSelection();
1268 this.SelectIdxEnd=newIdx;
1269 this.ShowSelection();
1272 RefreshSelection: function() {
1273 var cellList=this.getVisibleSelection();
1274 for (var i=0; i<cellList.length; i++) {
1275 this.columns[cellList[i].column].displayValue(cellList[i].row);
1279 FillSelection: function(newVal,newStyle) {
1280 if (this.SelectIdxStart && this.SelectIdxEnd) {
1281 var r1=Math.min(this.SelectIdxEnd.row,this.SelectIdxStart.row);
1282 var r2=Math.max(this.SelectIdxEnd.row,this.SelectIdxStart.row);
1283 var c1=Math.min(this.SelectIdxEnd.column,this.SelectIdxStart.column);
1284 var c2=Math.max(this.SelectIdxEnd.column,this.SelectIdxStart.column);
1285 for (var r=r1; r<=r2; r++) {
1286 for (var c=c1; c<=c2; c++) {
1287 this.buffer.setValue(r,c,newVal,newStyle);
1291 if (this.SelectCtrl) {
1292 for (var i=0; i<this.SelectCtrl.length; i++) {
1293 this.buffer.setValue(this.SelectCtrl[i].row,this.SelectCtrl[i].column,newVal,newStyle);
1296 this.RefreshSelection();
1300 * Process mouse down event
1301 * @param e event object
1303 selectMouseDown: function(e) {
1304 if (this.highlightEnabled==false) return true;
1306 var cell=Event.element(e);
1307 if (!Event.isLeftClick(e)) return true;
1308 cell=RicoUtil.getParentByTagName(cell,'div','ricoLG_cell');
1309 if (!cell) return true;
1311 var newIdx=this.datasetIndex(cell);
1312 if (newIdx.onBlankRow) return true;
1313 Rico.writeDebugMsg("selectMouseDown @"+newIdx.row+','+newIdx.column);
1315 if (!this.SelectIdxStart || this.options.highlightMethod!='class') return true;
1316 if (!this.isSelected(cell)) {
1317 this.highlightCell(cell);
1318 this.SelectCtrl.push(this.datasetIndex(cell));
1320 for (var i=0; i<this.SelectCtrl.length; i++) {
1321 if (this.SelectCtrl[i].row==newIdx.row && this.SelectCtrl[i].column==newIdx.column) {
1322 this.unhighlightCell(cell);
1323 this.SelectCtrl.splice(i,1);
1328 } else if (e.shiftKey) {
1329 if (!this.SelectIdxStart) return true;
1330 this.AdjustSelection(cell);
1332 this.selectCell(cell);
1333 this.pluginSelect();
1338 pluginSelect: function() {
1339 if (this.selectPluggedIn) return;
1340 var tBody=this.tbody[this.SelectIdxStart.tabIdx];
1341 Event.observe(tBody,"mouseover", this.options.mouseOverHandler, false);
1342 Event.observe(this.outerDiv,"mouseup", this.options.mouseUpHandler, false);
1343 this.selectPluggedIn=true;
1346 unplugSelect: function() {
1347 if (!this.selectPluggedIn) return;
1348 var tBody=this.tbody[this.SelectIdxStart.tabIdx];
1349 Event.stopObserving(tBody,"mouseover", this.options.mouseOverHandler , false);
1350 Event.stopObserving(this.outerDiv,"mouseup", this.options.mouseUpHandler , false);
1351 this.selectPluggedIn=false;
1354 selectMouseUp: function(e) {
1355 this.unplugSelect();
1356 var cell=Event.element(e);
1357 cell=RicoUtil.getParentByTagName(cell,'div','ricoLG_cell');
1359 if (this.SelectIdxStart && this.SelectIdxEnd)
1360 this.AdjustSelection(cell);
1362 this.ClearSelection();
1365 selectMouseOver: function(e) {
1366 var cell=Event.element(e);
1367 cell=RicoUtil.getParentByTagName(cell,'div','ricoLG_cell');
1369 this.AdjustSelection(cell);
1373 isSelected: function(cell) {
1374 if (this.options.highlightMethod!='outline') return Element.hasClassName(cell,this.options.highlightClass);
1375 if (!this.SelectIdxStart || !this.SelectIdxEnd) return false;
1376 var r1=Math.max(Math.min(this.SelectIdxEnd.row,this.SelectIdxStart.row), this.buffer.windowStart);
1377 var r2=Math.min(Math.max(this.SelectIdxEnd.row,this.SelectIdxStart.row), this.buffer.windowEnd-1);
1378 if (r1 > r2) return false;
1379 var c1=Math.min(this.SelectIdxEnd.column,this.SelectIdxStart.column);
1380 var c2=Math.max(this.SelectIdxEnd.column,this.SelectIdxStart.column);
1381 var curIdx=this.datasetIndex(cell);
1382 return (r1<=curIdx.row && curIdx.row<=r2 && c1<=curIdx.column && curIdx.column<=c2);
1385 highlightCell: function(cell) {
1386 Element.addClassName(cell,this.options.highlightClass);
1389 unhighlightCell: function(cell) {
1390 if (cell==null) return;
1391 Element.removeClassName(cell,this.options.highlightClass);
1394 selectRow: function(r) {
1395 for (var c=0; c<this.columns.length; c++)
1396 this.highlightCell(this.columns[c].cell(r));
1399 unselectRow: function(r) {
1400 for (var c=0; c<this.columns.length; c++)
1401 this.unhighlightCell(this.columns[c].cell(r));
1404 rowMouseOver: function(e) {
1405 if (!this.highlightEnabled) return;
1406 var cell=Event.element(e);
1407 cell=RicoUtil.getParentByTagName(cell,'div','ricoLG_cell');
1409 var newIdx=this.winCellIndex(cell);
1410 if ((this.options.highlightSection & (newIdx.tabIdx+1))==0) return;
1411 this.highlight(newIdx);
1414 highlight: function(newIdx) {
1415 if (this.options.highlightMethod!='outline') this.cursorSetClass(newIdx);
1416 if (this.options.highlightMethod!='class') this.cursorOutline(newIdx);
1417 this.highlightIdx=newIdx;
1420 cursorSetClass: function(newIdx) {
1421 switch (this.options.highlightElem) {
1424 if (this.highlightIdx) this.unhighlightCell(this.highlightIdx.cell);
1425 this.highlightCell(newIdx.cell);
1429 if (this.highlightIdx) this.unselectRow(this.highlightIdx.row);
1430 var s1=this.options.highlightSection & 1;
1431 var s2=this.options.highlightSection & 2;
1432 var c0=s1 ? 0 : this.options.frozenColumns;
1433 var c1=s2 ? this.columns.length : this.options.frozenColumns;
1434 for (var c=c0; c<c1; c++)
1435 this.highlightCell(this.columns[c].cell(newIdx.row));
1441 cursorOutline: function(newIdx) {
1443 switch (this.options.highlightElem) {
1446 div=this.highlightDiv[newIdx.tabIdx];
1447 div.style.left=(this.columns[newIdx.column].dataCell.offsetLeft-1)+'px';
1448 div.style.width=this.columns[newIdx.column].colWidth;
1449 this.highlightDiv[1-newIdx.tabIdx].style.display='none';
1453 div=this.highlightDiv[0];
1454 var s1=this.options.highlightSection & 1;
1455 var s2=this.options.highlightSection & 2;
1456 div.style.left=s1 ? '0px' : this.frozenTabs.style.width;
1457 div.style.width=((s1 ? this.frozenTabs.offsetWidth : 0) + (s2 ? this.innerDiv.offsetWidth : 0) - 4)+'px';
1461 div.style.top=(this.hdrHt+newIdx.row*this.rowHeight-1)+'px';
1462 div.style.height=(this.rowHeight-1)+'px';
1463 div.style.display='';
1466 unhighlight: function() {
1467 switch (this.options.highlightElem) {
1469 this.highlightIdx=this.menuIdx;
1472 if (this.highlightIdx) this.unhighlightCell(this.highlightIdx.cell);
1473 if (!this.highlightDiv) return;
1474 for (var i=0; i<2; i++)
1475 this.highlightDiv[i].style.display='none';
1478 this.highlightIdx=this.menuIdx;
1481 if (this.highlightIdx) this.unselectRow(this.highlightIdx.row);
1482 if (this.highlightDiv) this.highlightDiv[0].style.display='none';
1487 resetContents: function(resetHt) {
1488 Rico.writeDebugMsg("resetContents("+resetHt+")");
1489 this.ClearSelection();
1490 this.buffer.clear();
1492 if (typeof resetHt=='undefined' || resetHt==true) {
1493 this.buffer.setTotalRows(0);
1495 this.scrollToRow(0);
1497 this.clearBookmark();
1500 setImages: function() {
1501 for (var n=0; n<this.columns.length; n++)
1502 this.columns[n].setImage();
1505 // returns column index, or -1 if there are no sorted columns
1506 findSortedColumn: function() {
1507 for (var n=0; n<this.columns.length; n++) {
1508 if (this.columns[n].isSorted()) return n;
1513 findColumnName: function(name) {
1514 for (var n=0; n<this.columns.length; n++) {
1515 if (this.columns[n].fieldName == name) return n;
1523 setSortUI: function( columnNameOrNum, sortDirection ) {
1524 Rico.writeDebugMsg("setSortUI: "+columnNameOrNum+' '+sortDirection);
1525 var colnum=this.findSortedColumn();
1527 sortDirection=this.columns[colnum].getSortDirection();
1529 if (typeof sortDirection!='string') {
1530 sortDirection=Rico.TableColumn.SORT_ASC;
1532 sortDirection=sortDirection.toUpperCase();
1533 if (sortDirection != Rico.TableColumn.SORT_DESC) sortDirection=Rico.TableColumn.SORT_ASC;
1535 switch (typeof columnNameOrNum) {
1537 colnum=this.findColumnName(columnNameOrNum);
1540 colnum=columnNameOrNum;
1544 if (typeof(colnum)!='number' || colnum < 0) return;
1546 this.columns[colnum].setSorted(sortDirection);
1547 this.buffer.sortBuffer(colnum);
1551 * clear sort flag on all columns
1553 clearSort: function() {
1554 for (var x=0;x<this.columns.length;x++)
1555 this.columns[x].setUnsorted();
1559 * clear filters on all columns
1561 clearFilters: function() {
1562 for (var x=0;x<this.columns.length;x++) {
1563 this.columns[x].setUnfiltered(true);
1565 if (this.options.filterHandler) {
1566 this.options.filterHandler();
1571 * returns number of columns with a user filter set
1573 filterCount: function() {
1574 for (var x=0,cnt=0;x<this.columns.length;x++) {
1575 if (this.columns[x].isFiltered()) cnt++;
1580 sortHandler: function() {
1582 this.ClearSelection();
1584 var n=this.findSortedColumn();
1586 Rico.writeDebugMsg("sortHandler: sorting column "+n);
1587 this.buffer.sortBuffer(n);
1589 this.scrollDiv.scrollTop = 0;
1590 this.buffer.fetch(0);
1593 filterHandler: function() {
1594 Rico.writeDebugMsg("filterHandler");
1596 if (this.buffer.processingRequest) {
1597 this.queueFilter=true;
1600 this.unplugScroll();
1601 this.ClearSelection();
1603 this.clearBookmark();
1605 this.buffer.fetch(-1);
1606 setTimeout(this.pluginScroll.bind(this), 1); // resetting ht div can cause a scroll event, triggering an extra fetch
1609 clearBookmark: function() {
1610 if (this.bookmark) this.bookmark.innerHTML=" ";
1613 bookmarkHandler: function(firstrow,lastrow) {
1615 if (isNaN(firstrow) || !this.bookmark) return;
1616 var totrows=this.buffer.totalRows;
1617 if (totrows < lastrow) lastrow=totrows;
1619 newhtml = RicoTranslate.getPhraseById('bookmarkNoMatch');
1620 } else if (lastrow<0) {
1621 newhtml = RicoTranslate.getPhraseById('bookmarkNoRec');
1622 } else if (this.buffer.foundRowCount) {
1623 newhtml = RicoTranslate.getPhraseById('bookmarkExact',firstrow,lastrow,totrows);
1625 newhtml = RicoTranslate.getPhraseById('bookmarkAbout',firstrow,lastrow,totrows);
1627 this.bookmark.innerHTML = newhtml;
1630 clearRows: function() {
1631 if (this.isBlank==true) return;
1632 for (var c=0; c < this.columns.length; c++)
1633 this.columns[c].clearColumn();
1634 this.isBlank = true;
1637 refreshContents: function(startPos) {
1638 Rico.writeDebugMsg("refreshContents: startPos="+startPos+" lastRow="+this.lastRowPos+" PartBlank="+this.isPartialBlank+" pageSize="+this.pageSize);
1641 this.unhighlight(); // in case highlighting was manually invoked
1642 if (this.queueFilter) {
1643 Rico.writeDebugMsg("refreshContents: cancelling refresh because filter has changed");
1644 this.queueFilter=false;
1645 this.filterHandler();
1648 this.highlightEnabled=this.options.highlightSection!='none';
1649 if (startPos == this.lastRowPos && !this.isPartialBlank && !this.isBlank) return;
1650 this.isBlank = false;
1651 var viewPrecedesBuffer = this.buffer.startPos > startPos;
1652 var contentStartPos = viewPrecedesBuffer ? this.buffer.startPos: startPos;
1653 this.contentStartPos = contentStartPos+1;
1654 var contentEndPos = Math.min(this.buffer.startPos + this.buffer.size, startPos + this.pageSize);
1655 var onRefreshComplete = this.options.onRefreshComplete;
1657 if ((startPos + this.pageSize < this.buffer.startPos) ||
1658 (this.buffer.startPos + this.buffer.size < startPos) ||
1659 (this.buffer.size == 0)) {
1661 if (onRefreshComplete) onRefreshComplete(this.contentStartPos,contentEndPos); // update bookmark
1665 Rico.writeDebugMsg('refreshContents: contentStartPos='+contentStartPos+' contentEndPos='+contentEndPos+' viewPrecedesBuffer='+viewPrecedesBuffer);
1666 var rowSize = contentEndPos - contentStartPos;
1667 this.buffer.setWindow(contentStartPos, rowSize );
1668 var blankSize = this.pageSize - rowSize;
1669 var blankOffset = viewPrecedesBuffer ? 0: rowSize;
1670 var contentOffset = viewPrecedesBuffer ? blankSize: 0;
1672 for (var r=0; r < rowSize; r++) { //initialize what we have
1673 for (var c=0; c < this.columns.length; c++)
1674 this.columns[c].displayValue(r + contentOffset);
1676 for (var i=0; i < blankSize; i++) // blank out the rest
1677 this.blankRow(i + blankOffset);
1678 if (this.options.highlightElem=='selection') this.ShowSelection();
1679 this.isPartialBlank = blankSize > 0;
1680 this.lastRowPos = startPos;
1681 Rico.writeDebugMsg("refreshContents complete, startPos="+startPos);
1682 if (onRefreshComplete) onRefreshComplete(this.contentStartPos,contentEndPos); // update bookmark
1685 scrollToRow: function(rowOffset) {
1686 var p=this.rowToPixel(rowOffset);
1687 Rico.writeDebugMsg("scrollToRow, rowOffset="+rowOffset+" pixel="+p);
1688 this.scrollDiv.scrollTop = p; // this causes a scroll event
1689 if ( this.options.onscroll )
1690 this.options.onscroll( this, rowOffset );
1693 scrollUp: function() {
1694 this.moveRelative(-1);
1697 scrollDown: function() {
1698 this.moveRelative(1);
1701 pageUp: function() {
1702 this.moveRelative(-this.pageSize);
1705 pageDown: function() {
1706 this.moveRelative(this.pageSize);
1709 adjustRow: function(rowOffset) {
1710 var notdisp=this.topOfLastPage();
1711 if (notdisp == 0 || !rowOffset) return 0;
1712 return Math.min(notdisp,rowOffset);
1715 rowToPixel: function(rowOffset) {
1716 return this.adjustRow(rowOffset) * this.rowHeight;
1720 * @returns row to display at top of scroll div
1722 pixeltorow: function(p) {
1723 var notdisp=this.topOfLastPage();
1724 if (notdisp == 0) return 0;
1725 var prow=parseInt(p/this.rowHeight,10);
1726 return Math.min(notdisp,prow);
1729 moveRelative: function(relOffset) {
1730 var newoffset=Math.max(this.scrollDiv.scrollTop+relOffset*this.rowHeight,0);
1731 newoffset=Math.min(newoffset,this.scrollDiv.scrollHeight);
1732 //Rico.writeDebugMsg("moveRelative, newoffset="+newoffset);
1733 this.scrollDiv.scrollTop=newoffset;
1736 pluginScroll: function() {
1737 if (this.scrollPluggedIn) return;
1738 Rico.writeDebugMsg("pluginScroll: wheelEvent="+this.wheelEvent);
1739 Event.observe(this.scrollDiv,"scroll",this.scrollEventFunc, false);
1740 for (var t=0; t<2; t++)
1741 Event.observe(this.tabs[t],this.wheelEvent,this.wheelEventFunc, false);
1742 this.scrollPluggedIn=true;
1745 unplugScroll: function() {
1746 if (!this.scrollPluggedIn) return;
1747 Rico.writeDebugMsg("unplugScroll");
1748 Event.stopObserving(this.scrollDiv,"scroll", this.scrollEventFunc , false);
1749 for (var t=0; t<2; t++)
1750 Event.stopObserving(this.tabs[t],this.wheelEvent,this.wheelEventFunc, false);
1751 this.scrollPluggedIn=false;
1754 handleWheel: function(e) {
1757 if (Prototype.Browser.Opera)
1758 delta = e.wheelDelta/120;
1759 else if (Prototype.Browser.WebKit)
1760 delta = -e.wheelDelta/12;
1762 delta = -e.wheelDelta/120;
1763 } else if (e.detail) {
1764 delta = e.detail/3; /* Mozilla/Gecko */
1766 if (delta) this.moveRelative(delta);
1771 handleScroll: function(e) {
1772 if ( this.scrollTimeout )
1773 clearTimeout( this.scrollTimeout );
1774 this.setHorizontalScroll();
1775 var scrtop=this.scrollDiv.scrollTop;
1776 var vscrollDiff = this.lastScrollPos-scrtop;
1777 if (vscrollDiff == 0.00) return;
1778 var newrow=this.pixeltorow(scrtop);
1779 if (newrow == this.lastRowPos && !this.isPartialBlank && !this.isBlank) return;
1780 var stamp1 = new Date();
1781 //Rico.writeDebugMsg("handleScroll, newrow="+newrow+" scrtop="+scrtop);
1782 if (this.options.highlightElem=='selection') this.HideSelection();
1783 this.buffer.fetch(newrow);
1784 if (this.options.onscroll) this.options.onscroll(this, newrow);
1785 this.scrollTimeout = setTimeout(this.scrollIdle.bind(this), 1200 );
1786 this.lastScrollPos = this.scrollDiv.scrollTop;
1787 var stamp2 = new Date();
1788 //Rico.writeDebugMsg("handleScroll, time="+(stamp2.getTime()-stamp1.getTime()));
1791 scrollIdle: function() {
1792 if ( this.options.onscrollidle )
1793 this.options.onscrollidle();
1796 printAll: function(exportType) {
1797 this.showMsg(RicoTranslate.getPhraseById('exportInProgress'));
1798 setTimeout(this._printAll.bind(this,exportType),10); // allow message to paint
1802 * Support function for printAll()
1804 _printAll: function(exportType) {
1806 this.buffer.exportAllRows(this.exportBuffer.bind(this),this.exportFinish.bind(this,exportType));
1809 _printVisible: function(exportType) {
1811 this.exportBuffer(this.buffer.visibleRows(),0);
1812 this.exportFinish(exportType);
1816 * Send all rows to print/export window
1818 exportBuffer: function(rows,startPos) {
1819 var r,c,v,col,exportText;
1820 Rico.writeDebugMsg("exportBuffer: "+rows.length+" rows");
1822 var totalcnt=startPos || 0;
1823 for (c=0; c<this.columns.length; c++) {
1824 if (this.columns[c].visible) tdstyle[c]=this.exportStyle(this.columns[c].cell(0)); // assumes row 0 style applies to all rows
1826 for(r=0; r < rows.length; r++) {
1828 for (c=0; c<this.columns.length; c++) {
1829 if (!this.columns[c].visible) continue;
1830 col=this.columns[c];
1831 col.expStyle=tdstyle[c];
1832 v=col._export(rows[r][c],rows[r]);
1833 if (v.match(/<span\s+class=(['"]?)ricolookup\1>(.*)<\/span>/i))
1834 v=RegExp.leftContext;
1835 if (v=='') v=' ';
1836 exportText+="<td style='"+col.expStyle+"'>"+v+"</td>";
1838 this.exportRows.push(exportText);
1840 if (totalcnt % 10 == 0) window.status=RicoTranslate.getPhraseById('exportStatus',totalcnt);
1847 Rico.TableColumn.prototype =
1848 /** @lends Rico.TableColumn# */
1851 * Implements a LiveGrid column. Also contains static properties used by SimpleGrid columns.
1852 * @extends Rico.TableColumnBase
1855 initialize: function(liveGrid,colIdx,hdrInfo,tabIdx) {
1856 Object.extend(this, new Rico.TableColumnBase());
1857 this.baseInit(liveGrid,colIdx,hdrInfo,tabIdx);
1858 if (typeof this.format.type!='string') this.format.type='raw';
1859 if (typeof this.isNullable!='boolean') this.isNullable = /number|date/.test(this.format.type);
1860 this.isText = /raw|text|showTags/.test(this.format.type);
1861 Rico.writeDebugMsg(" sortable="+this.sortable+" filterable="+this.filterable+" hideable="+this.hideable+" isNullable="+this.isNullable+' isText='+this.isText);
1862 this.fixHeaders(this.liveGrid.tableId, this.options.hdrIconsFirst);
1863 if (this.format.control) {
1864 // copy all properties/methods that start with '_'
1865 if (typeof this.format.control=='string') {
1866 this.format.control=eval(this.format.control);
1868 for (var property in this.format.control) {
1869 if (property.charAt(0)=='_') {
1870 Rico.writeDebugMsg("Copying control property "+property);
1871 this[property] = this.format.control[property];
1875 if (this['format_'+this.format.type])
1876 this._format=this['format_'+this.format.type].bind(this);
1880 * Sorts the column in ascending order
1882 sortAsc: function() {
1883 this.setColumnSort(Rico.TableColumn.SORT_ASC);
1887 * Sorts the column in descending order
1889 sortDesc: function() {
1890 this.setColumnSort(Rico.TableColumn.SORT_DESC);
1894 * Sorts the column in the specified direction
1895 * @param direction must be one of Rico.TableColumn.UNSORTED, .SORT_ASC, or .SORT_DESC
1897 setColumnSort: function(direction) {
1898 this.liveGrid.clearSort();
1899 this.setSorted(direction);
1900 if (this.liveGrid.options.saveColumnInfo.sort)
1901 this.liveGrid.setCookie();
1902 if (this.options.sortHandler)
1903 this.options.sortHandler();
1907 * @returns true if this column is allowed to be sorted
1909 isSortable: function() {
1910 return this.sortable;
1914 * @returns true if this column is currently sorted
1916 isSorted: function() {
1917 return this.currentSort != Rico.TableColumn.UNSORTED;
1921 * @returns Rico.TableColumn.UNSORTED, .SORT_ASC, or .SORT_DESC
1923 getSortDirection: function() {
1924 return this.currentSort;
1928 * toggle the sort sequence for this column
1930 toggleSort: function() {
1931 if (this.liveGrid.buffer && this.liveGrid.buffer.totalRows==0) return;
1932 if (this.currentSort == Rico.TableColumn.SORT_ASC)
1939 * Flags that this column is not sorted
1941 setUnsorted: function() {
1942 this.setSorted(Rico.TableColumn.UNSORTED);
1946 * Flags that this column is sorted, but doesn't actually carry out the sort
1947 * @param direction must be one of Rico.TableColumn.UNSORTED, .SORT_ASC, or .SORT_DESC
1949 setSorted: function(direction) {
1950 this.currentSort = direction;
1954 * @returns true if this column is allowed to be filtered
1956 canFilter: function() {
1957 return this.filterable;
1961 * @returns a textual representation of how this column is filtered
1963 getFilterText: function() {
1965 for (var i=0; i<this.filterValues.length; i++) {
1966 var v=this.filterValues[i];
1967 if (typeof(v)=='string' && v.match(/<span\s+class=(['"]?)ricolookup\1>(.*)<\/span>/i)) v=RegExp.leftContext;
1968 vals.push(v=='' ? RicoTranslate.getPhraseById('filterBlank') : v);
1970 switch (this.filterOp) {
1971 case 'EQ': return '= '+(vals[0]);
1972 case 'NE': return RicoTranslate.getPhraseById('filterNot',vals.join(', '));
1973 case 'LE': return '<= '+vals[0];
1974 case 'GE': return '>= '+vals[0];
1975 case 'LIKE': return RicoTranslate.getPhraseById('filterLike',vals[0]);
1976 case 'NULL': return RicoTranslate.getPhraseById('filterEmpty');
1977 case 'NOTNULL': return RicoTranslate.getPhraseById('filterNotEmpty');
1983 * @returns returns the query string representation of the filter
1985 getFilterQueryParm: function() {
1986 if (this.filterType == Rico.TableColumn.UNFILTERED) return '';
1987 var retval='&f['+this.index+'][op]='+this.filterOp;
1988 retval+='&f['+this.index+'][len]='+this.filterValues.length;
1989 for (var i=0; i<this.filterValues.length; i++) {
1990 retval+='&f['+this.index+']['+i+']='+escape(this.filterValues[i]);
1996 * removes the filter from this column
1998 setUnfiltered: function(skipHandler) {
1999 this.filterType = Rico.TableColumn.UNFILTERED;
2000 if (this.liveGrid.options.saveColumnInfo.filter)
2001 this.liveGrid.setCookie();
2002 if (this.removeFilterFunc)
2003 this.removeFilterFunc();
2004 if (this.options.filterHandler && !skipHandler)
2005 this.options.filterHandler();
2008 setFilterEQ: function() {
2009 this.setUserFilter('EQ');
2011 setFilterNE: function() {
2012 this.setUserFilter('NE');
2014 addFilterNE: function() {
2015 this.filterValues.push(this.userFilter);
2016 if (this.liveGrid.options.saveColumnInfo.filter)
2017 this.liveGrid.setCookie();
2018 if (this.options.filterHandler)
2019 this.options.filterHandler();
2021 setFilterGE: function() { this.setUserFilter('GE'); },
2022 setFilterLE: function() { this.setUserFilter('LE'); },
2023 setFilterKW: function(keyword) {
2024 if (keyword!='' && keyword!=null) {
2025 this.setFilter('LIKE',keyword,Rico.TableColumn.USERFILTER);
2027 this.setUnfiltered(false);
2031 setUserFilter: function(relop) {
2032 this.setFilter(relop,this.userFilter,Rico.TableColumn.USERFILTER);
2035 setSystemFilter: function(relop,filter) {
2036 this.setFilter(relop,filter,Rico.TableColumn.SYSTEMFILTER);
2039 setFilter: function(relop,filter,type,removeFilterFunc) {
2040 this.filterValues = [filter];
2041 this.filterType = type;
2042 this.filterOp = relop;
2043 if (type == Rico.TableColumn.USERFILTER && this.liveGrid.options.saveColumnInfo.filter)
2044 this.liveGrid.setCookie();
2045 this.removeFilterFunc=removeFilterFunc;
2046 if (this.options.filterHandler)
2047 this.options.filterHandler();
2050 isFiltered: function() {
2051 return this.filterType == Rico.TableColumn.USERFILTER;
2054 filterChange: function(e) {
2055 var selbox=Event.element(e);
2056 if (selbox.value==this.liveGrid.options.FilterAllToken)
2057 this.setUnfiltered();
2059 this.setFilter('EQ',selbox.value,Rico.TableColumn.USERFILTER,function() {selbox.selectedIndex=0;});
2062 filterClear: function(e,txtbox) {
2064 this.setUnfiltered();
2067 filterKeypress: function(e) {
2068 var txtbox=Event.element(e);
2069 if (typeof this.lastKeyFilter != 'string') this.lastKeyFilter='';
2070 if (this.lastKeyFilter==txtbox.value) return;
2072 Rico.writeDebugMsg("filterKeypress: "+this.index+' '+v);
2073 this.lastKeyFilter=v;
2074 if (v=='' || v=='*')
2075 this.setUnfiltered();
2077 this.setFilter('LIKE', v, Rico.TableColumn.USERFILTER, function() {txtbox.value='';});
2081 format_text: function(v) {
2082 if (typeof v!='string')
2085 return v.stripTags();
2088 format_showTags: function(v) {
2089 if (typeof v!='string')
2092 return v.replace(/&/g, '&').replace(/</g,'<').replace(/>/g,'>');
2095 format_number: function(v) {
2096 if (typeof v=='undefined' || v=='' || v==null)
2099 return v.formatNumber(this.format);
2102 format_datetime: function(v) {
2103 if (typeof v=='undefined' || v=='' || v==null)
2107 if (!d.setISO8601(v)) return v;
2108 return (this.format.prefix || '')+d.formatDate(this.format.dateFmt || 'translateDateTime')+(this.format.suffix || '');
2112 // converts GMT/UTC to local time
2113 format_UTCasLocalTime: function(v) {
2114 if (typeof v=='undefined' || v=='' || v==null)
2118 if (!d.setISO8601(v,-d.getTimezoneOffset())) return v;
2119 return (this.format.prefix || '')+d.formatDate(this.format.dateFmt || 'translateDateTime')+(this.format.suffix || '');
2123 format_date: function(v) {
2124 if (typeof v=='undefined' || v==null || v=='')
2128 if (!d.setISO8601(v)) return v;
2129 return (this.format.prefix || '')+d.formatDate(this.format.dateFmt || 'translateDate')+(this.format.suffix || '');
2133 fixHeaders: function(prefix, iconsfirst) {
2134 if (this.sortable) {
2135 switch (this.options.headingSort) {
2137 var a=RicoUtil.wrapChildren(this.hdrCellDiv,'ricoSort',undefined,'a');
2138 a.href = "javascript:void(0)";
2139 a.onclick = this.toggleSort.bindAsEventListener(this);
2142 this.hdrCellDiv.onclick = this.toggleSort.bindAsEventListener(this);
2146 this.imgFilter = document.createElement('img');
2147 this.imgFilter.style.display='none';
2148 this.imgFilter.src=Rico.imgDir+this.options.filterImg;
2149 this.imgFilter.className='ricoLG_HdrIcon';
2150 this.imgSort = document.createElement('img');
2151 this.imgSort.style.display='none';
2152 this.imgSort.src=Rico.imgDir+this.options.sortAscendImg;
2153 this.imgSort.className='ricoLG_HdrIcon';
2155 this.hdrCellDiv.insertBefore(this.imgSort,this.hdrCellDiv.firstChild);
2156 this.hdrCellDiv.insertBefore(this.imgFilter,this.hdrCellDiv.firstChild);
2158 this.hdrCellDiv.appendChild(this.imgFilter);
2159 this.hdrCellDiv.appendChild(this.imgSort);
2161 if (!this.format.filterUI) {
2162 Event.observe(this.imgFilter, 'click', this.filterClick.bindAsEventListener(this), false);
2166 filterClick: function(e) {
2167 if (this.filterType==Rico.TableColumn.USERFILTER && this.filterOp=='LIKE') {
2168 this.liveGrid.openKeyword(this.index);
2172 getValue: function(windowRow) {
2173 return this.liveGrid.buffer.getWindowValue(windowRow,this.index);
2176 getBufferCell: function(windowRow) {
2177 return this.liveGrid.buffer.getWindowCell(windowRow,this.index);
2180 getBufferAttr: function(windowRow) {
2181 return this.liveGrid.buffer.getWindowAttr(windowRow,this.index);
2184 setValue: function(windowRow,newval) {
2185 this.liveGrid.buffer.setWindowValue(windowRow,this.index,newval);
2188 _format: function(v) {
2192 _display: function(v,gridCell) {
2193 gridCell.innerHTML=this._format(v);
2196 _export: function(v) {
2197 return this._format(v);
2200 displayValue: function(windowRow) {
2201 var bufCell=this.getBufferCell(windowRow);
2202 if (bufCell==null) {
2203 this.clearCell(windowRow);
2206 var gridCell=this.cell(windowRow);
2207 this._display(bufCell,gridCell,windowRow);
2208 var acceptAttr=this.liveGrid.buffer.options.acceptAttr;
2209 if (acceptAttr.length==0) return;
2210 var bufAttr=this.getBufferAttr(windowRow);
2211 if (bufAttr==null) return;
2212 for (var k=0; k<acceptAttr.length; k++) {
2213 bufAttr=bufAttr['_'+acceptAttr[k]] || '';
2214 switch (acceptAttr[k]) {
2215 case 'style': gridCell.style.cssText=bufAttr; break;
2216 case 'class': gridCell.className=bufAttr; break;
2217 default: gridCell['_'+acceptAttr[k]]=bufAttr; break;
2224 Rico.includeLoaded('ricoLiveGrid.js');