/* * (c) 2005-2009 Richard Cowin (http://openrico.org) * (c) 2005-2009 Matt Brown (http://dowdybrown.com) * * Rico is licensed under the Apache License, Version 2.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or implied. See the License for the specific language governing permissions * and limitations under the License. */ if(typeof Rico=='undefined') throw("LiveGrid requires the Rico JavaScript framework"); if(typeof RicoUtil=='undefined') throw("LiveGrid requires the RicoUtil Library"); if(typeof RicoTranslate=='undefined') throw("LiveGrid requires the RicoTranslate Library"); if(typeof Rico.TableColumn=='undefined') throw("LiveGrid requires ricoGridCommon.js"); /** @namespace */ Rico.Buffer = {}; Rico.Buffer.Base = Class.create( /** @lends Rico.Buffer.Base# */ { /** * @class Defines the static buffer class (no AJAX). * Loads buffer with data that already exists in the document as an HTML table or passed via javascript. * Also serves as a base class for AJAX-enabled buffers. * @constructs */ initialize: function(dataTable, options) { this.clear(); this.updateInProgress = false; this.lastOffset = 0; this.rcvdRowCount = false; // true if an eof element was included in the last xml response this.foundRowCount = false; // true if an xml response is ever received with eof true this.totalRows = 0; this.rowcntContent = ""; this.rcvdOffset = -1; this.options = { fixedHdrRows : 0, canFilter : true, // does buffer object support filtering? isEncoded : true, // is the data received via ajax html encoded? acceptAttr : [] // attributes that can be copied from original/ajax data (e.g. className, style, id) }; Object.extend(this.options, options || {}); if (dataTable) { this.loadRowsFromTable(dataTable,this.options.fixedHdrRows); } else { this.clear(); } }, registerGrid: function(liveGrid) { this.liveGrid = liveGrid; }, setTotalRows: function( newTotalRows ) { if (typeof(newTotalRows)!='number') newTotalRows=this.size; if (this.totalRows == newTotalRows) return; this.totalRows = newTotalRows; if (this.liveGrid) { Rico.writeDebugMsg("setTotalRows, newTotalRows="+newTotalRows); if (this.liveGrid.sizeTo=='data') this.liveGrid.resizeWindow(); this.liveGrid.updateHeightDiv(); } }, loadRowsFromTable: function(tableElement,firstRow) { var newRows = new Array(); var trs = tableElement.getElementsByTagName("tr"); for ( var i=firstRow || 0; i < trs.length; i++ ) { var row = new Array(); var cells = trs[i].getElementsByTagName("td"); for ( var j=0; j < cells.length ; j++ ) row[j]=cells[j].innerHTML; newRows.push( row ); } this.loadRows(newRows); }, loadRowsFromArray: function(array2D) { for ( var i=0; i < array2D.length; i++ ) { for ( var j=0; j < array2D[i].length ; j++ ) { array2D[i][j]=array2D[i][j].toString(); } } this.loadRows(array2D); }, loadRows: function(jstable) { this.baseRows = jstable; this.startPos = 0; this.size = this.baseRows.length; }, dom2jstable: function(rowsElement) { Rico.writeDebugMsg('dom2jstable: encoded='+this.options.isEncoded); var newRows = new Array(); var trs = rowsElement.getElementsByTagName("tr"); for ( var i=0; i < trs.length; i++ ) { var row = new Array(); var cells = trs[i].getElementsByTagName("td"); for ( var j=0; j < cells.length ; j++ ) row[j]=RicoUtil.getContentAsString(cells[j],this.options.isEncoded); newRows.push( row ); } return newRows; }, dom2jstableAttr: function(rowsElement,firstRow) { var acceptAttr=this.options.acceptAttr; Rico.writeDebugMsg("dom2jstableAttr start, # attr="+acceptAttr.length); var newRows = new Array(); var trs = rowsElement.getElementsByTagName("tr"); for ( var i=firstRow || 0; i < trs.length; i++ ) { var row = new Array(); var cells = trs[i].getElementsByTagName("td"); for ( var j=0; j < cells.length ; j++ ) { row[j]={}; for (var k=0; k= this.startPos) && (lastRow <= this.endPos()); // && (this.size != 0); }, endPos: function() { return this.startPos + this.rows.length; }, fetch: function(offset) { Rico.writeDebugMsg('fetch '+this.liveGrid.tableId+': offset='+offset); this.applyFilters(); this.setTotalRows(); this.rcvdRowCount = true; this.foundRowCount = true; if (offset < 0) offset=0; this.liveGrid.refreshContents(offset); return; }, exportAllRows: function(populate,finish) { populate(this.getRows(0,this.totalRows)); finish(); }, /** * @return a 2D array of buffer data representing the rows that are currently visible on the grid */ visibleRows: function() { return this.rows.slice(this.windowStart,this.windowEnd); }, setWindow: function(start, count) { this.windowStart = start - this.startPos; // position in the buffer of first visible row this.windowEnd = Math.min(this.windowStart + count,this.size); // position in the buffer of last visible row containing data+1 this.windowPos = start; // position in the dataset of first visible row }, /** * @return true if bufRow is currently visible in the grid */ isVisible: function(bufRow) { return bufRow < this.rows.length && bufRow >= this.windowStart && bufRow < this.windowEnd; }, /** * takes a window row index and returns the corresponding buffer row index */ bufferRow: function(windowRow) { return this.windowStart+windowRow; }, /** * @return buffer cell at the specified visible row/col index */ getWindowCell: function(windowRow,col) { var bufrow=this.bufferRow(windowRow); return this.isVisible(bufrow) && col < this.rows[bufrow].length ? this.rows[bufrow][col] : null; }, getWindowAttr: function(windowRow,col) { var bufrow=this.bufferRow(windowRow); return this.attr && this.isVisible(bufrow) && col < this.attr[bufrow].length ? this.attr[bufrow][col] : null; }, getWindowValue: function(windowRow,col) { return this.getWindowCell(windowRow,col); }, setWindowValue: function(windowRow,col,newval) { var bufrow=this.bufferRow(windowRow); if (bufrow >= this.windowEnd) return false; return this.setValue(bufrow,col,newval); }, getCell: function(bufRow,col) { return bufRow < this.size ? this.rows[bufRow][col] : null; }, getValue: function(bufRow,col) { return this.getCell(bufRow,col); }, setValue: function(bufRow,col,newval,newstyle) { if (bufRow>=this.size) return false; if (!this.rows[bufRow][col]) this.rows[bufRow][col]={}; this.rows[bufRow][col]=newval; if (typeof newstyle=='string') this.rows[bufRow][col]._style=newstyle; this.rows[bufRow][col].modified=true; return true; }, getRows: function(start, count) { var begPos = start - this.startPos; var endPos = Math.min(begPos + count,this.size); var results = new Array(); for ( var i=begPos; i < endPos; i++ ) { results.push(this.rows[i]); } return results; }, applyFilters: function() { var newRows=[],re=[]; var r,c,n,i,showRow,filtercnt; var cols=this.liveGrid.columns; for (n=0,filtercnt=0; n=this.nan2zero(c.filterValues[0]); else showRow=this.baseRows[r][n]>=c.filterValues[0]; break; case 'NULL': showRow=this.baseRows[r][n]==''; break; case 'NOTNULL': showRow=this.baseRows[r][n]!=''; break; } } if (showRow) newRows.push(this.baseRows[r]); } this.rows = newRows; } this.rowcntContent = this.size = this.rows.length; } }); // Rico.LiveGrid ----------------------------------------------------- Rico.LiveGrid = Class.create( /** * @lends Rico.LiveGrid# * @property tableId id string for this grid * @property options the options object passed to the constructor extended with defaults * @property buffer the buffer object containing the data for this grid * @property columns array of {@link Rico.TableColumn} objects */ { /** * @class Buffered LiveGrid component * @extends Rico.GridCommon * @constructs */ initialize: function( tableId, buffer, options ) { Object.extend(this, new Rico.GridCommon); Object.extend(this, new Rico.LiveGridMethods); this.baseInit(); this.tableId = tableId; this.buffer = buffer; Rico.setDebugArea(tableId+"_debugmsgs"); // if used, this should be a textarea Object.extend(this.options, { 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) frozenColumns : 0, offset : 0, // first row to be displayed prefetchBuffer : true, // load table on page load? minPageRows : 2, maxPageRows : 50, canSortDefault : true, // can be overridden in the column specs canFilterDefault : buffer.options.canFilter, // can be overridden in the column specs canHideDefault : true, // can be overridden in the column specs // highlight & selection parameters highlightElem : 'none',// what gets highlighted/selected (cursorRow, cursorCell, menuRow, menuCell, selection, or none) highlightSection : 3, // which section gets highlighted (frozen=1, scrolling=2, all=3, none=0) highlightMethod : 'class', // outline, class, both (outline is less CPU intensive on the client) highlightClass : 'ricoLG_selection', // export/print parameters maxPrint : 1000, // max # of rows that can be printed/exported, 0=disable print/export feature // heading parameters headingSort : 'link', // link: make headings a link that will sort column, hover: make headings a hoverset, none: events on headings are disabled hdrIconsFirst : true, // true: put sort & filter icons before header text, false: after sortAscendImg : 'sort_asc.gif', sortDescendImg : 'sort_desc.gif', filterImg : 'filtercol.gif' }); // other options: // sortCol: initial sort column this.options.sortHandler = this.sortHandler.bind(this); this.options.filterHandler = this.filterHandler.bind(this); this.options.onRefreshComplete = this.bookmarkHandler.bind(this); this.options.rowOverHandler = this.rowMouseOver.bindAsEventListener(this); this.options.mouseDownHandler = this.selectMouseDown.bindAsEventListener(this); this.options.mouseOverHandler = this.selectMouseOver.bindAsEventListener(this); this.options.mouseUpHandler = this.selectMouseUp.bindAsEventListener(this); Object.extend(this.options, options || {}); switch (typeof this.options.visibleRows) { case 'string': this.sizeTo=this.options.visibleRows; switch (this.options.visibleRows) { case 'data': this.options.visibleRows=-2; break; case 'body': this.options.visibleRows=-3; break; case 'parent': this.options.visibleRows=-4; break; default: this.options.visibleRows=-1; break; } break; case 'number': switch (this.options.visibleRows) { case -1: this.sizeTo='window'; break; case -2: this.sizeTo='data'; break; case -3: this.sizeTo='body'; break; case -4: this.sizeTo='parent'; break; default: this.sizeTo='fixed'; break; } break; default: this.sizeTo='window'; this.options.visibleRows=-1; break; } this.highlightEnabled=this.options.highlightSection>0; this.pageSize=0; this.createTables(); if (this.headerColCnt==0) { alert('ERROR: no columns found in "'+this.tableId+'"'); return; } this.createColumnArray('TableColumn'); if (this.options.headingSort=='hover') this.createHoverSet(); this.bookmark=$(this.tableId+"_bookmark"); this.sizeDivs(); var filterUIrow=this.buffer.options.canFilter ? this.options.FilterLocation : false; if (typeof(filterUIrow)=='number' && filterUIrow<0) filterUIrow=this.addHeadingRow(); this.createDataCells(this.options.visibleRows); if (this.pageSize == 0) return; this.buffer.registerGrid(this); if (this.buffer.setBufferSize) this.buffer.setBufferSize(this.pageSize); this.scrollTimeout = null; this.lastScrollPos = 0; this.attachMenuEvents(); // preload the images... new Image().src = Rico.imgDir+this.options.filterImg; new Image().src = Rico.imgDir+this.options.sortAscendImg; new Image().src = Rico.imgDir+this.options.sortDescendImg; Rico.writeDebugMsg("images preloaded"); this.setSortUI( this.options.sortCol, this.options.sortDir ); this.setImages(); if (this.listInvisible().length==this.columns.length) this.columns[0].showColumn(); this.sizeDivs(); this.scrollDiv.style.display=""; if (this.buffer.totalRows>0) this.updateHeightDiv(); if (this.options.prefetchBuffer) { if (this.bookmark) this.bookmark.innerHTML = RicoTranslate.getPhraseById('bookmarkLoading'); if (this.options.canFilterDefault && this.options.getQueryParms) this.checkForFilterParms(); this.buffer.fetch(this.options.offset); } if (typeof(filterUIrow)=='number') this.createFilters(filterUIrow); this.scrollEventFunc=this.handleScroll.bindAsEventListener(this); this.wheelEventFunc=this.handleWheel.bindAsEventListener(this); this.wheelEvent=(Prototype.Browser.IE || Prototype.Browser.Opera || Prototype.Browser.WebKit) ? 'mousewheel' : 'DOMMouseScroll'; if (this.options.offset && this.options.offset < this.buffer.totalRows) setTimeout(this.scrollToRow.bind(this,this.options.offset),50); // Safari requires a delay this.pluginScroll(); this.setHorizontalScroll(); if (this.options.windowResize) setTimeout(this.pluginWindowResize.bind(this),100); } }); Rico.LiveGridMethods = function() {}; Rico.LiveGridMethods.prototype = { /** @lends Rico.LiveGrid# */ createHoverSet: function() { var hdrs=[]; for( var c=0; c < this.headerColCnt; c++ ) { if (this.columns[c].sortable) { hdrs.push(this.columns[c].hdrCellDiv); } } this.hoverSet = new Rico.HoverSet(hdrs); }, checkForFilterParms: function() { var s=window.location.search; if (s.charAt(0)=='?') s=s.substring(1); var pairs = s.split('&'); for (var i=0; i 0) { Rico.writeDebugMsg("Data provider returned an error:\n"+RicoUtil.getContentAsString(error[0],this.buffer.isEncoded)); alert(RicoTranslate.getPhraseById("requestError",RicoUtil.getContentAsString(error[0],this.buffer.isEncoded))); return false; } response=response.getElementsByTagName('response')[0]; var rowsElement = response.getElementsByTagName('rows')[0]; //var colnum = rowsElement.getAttribute("distinct"); var col=this.columns[parseInt(colnum,10)]; var rows = this.buffer.dom2jstable(rowsElement); var c0,c1,opt,v, field=$(this.filterId(colnum)); if (col.filterType==Rico.TableColumn.USERFILTER && col.filterOp=='EQ') v=col.filterValues[0]; Rico.writeDebugMsg('filterValuesUpdate: col='+colnum+' rows='+rows.length); for (var i=0; i0) { c0=c1=rows[i][0]; if (c0.match(/(.*)<\/span>/i)) { c1=RegExp.leftContext; } opt=RicoUtil.addSelectOption(field,c0,c1 || RicoTranslate.getPhraseById("filterBlank")); if (col.filterType==Rico.TableColumn.USERFILTER && c0==v) opt.selected=true; } } Event.observe(field,'change',col.filterChange.bindAsEventListener(col),false); return true; }, unplugHighlightEvents: function() { var s=this.options.highlightSection; if (s & 1) this.detachHighlightEvents(this.tbody[0]); if (s & 2) this.detachHighlightEvents(this.tbody[1]); }, /** * place panel names on first row of grid header (used by LiveGridForms) */ insertPanelNames: function(r,start,limit,cellClass) { Rico.writeDebugMsg('insertPanelNames: start='+start+' limit='+limit); r.className='ricoLG_hdg'; var lastIdx=-1, span, newCell=null, spanIdx=0; for( var c=start; c < limit; c++ ) { if (lastIdx == this.options.columnSpecs[c].panelIdx) { span++; } else { if (newCell) newCell.colSpan=span; newCell = r.insertCell(-1); if (cellClass) newCell.className=cellClass; span=1; lastIdx=this.options.columnSpecs[c].panelIdx; newCell.innerHTML=this.options.panels[lastIdx]; } } if (newCell) newCell.colSpan=span; }, /** * create grid header for table i (if none was provided) */ createHdr: function(i,start,limit) { Rico.writeDebugMsg('createHdr: i='+i+' start='+start+' limit='+limit); var mainRow = this.thead[i].insertRow(-1); mainRow.id=this.tableId+'_tab'+i+'h_main'; mainRow.className='ricoLG_hdg'; for( var c=start; c < limit; c++ ) { var newCell = mainRow.insertCell(-1); newCell.innerHTML=this.options.columnSpecs[c].Hdg; } }, /** * move header cells in original table to grid */ loadHdrSrc: function(hdrSrc) { var i,h,c,r,newrow,cells; Rico.writeDebugMsg('loadHdrSrc start'); for (i=0; i<2; i++) { for (r=0; r 0; c++) this.thead[c 0; c++) { if (cells[0].className=='ricoFrozen') { if (r==this.headerRowIdx) this.options.frozenColumns=c+1; } else { h=1; } this.thead[h].rows[r].appendChild(cells[0]); } } } Rico.writeDebugMsg('loadHdrSrc end'); }, /** * Size div elements */ sizeDivs: function() { Rico.writeDebugMsg('sizeDivs: '+this.tableId); //this.cancelMenu(); this.unhighlight(); this.baseSizeDivs(); var firstVisible=this.firstVisible(); if (this.pageSize == 0 || firstVisible < 0) return; var totRowHt=this.columns[firstVisible].dataColDiv.offsetHeight; this.rowHeight = Math.round(totRowHt/this.pageSize); var scrHt=this.dataHt; if (this.scrWi>0 || Prototype.Browser.IE || Prototype.Browser.WebKit) scrHt+=this.options.scrollBarWidth; this.scrollDiv.style.height=scrHt+'px'; this.innerDiv.style.width=(this.scrWi-this.options.scrollBarWidth+1)+'px'; this.resizeDiv.style.height=this.frozenTabs.style.height=this.innerDiv.style.height=(this.hdrHt+this.dataHt+1)+'px'; Rico.writeDebugMsg('sizeDivs scrHt='+scrHt+' innerHt='+this.innerDiv.style.height+' rowHt='+this.rowHeight+' pageSize='+this.pageSize); var pad=(this.scrWi-this.scrTabWi < this.options.scrollBarWidth) ? 2 : 0; this.shadowDiv.style.width=(this.scrTabWi+pad)+'px'; this.outerDiv.style.height=(this.hdrHt+scrHt)+'px'; this.setHorizontalScroll(); }, setHorizontalScroll: function() { var scrleft=this.scrollDiv.scrollLeft; this.scrollTabs.style.left=(-scrleft)+'px'; }, remainingHt: function() { var tabHt; var winHt=RicoUtil.windowHeight(); var margin=Prototype.Browser.IE ? 15 : 10; // if there is a horizontal scrollbar take it into account if (!Prototype.Browser.IE && window.frameElement && window.frameElement.scrolling=='yes' && this.sizeTo!='parent') margin+=this.options.scrollBarWidth; switch (this.sizeTo) { case 'window': case 'data': var divPos=Position.page(this.outerDiv); tabHt=Math.max(this.tabs[0].offsetHeight,this.tabs[1].offsetHeight); Rico.writeDebugMsg("remainingHt, winHt="+winHt+' tabHt='+tabHt+' gridY='+divPos[1]); return winHt-divPos[1]-tabHt-this.options.scrollBarWidth-margin; // allow for scrollbar and some margin case 'parent': var offset=this.offsetFromParent(this.outerDiv); tabHt=Math.max(this.tabs[0].offsetHeight,this.tabs[1].offsetHeight); if (Prototype.Browser.IE) Element.hide(this.outerDiv); var parentHt=this.outerDiv.parentNode.offsetHeight; if (Prototype.Browser.IE) Element.show(this.outerDiv); Rico.writeDebugMsg("remainingHt, parentHt="+parentHt+' gridY='+offset+' winHt='+winHt+' tabHt='+tabHt); return parentHt - tabHt - offset - this.options.scrollBarWidth; case 'body': //Rico.writeDebugMsg("remainingHt, document.height="+document.height); //Rico.writeDebugMsg("remainingHt, body.offsetHeight="+document.body.offsetHeight); //Rico.writeDebugMsg("remainingHt, body.scrollHeight="+document.body.scrollHeight); //Rico.writeDebugMsg("remainingHt, documentElement.scrollHeight="+document.documentElement.scrollHeight); var bodyHt=Prototype.Browser.IE ? document.body.scrollHeight : document.body.offsetHeight; var remHt=winHt-bodyHt-margin; if (!Prototype.Browser.WebKit) remHt-=this.options.scrollBarWidth; Rico.writeDebugMsg("remainingHt, winHt="+winHt+' pageHt='+bodyHt+' remHt='+remHt); return remHt; default: tabHt=Math.max(this.tabs[0].offsetHeight,this.tabs[1].offsetHeight); Rico.writeDebugMsg("remainingHt, winHt="+winHt+' tabHt='+tabHt); if (this.sizeTo.slice(-1)=='%') winHt*=parseFloat(this.sizeTo)/100.0; else if (this.sizeTo.slice(-2)=='px') winHt=parseInt(this.sizeTo,10); return winHt-tabHt-this.options.scrollBarWidth-margin; // allow for scrollbar and some margin } }, offsetFromParent: function(element) { var valueT = 0; var elParent=element.parentNode; do { //Rico.writeDebugMsg("offsetFromParent: "+element.tagName+' id='+element.id+' otop='+element.offsetTop); valueT += element.offsetTop || 0; element = element.offsetParent; if (!element || element==null) break; var p = Element.getStyle(element, 'position'); if (element.tagName=='BODY' || element.tagName=='HTML' || p=='absolute') return valueT-elParent.offsetTop; } while (element != elParent); return valueT; }, adjustPageSize: function() { var remHt=this.remainingHt(); Rico.writeDebugMsg('adjustPageSize remHt='+remHt+' lastRow='+this.lastRowPos); if (remHt > this.rowHeight) this.autoAppendRows(remHt); else if (remHt < 0 || this.sizeTo=='data') this.autoRemoveRows(-remHt); }, pluginWindowResize: function() { this.resizeWindowHandler=this.resizeWindow.bindAsEventListener(this); Event.observe(window, "resize", this.resizeWindowHandler, false); }, unplugWindowResize: function() { if (!this.resizeWindowHandler) return; Event.stopObserving(window,"resize", this.resizeWindowHandler, false); this.resizeWindowHandler=null; }, resizeWindow: function() { Rico.writeDebugMsg('resizeWindow '+this.tableId+' lastRow='+this.lastRowPos); if (this.resizeState=='finish') { Rico.writeDebugMsg('resizeWindow postponed'); this.resizeState='resize'; return; } if (!this.sizeTo || this.sizeTo=='fixed') { this.sizeDivs(); return; } if (this.sizeTo=='parent' && Element.getStyle(this.outerDiv.parentNode,'display') == 'none') return; var oldSize=this.pageSize; this.adjustPageSize(); if (this.pageSize > oldSize && this.buffer.totalRows>0) { this.isPartialBlank=true; var adjStart=this.adjustRow(this.lastRowPos); this.buffer.fetch(adjStart); } else if (this.pageSize < oldSize) { if (this.options.onRefreshComplete) this.options.onRefreshComplete(this.contentStartPos,this.contentStartPos+this.pageSize-1); // update bookmark } this.resizeState='finish'; setTimeout(this.finishResize.bind(this),20); Rico.writeDebugMsg('resizeWindow '+this.tableId+' complete. old size='+oldSize+' new size='+this.pageSize); }, finishResize: function() { this.sizeDivs(); this.updateHeightDiv(); if (this.resizeState=='resize') { this.resizeWindow(); } else { this.resizeState=''; } }, topOfLastPage: function() { return Math.max(this.buffer.totalRows-this.pageSize,0); }, updateHeightDiv: function() { var notdisp=this.topOfLastPage(); var ht = this.scrollDiv.clientHeight + this.rowHeight * notdisp; Rico.writeDebugMsg("updateHeightDiv, ht="+ht+' scrollDiv.clientHeight='+this.scrollDiv.clientHeight+' rowsNotDisplayed='+notdisp); this.shadowDiv.style.height=ht+'px'; }, autoRemoveRows: function(overage) { if (!this.rowHeight) return; var removeCnt=Math.ceil(overage / this.rowHeight); if (this.sizeTo=='data') removeCnt=Math.max(removeCnt,this.pageSize-this.buffer.totalRows); Rico.writeDebugMsg("autoRemoveRows overage="+overage+" removeCnt="+removeCnt); for (var i=0; i= this.buffer.endPos()); return idx; }, attachHighlightEvents: function(tBody) { switch (this.options.highlightElem) { case 'selection': Event.observe(tBody,"mousedown", this.options.mouseDownHandler, false); /** @ignore */ tBody.ondrag = function () { return false; }; /** @ignore */ tBody.onselectstart = function () { return false; }; break; case 'cursorRow': case 'cursorCell': Event.observe(tBody,"mouseover", this.options.rowOverHandler, false); break; } }, detachHighlightEvents: function(tBody) { switch (this.options.highlightElem) { case 'selection': Event.stopObserving(tBody,"mousedown", this.options.mouseDownHandler, false); tBody.ondrag = null; tBody.onselectstart = null; break; case 'cursorRow': case 'cursorCell': Event.stopObserving(tBody,"mouseover", this.options.rowOverHandler, false); break; } }, /** * @return array of objects containing row/col indexes (index values are relative to the start of the window) */ getVisibleSelection: function() { var cellList=[]; if (this.SelectIdxStart && this.SelectIdxEnd) { var r1=Math.max(Math.min(this.SelectIdxEnd.row,this.SelectIdxStart.row)-this.buffer.startPos,this.buffer.windowStart); var r2=Math.min(Math.max(this.SelectIdxEnd.row,this.SelectIdxStart.row)-this.buffer.startPos,this.buffer.windowEnd-1); var c1=Math.min(this.SelectIdxEnd.column,this.SelectIdxStart.column); var c2=Math.max(this.SelectIdxEnd.column,this.SelectIdxStart.column); //Rico.writeDebugMsg("getVisibleSelection "+r1+','+c1+' to '+r2+','+c2+' ('+this.SelectIdxStart.row+',startPos='+this.buffer.startPos+',windowPos='+this.buffer.windowPos+',windowEnd='+this.buffer.windowEnd+')'); for (var r=r1; r<=r2; r++) { for (var c=c1; c<=c2; c++) cellList.push({row:r-this.buffer.windowStart,column:c}); } } if (this.SelectCtrl) { for (var i=0; i=this.buffer.windowStart && this.SelectCtrl[i].row r2) { this.HideSelection(); return; } var c1=Math.min(this.SelectIdxEnd.column,this.SelectIdxStart.column); var c2=Math.max(this.SelectIdxEnd.column,this.SelectIdxStart.column); var top1=this.columns[c1].cell(r1-this.buffer.windowStart).offsetTop; var cell2=this.columns[c1].cell(r2-this.buffer.windowStart); var bottom2=cell2.offsetTop+cell2.offsetHeight; var left1=this.columns[c1].dataCell.offsetLeft; var left2=this.columns[c2].dataCell.offsetLeft; var right2=left2+this.columns[c2].dataCell.offsetWidth; //window.status='updateSelectOutline: '+r1+' '+r2+' top='+top1+' bot='+bottom2; this.highlightDiv[0].style.top=this.highlightDiv[3].style.top=this.highlightDiv[1].style.top=(this.hdrHt+top1-1) + 'px'; this.highlightDiv[2].style.top=(this.hdrHt+bottom2-1)+'px'; this.highlightDiv[3].style.left=(left1-2)+'px'; this.highlightDiv[0].style.left=this.highlightDiv[2].style.left=(left1-1)+'px'; this.highlightDiv[1].style.left=(right2-1)+'px'; this.highlightDiv[0].style.width=this.highlightDiv[2].style.width=(right2-left1-1) + 'px'; this.highlightDiv[1].style.height=this.highlightDiv[3].style.height=(bottom2-top1) + 'px'; //this.highlightDiv[0].style.right=this.highlightDiv[2].style.right=this.highlightDiv[1].style.right=()+'px'; //this.highlightDiv[2].style.bottom=this.highlightDiv[3].style.bottom=this.highlightDiv[1].style.bottom=(this.hdrHt+bottom2) + 'px'; for (var i=0; i<4; i++) this.highlightDiv[i].style.display=''; }, HideSelection: function() { var i; if (this.options.highlightMethod!='class') { for (i=0; i r2) return false; var c1=Math.min(this.SelectIdxEnd.column,this.SelectIdxStart.column); var c2=Math.max(this.SelectIdxEnd.column,this.SelectIdxStart.column); var curIdx=this.datasetIndex(cell); return (r1<=curIdx.row && curIdx.row<=r2 && c1<=curIdx.column && curIdx.column<=c2); }, highlightCell: function(cell) { Element.addClassName(cell,this.options.highlightClass); }, unhighlightCell: function(cell) { if (cell==null) return; Element.removeClassName(cell,this.options.highlightClass); }, selectRow: function(r) { for (var c=0; c= 0) { sortDirection=this.columns[colnum].getSortDirection(); } else { if (typeof sortDirection!='string') { sortDirection=Rico.TableColumn.SORT_ASC; } else { sortDirection=sortDirection.toUpperCase(); if (sortDirection != Rico.TableColumn.SORT_DESC) sortDirection=Rico.TableColumn.SORT_ASC; } switch (typeof columnNameOrNum) { case 'string': colnum=this.findColumnName(columnNameOrNum); break; case 'number': colnum=columnNameOrNum; break; } } if (typeof(colnum)!='number' || colnum < 0) return; this.clearSort(); this.columns[colnum].setSorted(sortDirection); this.buffer.sortBuffer(colnum); }, /** * clear sort flag on all columns */ clearSort: function() { for (var x=0;x startPos; var contentStartPos = viewPrecedesBuffer ? this.buffer.startPos: startPos; this.contentStartPos = contentStartPos+1; var contentEndPos = Math.min(this.buffer.startPos + this.buffer.size, startPos + this.pageSize); var onRefreshComplete = this.options.onRefreshComplete; if ((startPos + this.pageSize < this.buffer.startPos) || (this.buffer.startPos + this.buffer.size < startPos) || (this.buffer.size == 0)) { this.clearRows(); if (onRefreshComplete) onRefreshComplete(this.contentStartPos,contentEndPos); // update bookmark return; } Rico.writeDebugMsg('refreshContents: contentStartPos='+contentStartPos+' contentEndPos='+contentEndPos+' viewPrecedesBuffer='+viewPrecedesBuffer); var rowSize = contentEndPos - contentStartPos; this.buffer.setWindow(contentStartPos, rowSize ); var blankSize = this.pageSize - rowSize; var blankOffset = viewPrecedesBuffer ? 0: rowSize; var contentOffset = viewPrecedesBuffer ? blankSize: 0; for (var r=0; r < rowSize; r++) { //initialize what we have for (var c=0; c < this.columns.length; c++) this.columns[c].displayValue(r + contentOffset); } for (var i=0; i < blankSize; i++) // blank out the rest this.blankRow(i + blankOffset); if (this.options.highlightElem=='selection') this.ShowSelection(); this.isPartialBlank = blankSize > 0; this.lastRowPos = startPos; Rico.writeDebugMsg("refreshContents complete, startPos="+startPos); if (onRefreshComplete) onRefreshComplete(this.contentStartPos,contentEndPos); // update bookmark }, scrollToRow: function(rowOffset) { var p=this.rowToPixel(rowOffset); Rico.writeDebugMsg("scrollToRow, rowOffset="+rowOffset+" pixel="+p); this.scrollDiv.scrollTop = p; // this causes a scroll event if ( this.options.onscroll ) this.options.onscroll( this, rowOffset ); }, scrollUp: function() { this.moveRelative(-1); }, scrollDown: function() { this.moveRelative(1); }, pageUp: function() { this.moveRelative(-this.pageSize); }, pageDown: function() { this.moveRelative(this.pageSize); }, adjustRow: function(rowOffset) { var notdisp=this.topOfLastPage(); if (notdisp == 0 || !rowOffset) return 0; return Math.min(notdisp,rowOffset); }, rowToPixel: function(rowOffset) { return this.adjustRow(rowOffset) * this.rowHeight; }, /** * @returns row to display at top of scroll div */ pixeltorow: function(p) { var notdisp=this.topOfLastPage(); if (notdisp == 0) return 0; var prow=parseInt(p/this.rowHeight,10); return Math.min(notdisp,prow); }, moveRelative: function(relOffset) { var newoffset=Math.max(this.scrollDiv.scrollTop+relOffset*this.rowHeight,0); newoffset=Math.min(newoffset,this.scrollDiv.scrollHeight); //Rico.writeDebugMsg("moveRelative, newoffset="+newoffset); this.scrollDiv.scrollTop=newoffset; }, pluginScroll: function() { if (this.scrollPluggedIn) return; Rico.writeDebugMsg("pluginScroll: wheelEvent="+this.wheelEvent); Event.observe(this.scrollDiv,"scroll",this.scrollEventFunc, false); for (var t=0; t<2; t++) Event.observe(this.tabs[t],this.wheelEvent,this.wheelEventFunc, false); this.scrollPluggedIn=true; }, unplugScroll: function() { if (!this.scrollPluggedIn) return; Rico.writeDebugMsg("unplugScroll"); Event.stopObserving(this.scrollDiv,"scroll", this.scrollEventFunc , false); for (var t=0; t<2; t++) Event.stopObserving(this.tabs[t],this.wheelEvent,this.wheelEventFunc, false); this.scrollPluggedIn=false; }, handleWheel: function(e) { var delta = 0; if (e.wheelDelta) { if (Prototype.Browser.Opera) delta = e.wheelDelta/120; else if (Prototype.Browser.WebKit) delta = -e.wheelDelta/12; else delta = -e.wheelDelta/120; } else if (e.detail) { delta = e.detail/3; /* Mozilla/Gecko */ } if (delta) this.moveRelative(delta); Event.stop(e); return false; }, handleScroll: function(e) { if ( this.scrollTimeout ) clearTimeout( this.scrollTimeout ); this.setHorizontalScroll(); var scrtop=this.scrollDiv.scrollTop; var vscrollDiff = this.lastScrollPos-scrtop; if (vscrollDiff == 0.00) return; var newrow=this.pixeltorow(scrtop); if (newrow == this.lastRowPos && !this.isPartialBlank && !this.isBlank) return; var stamp1 = new Date(); //Rico.writeDebugMsg("handleScroll, newrow="+newrow+" scrtop="+scrtop); if (this.options.highlightElem=='selection') this.HideSelection(); this.buffer.fetch(newrow); if (this.options.onscroll) this.options.onscroll(this, newrow); this.scrollTimeout = setTimeout(this.scrollIdle.bind(this), 1200 ); this.lastScrollPos = this.scrollDiv.scrollTop; var stamp2 = new Date(); //Rico.writeDebugMsg("handleScroll, time="+(stamp2.getTime()-stamp1.getTime())); }, scrollIdle: function() { if ( this.options.onscrollidle ) this.options.onscrollidle(); }, printAll: function(exportType) { this.showMsg(RicoTranslate.getPhraseById('exportInProgress')); setTimeout(this._printAll.bind(this,exportType),10); // allow message to paint }, /** * Support function for printAll() */ _printAll: function(exportType) { this.exportStart(); this.buffer.exportAllRows(this.exportBuffer.bind(this),this.exportFinish.bind(this,exportType)); }, _printVisible: function(exportType) { this.exportStart(); this.exportBuffer(this.buffer.visibleRows(),0); this.exportFinish(exportType); }, /** * Send all rows to print/export window */ exportBuffer: function(rows,startPos) { var r,c,v,col,exportText; Rico.writeDebugMsg("exportBuffer: "+rows.length+" rows"); var tdstyle=[]; var totalcnt=startPos || 0; for (c=0; c(.*)<\/span>/i)) v=RegExp.leftContext; if (v=='') v=' '; exportText+=""+v+""; } this.exportRows.push(exportText); totalcnt++; if (totalcnt % 10 == 0) window.status=RicoTranslate.getPhraseById('exportStatus',totalcnt); } } }; Rico.TableColumn.prototype = /** @lends Rico.TableColumn# */ { /** * Implements a LiveGrid column. Also contains static properties used by SimpleGrid columns. * @extends Rico.TableColumnBase * @constructs */ initialize: function(liveGrid,colIdx,hdrInfo,tabIdx) { Object.extend(this, new Rico.TableColumnBase()); this.baseInit(liveGrid,colIdx,hdrInfo,tabIdx); if (typeof this.format.type!='string') this.format.type='raw'; if (typeof this.isNullable!='boolean') this.isNullable = /number|date/.test(this.format.type); this.isText = /raw|text|showTags/.test(this.format.type); Rico.writeDebugMsg(" sortable="+this.sortable+" filterable="+this.filterable+" hideable="+this.hideable+" isNullable="+this.isNullable+' isText='+this.isText); this.fixHeaders(this.liveGrid.tableId, this.options.hdrIconsFirst); if (this.format.control) { // copy all properties/methods that start with '_' if (typeof this.format.control=='string') { this.format.control=eval(this.format.control); } for (var property in this.format.control) { if (property.charAt(0)=='_') { Rico.writeDebugMsg("Copying control property "+property); this[property] = this.format.control[property]; } } } if (this['format_'+this.format.type]) this._format=this['format_'+this.format.type].bind(this); }, /** * Sorts the column in ascending order */ sortAsc: function() { this.setColumnSort(Rico.TableColumn.SORT_ASC); }, /** * Sorts the column in descending order */ sortDesc: function() { this.setColumnSort(Rico.TableColumn.SORT_DESC); }, /** * Sorts the column in the specified direction * @param direction must be one of Rico.TableColumn.UNSORTED, .SORT_ASC, or .SORT_DESC */ setColumnSort: function(direction) { this.liveGrid.clearSort(); this.setSorted(direction); if (this.liveGrid.options.saveColumnInfo.sort) this.liveGrid.setCookie(); if (this.options.sortHandler) this.options.sortHandler(); }, /** * @returns true if this column is allowed to be sorted */ isSortable: function() { return this.sortable; }, /** * @returns true if this column is currently sorted */ isSorted: function() { return this.currentSort != Rico.TableColumn.UNSORTED; }, /** * @returns Rico.TableColumn.UNSORTED, .SORT_ASC, or .SORT_DESC */ getSortDirection: function() { return this.currentSort; }, /** * toggle the sort sequence for this column */ toggleSort: function() { if (this.liveGrid.buffer && this.liveGrid.buffer.totalRows==0) return; if (this.currentSort == Rico.TableColumn.SORT_ASC) this.sortDesc(); else this.sortAsc(); }, /** * Flags that this column is not sorted */ setUnsorted: function() { this.setSorted(Rico.TableColumn.UNSORTED); }, /** * Flags that this column is sorted, but doesn't actually carry out the sort * @param direction must be one of Rico.TableColumn.UNSORTED, .SORT_ASC, or .SORT_DESC */ setSorted: function(direction) { this.currentSort = direction; }, /** * @returns true if this column is allowed to be filtered */ canFilter: function() { return this.filterable; }, /** * @returns a textual representation of how this column is filtered */ getFilterText: function() { var vals=[]; for (var i=0; i(.*)<\/span>/i)) v=RegExp.leftContext; vals.push(v=='' ? RicoTranslate.getPhraseById('filterBlank') : v); } switch (this.filterOp) { case 'EQ': return '= '+(vals[0]); case 'NE': return RicoTranslate.getPhraseById('filterNot',vals.join(', ')); case 'LE': return '<= '+vals[0]; case 'GE': return '>= '+vals[0]; case 'LIKE': return RicoTranslate.getPhraseById('filterLike',vals[0]); case 'NULL': return RicoTranslate.getPhraseById('filterEmpty'); case 'NOTNULL': return RicoTranslate.getPhraseById('filterNotEmpty'); } return '?'; }, /** * @returns returns the query string representation of the filter */ getFilterQueryParm: function() { if (this.filterType == Rico.TableColumn.UNFILTERED) return ''; var retval='&f['+this.index+'][op]='+this.filterOp; retval+='&f['+this.index+'][len]='+this.filterValues.length; for (var i=0; i/g,'>'); }, format_number: function(v) { if (typeof v=='undefined' || v=='' || v==null) return ' '; else return v.formatNumber(this.format); }, format_datetime: function(v) { if (typeof v=='undefined' || v=='' || v==null) return ' '; else { var d=new Date(); if (!d.setISO8601(v)) return v; return (this.format.prefix || '')+d.formatDate(this.format.dateFmt || 'translateDateTime')+(this.format.suffix || ''); } }, // converts GMT/UTC to local time format_UTCasLocalTime: function(v) { if (typeof v=='undefined' || v=='' || v==null) return ' '; else { var d=new Date(); if (!d.setISO8601(v,-d.getTimezoneOffset())) return v; return (this.format.prefix || '')+d.formatDate(this.format.dateFmt || 'translateDateTime')+(this.format.suffix || ''); } }, format_date: function(v) { if (typeof v=='undefined' || v==null || v=='') return ' '; else { var d=new Date(); if (!d.setISO8601(v)) return v; return (this.format.prefix || '')+d.formatDate(this.format.dateFmt || 'translateDate')+(this.format.suffix || ''); } }, fixHeaders: function(prefix, iconsfirst) { if (this.sortable) { switch (this.options.headingSort) { case 'link': var a=RicoUtil.wrapChildren(this.hdrCellDiv,'ricoSort',undefined,'a'); a.href = "javascript:void(0)"; a.onclick = this.toggleSort.bindAsEventListener(this); break; case 'hover': this.hdrCellDiv.onclick = this.toggleSort.bindAsEventListener(this); break; } } this.imgFilter = document.createElement('img'); this.imgFilter.style.display='none'; this.imgFilter.src=Rico.imgDir+this.options.filterImg; this.imgFilter.className='ricoLG_HdrIcon'; this.imgSort = document.createElement('img'); this.imgSort.style.display='none'; this.imgSort.src=Rico.imgDir+this.options.sortAscendImg; this.imgSort.className='ricoLG_HdrIcon'; if (iconsfirst) { this.hdrCellDiv.insertBefore(this.imgSort,this.hdrCellDiv.firstChild); this.hdrCellDiv.insertBefore(this.imgFilter,this.hdrCellDiv.firstChild); } else { this.hdrCellDiv.appendChild(this.imgFilter); this.hdrCellDiv.appendChild(this.imgSort); } if (!this.format.filterUI) { Event.observe(this.imgFilter, 'click', this.filterClick.bindAsEventListener(this), false); } }, filterClick: function(e) { if (this.filterType==Rico.TableColumn.USERFILTER && this.filterOp=='LIKE') { this.liveGrid.openKeyword(this.index); } }, getValue: function(windowRow) { return this.liveGrid.buffer.getWindowValue(windowRow,this.index); }, getBufferCell: function(windowRow) { return this.liveGrid.buffer.getWindowCell(windowRow,this.index); }, getBufferAttr: function(windowRow) { return this.liveGrid.buffer.getWindowAttr(windowRow,this.index); }, setValue: function(windowRow,newval) { this.liveGrid.buffer.setWindowValue(windowRow,this.index,newval); }, _format: function(v) { return v; }, _display: function(v,gridCell) { gridCell.innerHTML=this._format(v); }, _export: function(v) { return this._format(v); }, displayValue: function(windowRow) { var bufCell=this.getBufferCell(windowRow); if (bufCell==null) { this.clearCell(windowRow); return; } var gridCell=this.cell(windowRow); this._display(bufCell,gridCell,windowRow); var acceptAttr=this.liveGrid.buffer.options.acceptAttr; if (acceptAttr.length==0) return; var bufAttr=this.getBufferAttr(windowRow); if (bufAttr==null) return; for (var k=0; k