2 * (c) 2005-2007 Richard Cowin (http://openrico.org)
3 * (c) 2005-2007 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
7 * http://www.apache.org/licenses/LICENSE-2.0
11 if(typeof Rico=='undefined') throw("LiveGrid requires the Rico JavaScript framework");
12 if(typeof RicoUtil=='undefined') throw("LiveGrid requires the RicoUtil Library");
13 if(typeof RicoTranslate=='undefined') throw("LiveGrid requires the RicoTranslate Library");
14 if(typeof Rico.TableColumn=='undefined') throw("LiveGrid requires ricoGridCommon.js");
20 * Loads buffer with data that already exists in the document as an HTML table (no AJAX).
21 * Also serves as a base class for AJAX-enabled buffers.
23 Rico.Buffer.Base = Class.create();
25 Rico.Buffer.Base.prototype = {
27 initialize: function(dataTable, options) {
29 this.updateInProgress = false;
31 this.rcvdRowCount = false; // true if an eof element was included in the last xml response
32 this.foundRowCount = false; // true if an xml response is ever received with eof true
34 this.rowcntContent = "";
38 canFilter : false, // does buffer object support filtering?
39 isEncoded : true, // is the data received via ajax html encoded?
40 acceptAttr : [] // attributes that can be copied from original/ajax data (e.g. className, style, id)
42 Object.extend(this.options, options || {});
44 this.loadRowsFromTable(dataTable);
50 registerGrid: function(liveGrid) {
51 this.liveGrid = liveGrid;
54 setTotalRows: function( newTotalRows ) {
55 if (this.totalRows == newTotalRows) return;
56 this.totalRows = newTotalRows;
58 Rico.writeDebugMsg("setTotalRows, newTotalRows="+newTotalRows);
59 if (this.liveGrid.sizeTo=='data') this.liveGrid.resizeWindow();
60 this.liveGrid.updateHeightDiv();
64 loadRowsFromTable: function(tableElement) {
65 this.rows = this.dom2jstable(tableElement,this.options.fixedHdrRows);
67 this.size = this.rows.length;
68 this.setTotalRows(this.size);
69 this.rowcntContent = this.size.toString();
70 this.rcvdRowCount = true;
71 this.foundRowCount = true;
74 dom2jstable: function(rowsElement,firstRow) {
75 var newRows = new Array();
76 var trs = rowsElement.getElementsByTagName("tr");
77 var acceptAttr=this.options.acceptAttr;
78 for ( var i=firstRow || 0; i < trs.length; i++ ) {
79 var row = new Array();
80 var cells = trs[i].getElementsByTagName("td");
81 for ( var j=0; j < cells.length ; j++ ) {
83 row[j].content=RicoUtil.getContentAsString(cells[j],this.options.isEncoded);
84 for (var k=0; k<acceptAttr.length; k++) {
85 row[j]['_'+acceptAttr[k]]=cells[j].getAttribute(acceptAttr[k]);
87 if (Prototype.Browser.IE) row[j]._class=cells[j].getAttribute('className');
94 _blankRow: function() {
96 for (var i=0; i<this.liveGrid.columns.length; i++) {
103 insertRow: function(beforeRowIndex) {
104 this.rows.splice(beforeRowIndex,0,this._blankRow());
107 appendRows: function(cnt) {
108 for (var i=0; i<cnt; i++)
109 this.rows.push(this._blankRow());
110 this.size=this.rows.length;
113 sortBuffer: function(colnum,sortdir,coltype,getvalfunc) {
114 this.sortColumn=colnum;
115 this.getValFunc=getvalfunc;
118 case 'number': sortFunc=this._sortNumeric.bind(this); break;
119 case 'control':sortFunc=this._sortControl.bind(this); break;
120 default: sortFunc=this._sortAlpha.bind(this); break;
122 this.rows.sort(sortFunc);
123 if (sortdir=='DESC') this.rows.reverse();
126 _sortAlpha: function(a,b) {
127 var aa = this.sortColumn<a.length ? RicoUtil.getInnerText(a[this.sortColumn].content) : '';
128 var bb = this.sortColumn<b.length ? RicoUtil.getInnerText(b[this.sortColumn].content) : '';
129 if (aa==bb) return 0;
130 if (aa<bb) return -1;
134 _sortNumeric: function(a,b) {
135 var aa = this.sortColumn<a.length ? parseFloat(RicoUtil.getInnerText(a[this.sortColumn].content)) : 0;
136 if (isNaN(aa)) aa = 0;
137 var bb = this.sortColumn<b.length ? parseFloat(RicoUtil.getInnerText(b[this.sortColumn].content)) : 0;
138 if (isNaN(bb)) bb = 0;
142 _sortControl: function(a,b) {
143 var aa = this.sortColumn<a.length ? RicoUtil.getInnerText(a[this.sortColumn].content) : '';
144 var bb = this.sortColumn<b.length ? RicoUtil.getInnerText(b[this.sortColumn].content) : '';
145 if (this.getValFunc) {
146 aa=this.getValFunc(aa);
147 bb=this.getValFunc(bb);
149 if (aa==bb) return 0;
150 if (aa<bb) return -1;
155 this.rows = new Array();
161 isInRange: function(position) {
162 var lastRow=Math.min(this.totalRows, position + this.liveGrid.pageSize)
163 return (position >= this.startPos) && (lastRow <= this.endPos()); // && (this.size != 0);
167 return this.startPos + this.rows.length;
170 fetch: function(offset) {
171 this.liveGrid.refreshContents(offset);
175 exportAllRows: function(populate,finish) {
176 populate(this.getRows(0,this.totalRows));
180 setWindow: function(start, count) {
181 this.windowStart = start - this.startPos;
182 this.windowEnd = Math.min(this.windowStart + count,this.size);
183 this.windowPos = start;
186 isVisible: function(bufRow) {
187 return bufRow < this.rows.length && bufRow >= this.windowStart && bufRow < this.windowEnd;
190 getWindowCell: function(windowRow,col) {
191 var bufrow=this.windowStart+windowRow;
192 return this.isVisible(bufrow) && col < this.rows[bufrow].length ? this.rows[bufrow][col] : null;
195 getWindowValue: function(windowRow,col) {
196 var cell=this.getWindowCell(windowRow,col);
197 return cell ? cell.content : null;
200 setWindowValue: function(windowRow,col,newval) {
201 var bufRow=this.windowStart+windowRow;
202 if (bufRow >= this.windowEnd) return false;
203 return this.setValue(bufRow,col,newval);
206 getCell: function(bufRow,col) {
207 return bufRow < this.size ? this.rows[bufRow][col] : null;
210 getValue: function(bufRow,col) {
211 var cell=this.getCell(bufRow,col);
212 return cell ? cell.content : null;
215 setValue: function(bufRow,col,newval,newstyle) {
216 if (bufRow>=this.size) return false;
217 if (!this.rows[bufRow][col]) this.rows[bufRow][col]={};
218 this.rows[bufRow][col].content=newval;
219 if (typeof newstyle=='string') this.rows[bufRow][col]._style=newstyle;
220 this.rows[bufRow][col].modified=true;
224 getRows: function(start, count) {
225 var begPos = start - this.startPos;
226 var endPos = Math.min(begPos + count,this.size);
227 var results = new Array();
228 for ( var i=begPos; i < endPos; i++ )
229 results.push(this.rows[i]);
236 // Rico.LiveGrid -----------------------------------------------------
238 Rico.LiveGrid = Class.create();
240 Rico.LiveGrid.prototype = {
242 initialize: function( tableId, buffer, options ) {
243 Object.extend(this, new Rico.GridCommon);
244 Object.extend(this, new Rico.LiveGridMethods);
246 this.tableId = tableId;
247 this.buffer = buffer;
248 Rico.setDebugArea(tableId+"_debugmsgs"); // if used, this should be a textarea
250 Object.extend(this.options, {
251 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
253 offset : 0, // first row to be displayed
254 prefetchBuffer : true, // load table on page load?
257 canSortDefault : true, // can be overridden in the column specs
258 canFilterDefault : buffer.options.canFilter, // can be overridden in the column specs
259 canHideDefault : true, // can be overridden in the column specs
260 cookiePrefix : 'liveGrid.'+tableId,
262 // highlight & selection parameters
263 highlightElem : 'none',// what gets highlighted/selected (cursorRow, cursorCell, menuRow, menuCell, selection, or none)
264 highlightSection : 3, // which section gets highlighted (frozen=1, scrolling=2, all=3, none=0)
265 highlightMethod : 'class', // outline, class, both (outline is less CPU intensive on the client)
266 highlightClass : 'ricoLG_selection',
268 // export/print parameters
269 maxPrint : 1000, // max # of rows that can be printed/exported, 0=disable print/export feature
270 exportWindow : "height=300,width=500,scrollbars=1,menubar=1,resizable=1",
272 // heading parameters
273 headingSort : 'link', // link: make headings a link that will sort column, hover: make headings a hoverset, none: events on headings are disabled
274 hdrIconsFirst : true, // true: put sort & filter icons before header text, false: after
275 sortAscendImg : 'sort_asc.gif',
276 sortDescendImg : 'sort_desc.gif',
277 filterImg : 'filtercol.gif'
280 // sortCol: initial sort column
282 this.options.sortHandler = this.sortHandler.bind(this);
283 this.options.filterHandler = this.filterHandler.bind(this);
284 this.options.onRefreshComplete = this.bookmarkHandler.bind(this);
285 this.options.rowOverHandler = this.rowMouseOver.bindAsEventListener(this);
286 this.options.mouseDownHandler = this.selectMouseDown.bindAsEventListener(this);
287 this.options.mouseOverHandler = this.selectMouseOver.bindAsEventListener(this);
288 this.options.mouseUpHandler = this.selectMouseUp.bindAsEventListener(this);
289 Object.extend(this.options, options || {});
291 switch (typeof this.options.visibleRows) {
293 this.sizeTo=this.options.visibleRows;
294 this.options.visibleRows=-1;
297 switch (this.options.visibleRows) {
298 case -1: this.sizeTo='window'; break;
299 case -2: this.sizeTo='data'; break;
300 case -3: this.sizeTo='body'; break;
304 this.sizeTo='window';
305 this.options.visibleRows=-1;
307 this.highlightEnabled=this.options.highlightSection>0;
310 if (this.headerColCnt==0) {
311 alert('ERROR: no columns found in "'+this.tableId+'"');
314 this.createColumnArray();
315 if (this.options.headingSort=='hover')
316 this.createHoverSet();
318 this.bookmark=$(this.tableId+"_bookmark");
320 this.createDataCells(this.options.visibleRows);
321 if (this.pageSize == 0) return;
322 this.buffer.registerGrid(this);
323 if (this.buffer.setBufferSize) this.buffer.setBufferSize(this.pageSize);
324 this.scrollTimeout = null;
325 this.lastScrollPos = 0;
326 this.attachMenuEvents();
328 // preload the images...
329 new Image().src = Rico.imgDir+this.options.filterImg;
330 new Image().src = Rico.imgDir+this.options.sortAscendImg;
331 new Image().src = Rico.imgDir+this.options.sortDescendImg;
332 Rico.writeDebugMsg("images preloaded");
334 this.setSortUI( this.options.sortCol, this.options.sortDir );
336 if (this.listInvisible().length==this.columns.length)
337 this.columns[0].showColumn();
339 this.scrollDiv.style.display="";
340 if (this.buffer.totalRows>0)
341 this.updateHeightDiv();
342 if (this.options.prefetchBuffer) {
343 if (this.bookmark) this.bookmark.innerHTML = RicoTranslate.getPhrase("Loading...");
344 if (this.options.canFilterDefault && this.options.getQueryParms)
345 this.checkForFilterParms();
346 this.buffer.fetch(this.options.offset);
348 this.scrollEventFunc=this.handleScroll.bindAsEventListener(this);
349 this.wheelEventFunc=this.handleWheel.bindAsEventListener(this);
350 this.wheelEvent=(Prototype.Browser.IE || Prototype.Browser.Opera || Prototype.Browser.WebKit) ? 'mousewheel' : 'DOMMouseScroll';
351 if (this.options.offset && this.options.offset < this.buffer.totalRows)
352 setTimeout(this.scrollToRow.bind(this,this.options.offset),50); // Safari requires a delay
354 this.setHorizontalScroll();
355 if (this.options.windowResize)
356 setTimeout(this.pluginWindowResize.bind(this),100);
360 Rico.LiveGridMethods = function() {};
362 Rico.LiveGridMethods.prototype = {
364 createHoverSet: function() {
366 for( var c=0; c < this.headerColCnt; c++ )
367 hdrs.push(this.columns[c].hdrCellDiv);
368 this.hoverSet = new Rico.HoverSet(hdrs);
371 checkForFilterParms: function() {
372 var s=window.location.search;
373 if (s.charAt(0)=='?') s=s.substring(1);
374 var pairs = s.split('&');
375 for (var i=0; i<pairs.length; i++)
376 if (pairs[i].match(/^f\[\d+\]/)) {
377 this.buffer.options.requestParameters.push(pairs[i]);
382 * Create one table for frozen columns and one for scrolling columns.
383 * Also create div's to contain them.
385 createTables: function() {
388 var table = $(this.tableId);
389 if (!table) return result;
390 if (table.tagName.toLowerCase()=='table') {
391 var theads=table.getElementsByTagName("thead");
392 if (theads.length == 1) {
393 Rico.writeDebugMsg("createTables: using thead section, id="+this.tableId);
394 var hdrSrc=theads[0].rows;
396 Rico.writeDebugMsg("createTables: using tbody section, id="+this.tableId);
397 var hdrSrc=new Array(table.rows[0]);
400 } else if (this.options.columnSpecs.length > 0) {
402 Rico.writeDebugMsg("createTables: inserting at "+table.tagName+", id="+this.tableId);
404 alert("ERROR!\n\nUnable to initialize '"+this.tableId+"'\n\nLiveGrid terminated");
409 this.scrollTabs = this.createDiv("scrollTabs",this.innerDiv);
410 this.shadowDiv = this.createDiv("shadow",this.scrollDiv);
411 this.shadowDiv.style.direction='ltr'; // avoid FF bug
412 this.messageDiv = this.createDiv("message",this.outerDiv);
413 this.messageDiv.style.display="none";
414 this.messageShadow=new Rico.Shadow(this.messageDiv);
415 this.scrollDiv.style.display="none";
416 this.scrollDiv.scrollTop=0;
417 if (this.options.highlightMethod!='class') {
418 this.highlightDiv=[];
419 switch (this.options.highlightElem) {
422 this.highlightDiv[0] = this.createDiv("highlight",this.outerDiv);
423 this.highlightDiv[0].style.display="none";
427 for (var i=0; i<2; i++) {
428 this.highlightDiv[i] = this.createDiv("highlight",i==0 ? this.frozenTabs : this.scrollTabs);
429 this.highlightDiv[i].style.display="none";
430 this.highlightDiv[i].id+=i;
434 // create one div for each side of the rectangle
435 var parentDiv=this.options.highlightSection==1 ? this.frozenTabs : this.scrollTabs;
436 for (var i=0; i<4; i++) {
437 this.highlightDiv[i] = this.createDiv("highlight",parentDiv);
438 this.highlightDiv[i].style.display="none";
439 this.highlightDiv[i].id+=i;
440 this.highlightDiv[i].style[i % 2==0 ? 'height' : 'width']="0px";
447 for (var i=0; i<2; i++) {
448 this.tabs[i] = document.createElement("table");
449 this.tabs[i].className = 'ricoLG_table';
450 this.tabs[i].border=0;
451 this.tabs[i].cellPadding=0;
452 this.tabs[i].cellSpacing=0;
453 this.tabs[i].id = this.tableId+"_tab"+i;
454 this.thead[i]=this.tabs[i].createTHead();
455 this.thead[i].className='ricoLG_top';
456 if (this.tabs[i].tBodies.length==0)
457 this.tbody[i]=this.tabs[i].appendChild(document.createElement("tbody"));
459 this.tbody[i]=this.tabs[i].tBodies[0];
460 this.tbody[i].className='ricoLG_bottom';
461 this.tbody[i].insertRow(-1);
463 this.frozenTabs.appendChild(this.tabs[0]);
464 this.scrollTabs.appendChild(this.tabs[1]);
465 insertloc.parentNode.insertBefore(this.outerDiv,insertloc);
467 this.loadHdrSrc(hdrSrc);
470 for( var c=0; c < this.headerColCnt; c++ )
471 this.tbody[c<this.options.frozenColumns ? 0 : 1].rows[0].insertCell(-1);
472 if (table) table.parentNode.removeChild(table);
473 Rico.writeDebugMsg('createTables end');
476 createDataCells: function(visibleRows) {
477 if (visibleRows < 0) {
478 this.appendBlankRow();
480 this.autoAppendRows(this.remainingHt());
482 for( var r=0; r < visibleRows; r++ )
483 this.appendBlankRow();
485 var s=this.options.highlightSection;
486 if (s & 1) this.attachHighlightEvents(this.tbody[0]);
487 if (s & 2) this.attachHighlightEvents(this.tbody[1]);
491 createHdr: function() {
492 for (var i=0; i<2; i++) {
493 var start=(i==0) ? 0 : this.options.frozenColumns;
494 var limit=(i==0) ? this.options.frozenColumns : this.options.columnSpecs.length;
495 Rico.writeDebugMsg('createHdr: i='+i+' start='+start+' limit='+limit);
496 if (this.options.PanelNamesOnTabHdr && this.options.panels) {
497 // place panel names on first row of thead
498 var r = this.thead[i].insertRow(-1);
499 r.className='ricoLG_hdg';
500 var lastIdx=-1, span, newCell=null, spanIdx=0;
501 for( var c=start; c < limit; c++ ) {
502 if (lastIdx == this.options.columnSpecs[c].panelIdx) {
505 if (newCell) newCell.colSpan=span;
506 newCell = r.insertCell(-1);
508 lastIdx=this.options.columnSpecs[c].panelIdx;
509 newCell.innerHTML=this.options.panels[lastIdx];
512 if (newCell) newCell.colSpan=span;
514 var mainRow = this.thead[i].insertRow(-1);
515 mainRow.id=this.tableId+'_tab'+i+'h_main';
516 mainRow.className='ricoLG_hdg';
517 for( var c=start; c < limit; c++ ) {
518 var newCell = mainRow.insertCell(-1);
519 newCell.innerHTML=this.options.columnSpecs[c].Hdg;
521 this.headerColCnt = this.getColumnInfo(this.thead[i].rows);
525 loadHdrSrc: function(hdrSrc) {
526 Rico.writeDebugMsg('loadHdrSrc start');
527 this.headerColCnt = this.getColumnInfo(hdrSrc);
528 for (var i=0; i<2; i++) {
529 for (var r=0; r<hdrSrc.length; r++) {
530 var newrow = this.thead[i].insertRow(-1);
531 newrow.className='ricoLG_hdg';
534 if (hdrSrc.length==1) {
535 var cells=hdrSrc[0].cells;
536 for (var c=0; cells.length > 0; c++)
537 this.thead[c<this.options.frozenColumns ? 0 : 1].rows[0].appendChild(cells[0]);
539 for (var r=0; r<hdrSrc.length; r++) {
540 var cells=hdrSrc[r].cells;
541 for (var c=0; cells.length > 0; c++) {
542 if (cells[0].className=='ricoFrozen') {
543 this.thead[0].rows[r].appendChild(cells[0]);
544 if (r==this.headerRowIdx) this.options.frozenColumns=c+1;
546 this.thead[1].rows[r].appendChild(cells[0]);
551 Rico.writeDebugMsg('loadHdrSrc end');
554 sizeDivs: function() {
555 Rico.writeDebugMsg('sizeDivs: '+this.tableId);
559 if (this.pageSize == 0) return;
560 this.rowHeight = Math.round(this.dataHt/this.pageSize);
561 var scrHt=this.dataHt;
562 if (this.scrWi>0 || Prototype.Browser.IE || Prototype.Browser.WebKit)
563 scrHt+=this.options.scrollBarWidth;
564 this.scrollDiv.style.height=scrHt+'px';
565 this.innerDiv.style.width=(this.scrWi-this.options.scrollBarWidth+1)+'px';
566 this.resizeDiv.style.height=this.frozenTabs.style.height=this.innerDiv.style.height=(this.hdrHt+this.dataHt+1)+'px';
567 Rico.writeDebugMsg('sizeDivs scrHt='+scrHt+' innerHt='+this.innerDiv.style.height+' rowHt='+this.rowHeight+' pageSize='+this.pageSize);
568 pad=(this.scrWi-this.scrTabWi < this.options.scrollBarWidth) ? 2 : 0;
569 this.shadowDiv.style.width=(this.scrTabWi+pad)+'px';
570 this.outerDiv.style.height=(this.hdrHt+scrHt)+'px';
571 this.setHorizontalScroll();
574 setHorizontalScroll: function() {
575 var scrleft=this.scrollDiv.scrollLeft;
576 this.scrollTabs.style.left=(-scrleft)+'px';
579 remainingHt: function() {
580 var winHt=RicoUtil.windowHeight();
581 var margin=Prototype.Browser.IE ? 15 : 10;
582 switch (this.sizeTo) {
585 var divPos=Position.page(this.outerDiv);
586 var tabHt=Math.max(this.tabs[0].offsetHeight,this.tabs[1].offsetHeight);
587 Rico.writeDebugMsg("remainingHt, winHt="+winHt+' tabHt='+tabHt+' gridY='+divPos[1]);
588 return winHt-divPos[1]-tabHt-this.options.scrollBarWidth-margin; // allow for scrollbar and some margin
590 //Rico.writeDebugMsg("remainingHt, document.height="+document.height);
591 //Rico.writeDebugMsg("remainingHt, body.offsetHeight="+document.body.offsetHeight);
592 //Rico.writeDebugMsg("remainingHt, body.scrollHeight="+document.body.scrollHeight);
593 //Rico.writeDebugMsg("remainingHt, documentElement.scrollHeight="+document.documentElement.scrollHeight);
594 var bodyHt=Prototype.Browser.IE ? document.body.scrollHeight : document.body.offsetHeight;
595 var remHt=winHt-bodyHt-margin;
596 if (!Prototype.Browser.WebKit) remHt-=this.options.scrollBarWidth;
597 Rico.writeDebugMsg("remainingHt, winHt="+winHt+' pageHt='+bodyHt+' remHt='+remHt);
602 adjustPageSize: function() {
603 var remHt=this.remainingHt();
604 Rico.writeDebugMsg('adjustPageSize remHt='+remHt+' lastRow='+this.lastRowPos);
605 if (remHt > this.rowHeight)
606 this.autoAppendRows(remHt);
607 else if (remHt < 0 || this.sizeTo=='data')
608 this.autoRemoveRows(-remHt);
611 pluginWindowResize: function() {
612 Event.observe(window, "resize", this.resizeWindow.bindAsEventListener(this), false);
615 resizeWindow: function() {
616 Rico.writeDebugMsg('resizeWindow '+this.tableId+' lastRow='+this.lastRowPos);
621 var oldSize=this.pageSize;
622 this.adjustPageSize();
623 if (this.pageSize > oldSize) {
624 this.isPartialBlank=true;
625 var adjStart=this.adjustRow(this.lastRowPos);
626 this.buffer.fetch(adjStart);
628 if (oldSize != this.pageSize)
629 setTimeout(this.finishResize.bind(this),50);
632 Rico.writeDebugMsg('resizeWindow complete. old size='+oldSize+' new size='+this.pageSize);
635 finishResize: function() {
637 this.updateHeightDiv();
640 topOfLastPage: function() {
641 return Math.max(this.buffer.totalRows-this.pageSize,0);
644 updateHeightDiv: function() {
645 var notdisp=this.topOfLastPage();
646 var ht = this.scrollDiv.clientHeight + this.rowHeight * notdisp;
647 //if (Prototype.Browser.Opera) ht+=this.options.scrollBarWidth-3;
648 Rico.writeDebugMsg("updateHeightDiv, ht="+ht+' scrollDiv.clientHeight='+this.scrollDiv.clientHeight+' rowsNotDisplayed='+notdisp);
649 this.shadowDiv.style.height=ht+'px';
652 autoRemoveRows: function(overage) {
653 var removeCnt=Math.ceil(overage / this.rowHeight);
654 if (this.sizeTo=='data')
655 removeCnt=Math.max(removeCnt,this.pageSize-this.buffer.totalRows);
656 Rico.writeDebugMsg("autoRemoveRows overage="+overage+" removeCnt="+removeCnt);
657 for (var i=0; i<removeCnt; i++)
661 removeRow: function() {
662 if (this.pageSize <= this.options.minPageRows) return;
664 for( var c=0; c < this.headerColCnt; c++ ) {
665 var cell=this.columns[c].cell(this.pageSize);
666 this.columns[c].dataColDiv.removeChild(cell);
670 autoAppendRows: function(overage) {
671 var addCnt=Math.floor(overage / this.rowHeight);
672 Rico.writeDebugMsg("autoAppendRows overage="+overage+" cnt="+addCnt+" rowHt="+this.rowHeight);
673 for (var i=0; i<addCnt; i++) {
674 if (this.sizeTo=='data' && this.pageSize>=this.buffer.totalRows) break;
675 this.appendBlankRow();
679 // on older systems, this can be fairly slow
680 appendBlankRow: function() {
681 if (this.pageSize >= this.options.maxPageRows) return;
682 Rico.writeDebugMsg("appendBlankRow #"+this.pageSize);
683 var cls=this.defaultRowClass(this.pageSize);
684 for( var c=0; c < this.headerColCnt; c++ ) {
685 var newdiv = document.createElement("div");
686 newdiv.className = 'ricoLG_cell '+cls;
687 newdiv.id=this.tableId+'_'+this.pageSize+'_'+c;
688 this.columns[c].dataColDiv.appendChild(newdiv);
689 newdiv.innerHTML=' ';
690 if (this.columns[c]._create)
691 this.columns[c]._create(newdiv,this.pageSize);
696 defaultRowClass: function(rownum) {
697 return (rownum % 2==0) ? 'ricoLG_evenRow' : 'ricoLG_oddRow';
700 handleMenuClick: function(e) {
702 if (!this.menu) return;
704 this.unhighlight(); // in case highlighting was invoked externally
705 var cell=Event.element(e);
706 if (cell.className=='ricoLG_highlightDiv') {
707 var idx=this.highlightIdx;
709 cell=RicoUtil.getParentByTagName(cell,'div','ricoLG_cell');
711 var idx=this.winCellIndex(cell);
712 if ((this.options.highlightSection & (idx.tabIdx+1))==0) return;
715 this.highlightEnabled=false;
716 if (this.hideScroll) this.scrollDiv.style.overflow="hidden";
718 if (!this.menu.div) this.menu.createDiv();
719 this.menu.liveGrid=this;
720 if (this.menu.buildGridMenu) {
721 var showMenu=this.menu.buildGridMenu(idx.row, idx.column, idx.tabIdx);
722 if (!showMenu) return;
724 if (this.options.highlightElem=='selection' && !this.isSelected(idx.cell))
725 this.selectCell(idx.cell);
726 this.menu.showmenu(e,this.closeMenu.bind(this));
729 closeMenu: function() {
730 if (!this.menuIdx) return;
731 if (this.hideScroll) this.scrollDiv.style.overflow="";
733 this.highlightEnabled=true;
738 * @return index of cell within the window
740 winCellIndex: function(cell) {
741 var a=cell.id.split(/_/);
743 var r=parseInt(a[l-2]);
744 var c=parseInt(a[l-1]);
745 return {row:r, column:c, tabIdx:this.columns[c].tabIdx, cell:cell};
749 * @return index of cell within the buffer
751 bufCellIndex: function(cell) {
752 var idx=this.winCellIndex(cell);
753 idx.row+=this.buffer.windowPos;
754 if (idx.row >= this.buffer.size) idx.onBlankRow=true;
758 attachHighlightEvents: function(tBody) {
759 switch (this.options.highlightElem) {
761 Event.observe(tBody,"mousedown", this.options.mouseDownHandler, false);
762 tBody.ondrag = function () { return false; };
763 tBody.onselectstart = function () { return false; };
767 Event.observe(tBody,"mouseover", this.options.rowOverHandler, false);
772 getVisibleSelection: function() {
774 if (this.SelectIdxStart && this.SelectIdxEnd) {
775 var r1=Math.max(Math.min(this.SelectIdxEnd.row,this.SelectIdxStart.row),this.buffer.windowPos);
776 var r2=Math.min(Math.max(this.SelectIdxEnd.row,this.SelectIdxStart.row),this.buffer.windowEnd-1);
777 var c1=Math.min(this.SelectIdxEnd.column,this.SelectIdxStart.column);
778 var c2=Math.max(this.SelectIdxEnd.column,this.SelectIdxStart.column);
779 for (var r=r1; r<=r2; r++)
780 for (var c=c1; c<=c2; c++)
781 cellList.push({row:r-this.buffer.windowPos,column:c});
783 if (this.SelectCtrl) {
784 for (var i=0; i<this.SelectCtrl.length; i++) {
785 if (this.SelectCtrl[i].row>=this.buffer.windowPos && this.SelectCtrl[i].row<this.buffer.windowEnd)
786 cellList.push({row:this.SelectCtrl[i].row-this.buffer.windowPos,column:this.SelectCtrl[i].column});
792 updateSelectOutline: function() {
793 if (!this.SelectIdxStart || !this.SelectIdxEnd) return;
794 var r1=Math.max(Math.min(this.SelectIdxEnd.row,this.SelectIdxStart.row), this.buffer.windowStart);
795 var r2=Math.min(Math.max(this.SelectIdxEnd.row,this.SelectIdxStart.row), this.buffer.windowEnd-1);
797 this.HideSelection();
800 var c1=Math.min(this.SelectIdxEnd.column,this.SelectIdxStart.column);
801 var c2=Math.max(this.SelectIdxEnd.column,this.SelectIdxStart.column);
802 var top1=this.columns[c1].cell(r1-this.buffer.windowStart).offsetTop;
803 var cell2=this.columns[c1].cell(r2-this.buffer.windowStart);
804 var bottom2=cell2.offsetTop+cell2.offsetHeight;
805 var left1=this.columns[c1].dataCell.offsetLeft;
806 var left2=this.columns[c2].dataCell.offsetLeft;
807 var right2=left2+this.columns[c2].dataCell.offsetWidth;
808 //window.status='updateSelectOutline: '+r1+' '+r2+' top='+top1+' bot='+bottom2;
809 this.highlightDiv[0].style.top=this.highlightDiv[3].style.top=this.highlightDiv[1].style.top=(this.hdrHt+top1-1) + 'px';
810 this.highlightDiv[2].style.top=(this.hdrHt+bottom2-1)+'px';
811 this.highlightDiv[3].style.left=(left1-2)+'px';
812 this.highlightDiv[0].style.left=this.highlightDiv[2].style.left=(left1-1)+'px';
813 this.highlightDiv[1].style.left=(right2-1)+'px';
814 this.highlightDiv[0].style.width=this.highlightDiv[2].style.width=(right2-left1-1) + 'px';
815 this.highlightDiv[1].style.height=this.highlightDiv[3].style.height=(bottom2-top1) + 'px';
816 //this.highlightDiv[0].style.right=this.highlightDiv[2].style.right=this.highlightDiv[1].style.right=()+'px';
817 //this.highlightDiv[2].style.bottom=this.highlightDiv[3].style.bottom=this.highlightDiv[1].style.bottom=(this.hdrHt+bottom2) + 'px';
818 for (var i=0; i<4; i++)
819 this.highlightDiv[i].style.display='';
822 HideSelection: function(cellList) {
823 if (this.options.highlightMethod!='class') {
824 for (var i=0; i<4; i++)
825 this.highlightDiv[i].style.display='none';
827 if (this.options.highlightMethod!='outline') {
828 var cellList=this.getVisibleSelection();
829 for (var i=0; i<cellList.length; i++)
830 this.unhighlightCell(this.columns[cellList[i].column].cell(cellList[i].row));
834 ShowSelection: function() {
835 if (this.options.highlightMethod!='class')
836 this.updateSelectOutline();
837 if (this.options.highlightMethod!='outline') {
838 var cellList=this.getVisibleSelection();
839 for (var i=0; i<cellList.length; i++)
840 this.highlightCell(this.columns[cellList[i].column].cell(cellList[i].row));
844 ClearSelection: function() {
845 this.HideSelection();
846 this.SelectIdxStart=null;
847 this.SelectIdxEnd=null;
851 selectCell: function(cell) {
852 this.ClearSelection();
853 this.SelectIdxStart=this.SelectIdxEnd=this.bufCellIndex(cell);
854 this.ShowSelection();
857 AdjustSelection: function(cell) {
858 var newIdx=this.bufCellIndex(cell);
859 if (this.SelectIdxStart.tabIdx != newIdx.tabIdx) return;
860 this.HideSelection();
861 this.SelectIdxEnd=newIdx;
862 this.ShowSelection();
865 RefreshSelection: function() {
866 var cellList=this.getVisibleSelection();
867 for (var i=0; i<cellList.length; i++)
868 this.columns[cellList[i].column].displayValue(cellList[i].row);
871 FillSelection: function(newVal,newStyle) {
872 if (this.SelectIdxStart && this.SelectIdxEnd) {
873 var r1=Math.min(this.SelectIdxEnd.row,this.SelectIdxStart.row);
874 var r2=Math.max(this.SelectIdxEnd.row,this.SelectIdxStart.row);
875 var c1=Math.min(this.SelectIdxEnd.column,this.SelectIdxStart.column);
876 var c2=Math.max(this.SelectIdxEnd.column,this.SelectIdxStart.column);
877 for (var r=r1; r<=r2; r++)
878 for (var c=c1; c<=c2; c++)
879 this.buffer.setValue(r,c,newVal,newStyle);
881 if (this.SelectCtrl) {
882 for (var i=0; i<this.SelectCtrl.length; i++)
883 this.buffer.setValue(this.SelectCtrl[i].row,this.SelectCtrl[i].column,newVal,newStyle);
885 this.RefreshSelection();
888 selectMouseDown: function(e) {
889 if (this.highlightEnabled==false) return true;
891 var cell=Event.element(e);
893 if (!Event.isLeftClick(e)) return;
894 cell=RicoUtil.getParentByTagName(cell,'div','ricoLG_cell');
896 var newIdx=this.bufCellIndex(cell);
897 if (newIdx.onBlankRow) return;
899 if (!this.SelectIdxStart || this.options.highlightMethod!='class') return;
900 if (!this.isSelected(cell)) {
901 this.highlightCell(cell);
902 this.SelectCtrl.push(this.bufCellIndex(cell));
904 for (var i=0; i<this.SelectCtrl.length; i++) {
905 if (this.SelectCtrl[i].row==newIdx.row && this.SelectCtrl[i].column==newIdx.column) {
906 this.unhighlightCell(cell);
907 this.SelectCtrl.splice(i,1);
912 } else if (e.shiftKey) {
913 if (!this.SelectIdxStart) return;
914 this.AdjustSelection(cell);
916 this.selectCell(cell);
921 pluginSelect: function() {
922 if (this.selectPluggedIn) return;
923 var tBody=this.tbody[this.SelectIdxStart.tabIdx];
924 Event.observe(tBody,"mouseover", this.options.mouseOverHandler, false);
925 Event.observe(this.outerDiv,"mouseup", this.options.mouseUpHandler, false);
926 if (this.options.highlightMethod!='class')
927 this.selectPluggedIn=true;
930 unplugSelect: function() {
931 var tBody=this.tbody[this.SelectIdxStart.tabIdx];
932 Event.stopObserving(tBody,"mouseover", this.options.mouseOverHandler , false);
933 Event.stopObserving(this.outerDiv,"mouseup", this.options.mouseUpHandler , false);
934 this.selectPluggedIn=false;
937 selectMouseUp: function(e) {
939 var cell=Event.element(e);
940 cell=RicoUtil.getParentByTagName(cell,'div','ricoLG_cell');
942 if (this.SelectIdxStart && this.SelectIdxEnd)
943 this.AdjustSelection(cell);
945 this.ClearSelection();
948 selectMouseOver: function(e) {
949 var cell=Event.element(e);
950 cell=RicoUtil.getParentByTagName(cell,'div','ricoLG_cell');
952 this.AdjustSelection(cell);
956 isSelected: function(cell) {
957 return Element.hasClassName(cell,this.options.highlightClass);
960 highlightCell: function(cell) {
961 Element.addClassName(cell,this.options.highlightClass);
964 unhighlightCell: function(cell) {
965 if (cell==null) return;
966 Element.removeClassName(cell,this.options.highlightClass);
969 selectRow: function(r) {
970 for (var c=0; c<this.columns.length; c++)
971 this.highlightCell(this.columns[c].cell(r));
974 unselectRow: function(r) {
975 for (var c=0; c<this.columns.length; c++)
976 this.unhighlightCell(this.columns[c].cell(r));
979 rowMouseOver: function(e) {
980 if (!this.highlightEnabled) return;
981 var cell=Event.element(e);
982 cell=RicoUtil.getParentByTagName(cell,'div','ricoLG_cell');
984 var newIdx=this.winCellIndex(cell);
985 if ((this.options.highlightSection & (newIdx.tabIdx+1))==0) return;
986 this.highlight(newIdx);
989 highlight: function(newIdx) {
990 if (this.options.highlightMethod!='outline') this.cursorSetClass(newIdx);
991 if (this.options.highlightMethod!='class') this.cursorOutline(newIdx);
992 this.highlightIdx=newIdx;
995 cursorSetClass: function(newIdx) {
996 switch (this.options.highlightElem) {
999 if (this.highlightIdx) this.unhighlightCell(this.highlightIdx.cell);
1000 this.highlightCell(newIdx.cell);
1004 if (this.highlightIdx) this.unselectRow(this.highlightIdx.row);
1005 var s1=this.options.highlightSection & 1;
1006 var s2=this.options.highlightSection & 2;
1007 var c0=s1 ? 0 : this.options.frozenColumns;
1008 var c1=s2 ? this.columns.length : this.options.frozenColumns;
1009 for (var c=c0; c<c1; c++)
1010 this.highlightCell(this.columns[c].cell(newIdx.row));
1016 cursorOutline: function(newIdx) {
1017 switch (this.options.highlightElem) {
1020 var div=this.highlightDiv[newIdx.tabIdx];
1021 div.style.left=(this.columns[newIdx.column].dataCell.offsetLeft-1)+'px';
1022 div.style.width=this.columns[newIdx.column].colWidth;
1023 this.highlightDiv[1-newIdx.tabIdx].style.display='none';
1027 var div=this.highlightDiv[0];
1028 var s1=this.options.highlightSection & 1;
1029 var s2=this.options.highlightSection & 2;
1030 div.style.left=s1 ? '0px' : this.frozenTabs.style.width;
1031 div.style.width=((s1 ? this.frozenTabs.offsetWidth : 0) + (s2 ? this.innerDiv.offsetWidth : 0) - 4)+'px';
1035 div.style.top=(this.hdrHt+newIdx.row*this.rowHeight-1)+'px';
1036 div.style.height=(this.rowHeight-1)+'px';
1037 div.style.display='';
1040 unhighlight: function() {
1041 switch (this.options.highlightElem) {
1043 this.highlightIdx=this.menuIdx;
1045 if (this.highlightIdx) this.unhighlightCell(this.highlightIdx.cell);
1046 if (!this.highlightDiv) return;
1047 for (var i=0; i<2; i++)
1048 this.highlightDiv[i].style.display='none';
1051 this.highlightIdx=this.menuIdx;
1053 if (this.highlightIdx) this.unselectRow(this.highlightIdx.row);
1054 if (this.highlightDiv) this.highlightDiv[0].style.display='none';
1059 hideMsg: function() {
1060 if (this.messageDiv.style.display=="none") return;
1061 this.messageDiv.style.display="none";
1062 this.messageShadow.hide();
1065 showMsg: function(msg) {
1066 this.messageDiv.innerHTML=RicoTranslate.getPhrase(msg);
1067 this.messageDiv.style.display="";
1068 var msgWidth=this.messageDiv.offsetWidth;
1069 var msgHeight=this.messageDiv.offsetHeight;
1070 var divwi=this.outerDiv.offsetWidth;
1071 var divht=this.outerDiv.offsetHeight;
1072 this.messageDiv.style.top=parseInt((divht-msgHeight)/2)+'px';
1073 this.messageDiv.style.left=parseInt((divwi-msgWidth)/2)+'px';
1074 this.messageShadow.show();
1075 Rico.writeDebugMsg("showMsg: "+msg);
1078 resetContents: function(resetHt) {
1079 Rico.writeDebugMsg("resetContents("+resetHt+")");
1080 this.buffer.clear();
1082 if (typeof resetHt=='undefined' || resetHt==true) {
1083 this.buffer.setTotalRows(0);
1085 this.scrollToRow(0);
1087 if (this.bookmark) this.bookmark.innerHTML=" ";
1090 setImages: function() {
1091 for (n=0; n<this.columns.length; n++)
1092 this.columns[n].setImage();
1095 // returns column index, or -1 if there are no sorted columns
1096 findSortedColumn: function() {
1097 for (var n=0; n<this.columns.length; n++)
1098 if (this.columns[n].isSorted()) return n;
1102 findColumnName: function(name) {
1103 for (var n=0; n<this.columns.length; n++)
1104 if (this.columns[n].fieldName == name) return n;
1111 setSortUI: function( columnNameOrNum, sortDirection ) {
1112 Rico.writeDebugMsg("setSortUI: "+columnNameOrNum+' '+sortDirection);
1113 var colnum=this.findSortedColumn();
1115 sortDirection=this.columns[colnum].getSortDirection();
1117 if (typeof sortDirection!='string') {
1118 sortDirection=Rico.TableColumn.SORT_ASC;
1120 sortDirection=sortDirection.toUpperCase();
1121 if (sortDirection != Rico.TableColumn.SORT_DESC) sortDirection=Rico.TableColumn.SORT_ASC;
1123 switch (typeof columnNameOrNum) {
1125 colnum=this.findColumnName(columnNameOrNum);
1128 colnum=columnNameOrNum;
1132 if (typeof(colnum)!='number' || colnum < 0) return;
1134 this.columns[colnum].setSorted(sortDirection);
1135 this.buffer.sortBuffer(colnum,sortDirection,this.columns[colnum].format.type,this.columns[colnum]._sortfunc);
1139 * clear sort flag on all columns
1141 clearSort: function() {
1142 for (var x=0;x<this.columns.length;x++)
1143 this.columns[x].setUnsorted();
1147 * clear filters on all columns
1149 clearFilters: function() {
1150 for (var x=0;x<this.columns.length;x++)
1151 this.columns[x].setUnfiltered(true);
1152 if (this.options.filterHandler)
1153 this.options.filterHandler();
1157 * returns number of columns with a user filter set
1159 filterCount: function() {
1160 for (var x=0,cnt=0;x<this.columns.length;x++)
1161 if (this.columns[x].isFiltered()) cnt++;
1165 sortHandler: function() {
1168 var n=this.findSortedColumn();
1170 Rico.writeDebugMsg("sortHandler: sorting column "+n);
1171 this.buffer.sortBuffer(n,this.columns[n].getSortDirection(),this.columns[n].format.type,this.columns[n]._sortfunc);
1173 this.scrollDiv.scrollTop = 0;
1174 this.buffer.fetch(0);
1177 filterHandler: function() {
1178 Rico.writeDebugMsg("filterHandler");
1180 this.ClearSelection();
1182 if (this.bookmark) this.bookmark.innerHTML=" ";
1184 this.buffer.fetch(-1);
1187 bookmarkHandler: function(firstrow,lastrow) {
1188 if (isNaN(firstrow) || !this.bookmark) return;
1189 var totrows=this.buffer.totalRows;
1190 if (totrows < lastrow) lastrow=totrows;
1192 var newhtml = RicoTranslate.getPhrase("No matching records");
1193 } else if (lastrow<0) {
1194 var newhtml = RicoTranslate.getPhrase("No records");
1196 var newhtml = RicoTranslate.getPhrase("Listing records")+" "+firstrow+" - "+lastrow;
1197 var totphrase = this.buffer.foundRowCount ? "of" : "of about";
1198 newhtml+=" "+RicoTranslate.getPhrase(totphrase)+" "+totrows;
1200 this.bookmark.innerHTML = newhtml;
1204 * @return array of column objects which have invisible status
1206 listInvisible: function() {
1207 var hiddenColumns=new Array();
1208 for (var x=0;x<this.columns.length;x++)
1209 if (this.columns[x].visible==false)
1210 hiddenColumns.push(this.columns[x]);
1211 return hiddenColumns;
1217 showAll: function() {
1218 var invisible=this.listInvisible();
1219 for (var x=0;x<invisible.length;x++)
1220 invisible[x].showColumn();
1223 clearRows: function() {
1224 if (this.isBlank==true) return;
1225 for (var c=0; c < this.columns.length; c++)
1226 this.columns[c].clearColumn();
1227 this.ClearSelection();
1228 this.isBlank = true;
1231 blankRow: function(r) {
1232 for (var c=0; c < this.columns.length; c++)
1233 this.columns[c].clearCell(r);
1236 refreshContents: function(startPos) {
1237 Rico.writeDebugMsg("refreshContents: startPos="+startPos+" lastRow="+this.lastRowPos+" PartBlank="+this.isPartialBlank+" pageSize="+this.pageSize);
1240 this.unhighlight(); // in case highlighting was manually invoked
1241 this.highlightEnabled=this.options.highlightSection!='none';
1242 if (startPos == this.lastRowPos && !this.isPartialBlank && !this.isBlank) return;
1243 this.isBlank = false;
1244 var viewPrecedesBuffer = this.buffer.startPos > startPos
1245 var contentStartPos = viewPrecedesBuffer ? this.buffer.startPos: startPos;
1246 var contentEndPos = Math.min(this.buffer.startPos + this.buffer.size, startPos + this.pageSize);
1247 var onRefreshComplete = this.options.onRefreshComplete;
1249 if ((startPos + this.pageSize < this.buffer.startPos)
1250 || (this.buffer.startPos + this.buffer.size < startPos)
1251 || (this.buffer.size == 0)) {
1253 if (onRefreshComplete != null)
1254 onRefreshComplete(contentStartPos+1,contentEndPos);
1258 Rico.writeDebugMsg('refreshContents: contentStartPos='+contentStartPos+' contentEndPos='+contentEndPos+' viewPrecedesBuffer='+viewPrecedesBuffer);
1259 if (this.options.highlightElem=='selection') this.HideSelection();
1260 var rowSize = contentEndPos - contentStartPos;
1261 this.buffer.setWindow(contentStartPos, rowSize );
1262 var blankSize = this.pageSize - rowSize;
1263 var blankOffset = viewPrecedesBuffer ? 0: rowSize;
1264 var contentOffset = viewPrecedesBuffer ? blankSize: 0;
1266 for (var r=0; r < rowSize; r++) { //initialize what we have
1267 for (var c=0; c < this.columns.length; c++)
1268 this.columns[c].displayValue(r + contentOffset);
1270 for (var i=0; i < blankSize; i++) // blank out the rest
1271 this.blankRow(i + blankOffset);
1272 if (this.options.highlightElem=='selection') this.ShowSelection();
1273 this.isPartialBlank = blankSize > 0;
1274 this.lastRowPos = startPos;
1275 Rico.writeDebugMsg("refreshContents complete, startPos="+startPos);
1276 // Check if user has set a onRefreshComplete function
1277 if (onRefreshComplete != null)
1278 onRefreshComplete(contentStartPos+1,contentEndPos);
1281 scrollToRow: function(rowOffset) {
1282 var p=this.rowToPixel(rowOffset);
1283 Rico.writeDebugMsg("scrollToRow, rowOffset="+rowOffset+" pixel="+p);
1284 this.scrollDiv.scrollTop = p; // this causes a scroll event
1285 if ( this.options.onscroll )
1286 this.options.onscroll( this, rowOffset );
1289 scrollUp: function() {
1290 this.moveRelative(-1);
1293 scrollDown: function() {
1294 this.moveRelative(1);
1297 pageUp: function() {
1298 this.moveRelative(-this.pageSize);
1301 pageDown: function() {
1302 this.moveRelative(this.pageSize);
1305 adjustRow: function(rowOffset) {
1306 var notdisp=this.topOfLastPage();
1307 if (notdisp == 0 || !rowOffset) return 0;
1308 return Math.min(notdisp,rowOffset);
1311 rowToPixel: function(rowOffset) {
1312 return this.adjustRow(rowOffset) * this.rowHeight;
1316 * @returns row to display at top of scroll div
1318 pixeltorow: function(p) {
1319 var notdisp=this.topOfLastPage();
1320 if (notdisp == 0) return 0;
1321 var prow=parseInt(p/this.rowHeight);
1322 return Math.min(notdisp,prow);
1325 moveRelative: function(relOffset) {
1326 newoffset=Math.max(this.scrollDiv.scrollTop+relOffset*this.rowHeight,0);
1327 newoffset=Math.min(newoffset,this.scrollDiv.scrollHeight);
1328 //Rico.writeDebugMsg("moveRelative, newoffset="+newoffset);
1329 this.scrollDiv.scrollTop=newoffset;
1332 pluginScroll: function() {
1333 if (this.scrollPluggedIn) return;
1334 Rico.writeDebugMsg("pluginScroll: wheelEvent="+this.wheelEvent);
1335 Event.observe(this.scrollDiv,"scroll",this.scrollEventFunc, false);
1336 for (var t=0; t<2; t++)
1337 Event.observe(this.tabs[t],this.wheelEvent,this.wheelEventFunc, false);
1338 this.scrollPluggedIn=true;
1341 unplugScroll: function() {
1342 if (!this.scrollPluggedIn) return;
1343 Rico.writeDebugMsg("unplugScroll");
1344 Event.stopObserving(this.scrollDiv,"scroll", this.scrollEventFunc , false);
1345 for (var t=0; t<2; t++)
1346 Event.stopObserving(this.tabs[t],this.wheelEvent,this.wheelEventFunc, false);
1347 this.scrollPluggedIn=false;
1350 handleWheel: function(e) {
1353 if (Prototype.Browser.Opera)
1354 delta = e.wheelDelta/120;
1355 else if (Prototype.Browser.WebKit)
1356 delta = -e.wheelDelta/12;
1358 delta = -e.wheelDelta/120;
1359 } else if (e.detail) {
1360 delta = e.detail/3; /* Mozilla/Gecko */
1362 if (delta) this.moveRelative(delta);
1367 handleScroll: function(e) {
1368 if ( this.scrollTimeout )
1369 clearTimeout( this.scrollTimeout );
1370 this.setHorizontalScroll();
1371 var scrtop=this.scrollDiv.scrollTop;
1372 var vscrollDiff = this.lastScrollPos-scrtop;
1373 if (vscrollDiff == 0.00) return;
1374 var newrow=this.pixeltorow(scrtop);
1375 if (newrow == this.lastRowPos && !this.isPartialBlank && !this.isBlank) return;
1376 var stamp1 = new Date();
1377 //Rico.writeDebugMsg("handleScroll, newrow="+newrow+" scrtop="+scrtop);
1378 this.buffer.fetch(newrow);
1379 if (this.options.onscroll) this.options.onscroll(this, newrow);
1380 this.scrollTimeout = setTimeout(this.scrollIdle.bind(this), 1200 );
1381 this.lastScrollPos = this.scrollDiv.scrollTop;
1382 var stamp2 = new Date();
1383 //Rico.writeDebugMsg("handleScroll, time="+(stamp2.getTime()-stamp1.getTime()));
1386 scrollIdle: function() {
1387 if ( this.options.onscrollidle )
1388 this.options.onscrollidle();
1391 printAll: function(exportType) {
1393 this.buffer.exportAllRows(this.exportBuffer.bind(this),this.exportFinish.bind(this,exportType));
1397 * Send all rows to print window
1399 exportBuffer: function(rows) {
1400 for(var r=0; r < rows.length; r++) {
1401 this.exportText+="<tr>";
1402 for (var c=0; c<this.columns.length; c++) {
1403 if (this.columns[c].visible)
1404 this.exportText+="<td>"+this.columns[c]._format(rows[r][c].content)+"</td>";
1406 this.exportText+="</tr>";
1413 Object.extend(Rico.TableColumn.prototype, {
1415 initialize: function(liveGrid,colIdx,hdrInfo,tabIdx) {
1416 this.baseInit(liveGrid,colIdx,hdrInfo,tabIdx);
1417 Rico.writeDebugMsg(" sortable="+this.sortable+" filterable="+this.filterable+" hideable="+this.hideable+" isNullable="+this.isNullable+' isText='+this.isText);
1418 this.fixHeaders(this.liveGrid.tableId, this.options.hdrIconsFirst);
1419 if (this.format.type=='control' && this.format.control) {
1420 // copy all properties/methods that start with '_'
1421 if (typeof this.format.control=='string')
1422 this.format.control=eval(this.format.control);
1423 for (var property in this.format.control)
1424 if (property.charAt(0)=='_') {
1425 Rico.writeDebugMsg("Copying control property "+property);
1426 this[property] = this.format.control[property];
1428 } else if (this['format_'+this.format.type]) {
1429 this._format=this['format_'+this.format.type].bind(this);
1433 sortAsc: function() {
1434 this.setColumnSort(Rico.TableColumn.SORT_ASC);
1437 sortDesc: function() {
1438 this.setColumnSort(Rico.TableColumn.SORT_DESC);
1441 setColumnSort: function(direction) {
1442 this.liveGrid.clearSort();
1443 this.setSorted(direction);
1444 if (this.liveGrid.options.saveColumnInfo.sort)
1445 this.liveGrid.setCookie();
1446 if (this.options.sortHandler)
1447 this.options.sortHandler();
1450 isSortable: function() {
1451 return this.sortable;
1454 isSorted: function() {
1455 return this.currentSort != Rico.TableColumn.UNSORTED;
1458 getSortDirection: function() {
1459 return this.currentSort;
1462 toggleSort: function() {
1463 if (this.liveGrid.buffer && this.liveGrid.buffer.totalRows==0) return;
1464 if (this.currentSort == Rico.TableColumn.SORT_ASC)
1470 setUnsorted: function() {
1471 this.setSorted(Rico.TableColumn.UNSORTED);
1475 * direction must be one of Rico.TableColumn.UNSORTED, .SORT_ASC, or .SORT_DESC
1477 setSorted: function(direction) {
1478 this.currentSort = direction;
1481 canFilter: function() {
1482 return this.filterable;
1485 getFilterText: function() {
1487 for (var i=0; i<this.filterValues.length; i++) {
1488 var v=this.filterValues[i];
1489 if (v!=null && v.match(/<span\s+class=(['"]?)ricolookup\1>(.*)<\/span>/i))
1490 vals.push(RegExp.leftContext);
1494 switch (this.filterOp) {
1495 case 'EQ': return vals[0];
1496 case 'NE': return 'not: '+vals.join(', ');
1497 case 'LE': return '<= '+vals[0];
1498 case 'GE': return '>= '+vals[0];
1499 case 'LIKE': return 'like: '+vals[0];
1500 case 'NULL': return '<empty>';
1501 case 'NOTNULL': return '<not empty>';
1506 getFilterQueryParm: function() {
1507 if (this.filterType == Rico.TableColumn.UNFILTERED) return '';
1508 var retval='&f['+this.index+'][op]='+this.filterOp;
1509 retval+='&f['+this.index+'][len]='+this.filterValues.length
1510 for (var i=0; i<this.filterValues.length; i++)
1511 retval+='&f['+this.index+']['+i+']='+escape(this.filterValues[i]);
1515 setUnfiltered: function(skipHandler) {
1516 this.filterType = Rico.TableColumn.UNFILTERED;
1517 if (this.liveGrid.options.saveColumnInfo.filter)
1518 this.liveGrid.setCookie();
1519 if (this.removeFilterFunc)
1520 this.removeFilterFunc();
1521 if (this.options.filterHandler && !skipHandler)
1522 this.options.filterHandler();
1525 setFilterEQ: function() {
1526 if (this.userFilter=='' && this.isNullable)
1527 this.setUserFilter('NULL');
1529 this.setUserFilter('EQ');
1531 setFilterNE: function() {
1532 if (this.userFilter=='' && this.isNullable)
1533 this.setUserFilter('NOTNULL');
1535 this.setUserFilter('NE');
1537 addFilterNE: function() {
1538 this.filterValues.push(this.userFilter);
1539 if (this.liveGrid.options.saveColumnInfo.filter)
1540 this.liveGrid.setCookie();
1541 if (this.options.filterHandler)
1542 this.options.filterHandler();
1544 setFilterGE: function() { this.setUserFilter('GE'); },
1545 setFilterLE: function() { this.setUserFilter('LE'); },
1546 setFilterKW: function() {
1547 var keyword=prompt(RicoTranslate.getPhrase("Enter keyword to search for")+RicoTranslate.getPhrase(" (use * as a wildcard):"),'');
1548 if (keyword!='' && keyword!=null) {
1549 if (keyword.indexOf('*')==-1) keyword='*'+keyword+'*';
1550 this.setFilter('LIKE',keyword,Rico.TableColumn.USERFILTER);
1552 this.liveGrid.cancelMenu();
1556 setUserFilter: function(relop) {
1557 this.setFilter(relop,this.userFilter,Rico.TableColumn.USERFILTER);
1560 setSystemFilter: function(relop,filter) {
1561 this.setFilter(relop,filter,Rico.TableColumn.SYSTEMFILTER);
1564 setFilter: function(relop,filter,type,removeFilterFunc) {
1565 this.filterValues = [filter];
1566 this.filterType = type;
1567 this.filterOp = relop;
1568 if (type == Rico.TableColumn.USERFILTER && this.liveGrid.options.saveColumnInfo.filter)
1569 this.liveGrid.setCookie();
1570 this.removeFilterFunc=removeFilterFunc;
1571 if (this.options.filterHandler)
1572 this.options.filterHandler();
1575 isFiltered: function() {
1576 return this.filterType == Rico.TableColumn.USERFILTER;
1579 format_text: function(v) {
1580 if (typeof v!='string')
1583 return v.stripTags();
1586 format_showTags: function(v) {
1587 if (typeof v!='string')
1590 return v.replace(/&/g, '&').replace(/</g,'<').replace(/>/g,'>');
1593 format_number: function(v) {
1594 if (typeof v=='undefined' || v=='' || v==null)
1597 return v.formatNumber(this.format);
1600 format_datetime: function(v) {
1601 if (typeof v=='undefined' || v=='' || v==null)
1606 return d.formatDate(this.format.dateFmt || 'translateDateTime');
1610 format_date: function(v) {
1611 if (typeof v=='undefined' || v==null || v=='')
1615 if (!d.setISO8601(v)) return v;
1616 return d.formatDate(this.format.dateFmt || 'translateDate');
1620 fixHeaders: function(prefix, iconsfirst) {
1621 if (this.sortable) {
1622 switch (this.options.headingSort) {
1624 var a=RicoUtil.wrapChildren(this.hdrCellDiv,'ricoSort',undefined,'a')
1626 a.onclick = this.toggleSort.bindAsEventListener(this);
1629 this.hdrCellDiv.onclick = this.toggleSort.bindAsEventListener(this);
1633 this.imgFilter = document.createElement('img');
1634 this.imgFilter.style.display='none';
1635 this.imgFilter.src=Rico.imgDir+this.options.filterImg;
1636 this.imgFilter.className='ricoLG_HdrIcon';
1637 this.imgSort = document.createElement('img');
1638 this.imgSort.style.display='none';
1639 this.imgSort.className='ricoLG_HdrIcon';
1641 this.hdrCellDiv.insertBefore(this.imgSort,this.hdrCellDiv.firstChild);
1642 this.hdrCellDiv.insertBefore(this.imgFilter,this.hdrCellDiv.firstChild);
1644 this.hdrCellDiv.appendChild(this.imgFilter);
1645 this.hdrCellDiv.appendChild(this.imgSort);
1649 getValue: function(windowRow) {
1650 return this.liveGrid.buffer.getWindowValue(windowRow,this.index);
1653 getFormattedValue: function(windowRow) {
1654 return this._format(this.getValue(windowRow));
1657 getBufferCell: function(windowRow) {
1658 return this.liveGrid.buffer.getWindowCell(windowRow,this.index);
1661 setValue: function(windowRow,newval) {
1662 this.liveGrid.buffer.setWindowValue(windowRow,this.index,newval);
1665 _format: function(v) {
1669 _display: function(v,gridCell) {
1670 gridCell.innerHTML=this._format(v);
1673 displayValue: function(windowRow) {
1674 var bufCell=this.getBufferCell(windowRow);
1676 this.clearCell(windowRow);
1679 var gridCell=this.cell(windowRow);
1680 this._display(bufCell.content,gridCell,windowRow);
1681 var acceptAttr=this.liveGrid.buffer.options.acceptAttr;
1682 for (var k=0; k<acceptAttr.length; k++) {
1683 var bufAttr=bufCell['_'+acceptAttr[k]] || '';
1684 switch (acceptAttr[k]) {
1685 case 'style': gridCell.style.cssText=bufAttr; break;
1686 case 'class': gridCell.className=bufAttr; break;
1687 default: gridCell['_'+acceptAttr[k]]=bufAttr; break;
1694 Rico.TableColumn.checkbox = Class.create();
1696 Rico.TableColumn.checkbox.prototype = {
1698 initialize: function(checkedValue, uncheckedValue, defaultValue, readOnly) {
1699 this._checkedValue=checkedValue;
1700 this._uncheckedValue=uncheckedValue;
1701 this._defaultValue=defaultValue || false;
1702 this._readOnly=readOnly || false;
1703 this._checkboxes=[];
1706 _create: function(gridCell,windowRow) {
1707 this._checkboxes[windowRow]=RicoUtil.createFormField(gridCell,'input','checkbox',this.liveGrid.tableId+'_chkbox_'+this.index+'_'+windowRow);
1708 this._clear(gridCell,windowRow);
1710 this._checkboxes[windowRow].disabled=true;
1712 Event.observe(this._checkboxes[windowRow], "click", this._onclick.bindAsEventListener(this), false);
1715 _onclick: function(e) {
1716 var elem=Event.element(e);
1717 var windowRow=parseInt(elem.id.split(/_/).pop());
1718 var newval=elem.checked ? this._checkedValue : this._uncheckedValue;
1719 this.setValue(windowRow,newval);
1722 _clear: function(gridCell,windowRow) {
1723 this._checkboxes[windowRow].checked=this._defaultValue;
1726 _display: function(v,gridCell,windowRow) {
1727 this._checkboxes[windowRow].checked=(v==this._checkedValue);
1732 Rico.TableColumn.link = Class.create();
1734 Rico.TableColumn.link.prototype = {
1736 initialize: function(href,target) {
1738 this._target=target;
1742 _create: function(gridCell,windowRow) {
1743 this._anchors[windowRow]=RicoUtil.createFormField(gridCell,'a',null,this.liveGrid.tableId+'_a_'+this.index+'_'+windowRow);
1744 if (this._target) this._anchors[windowRow].target=this._target;
1745 this._clear(gridCell,windowRow);
1748 _clear: function(gridCell,windowRow) {
1749 this._anchors[windowRow].href='';
1750 this._anchors[windowRow].innerHTML='';
1753 _display: function(v,gridCell,windowRow) {
1754 this._anchors[windowRow].innerHTML=v;
1755 var getWindowValue=this.liveGrid.buffer.getWindowValue.bind(this.liveGrid.buffer);
1756 this._anchors[windowRow].href=this._href.replace(/\{\d+\}/g,
1758 var colIdx=parseInt($1.substr(1));
1759 return getWindowValue(windowRow,colIdx);
1766 Rico.TableColumn.lookup = Class.create();
1768 Rico.TableColumn.lookup.prototype = {
1770 initialize: function(map, defaultCode, defaultDesc) {
1772 this._defaultCode=defaultCode || '';
1773 this._defaultDesc=defaultDesc || ' ';
1774 this._sortfunc=this._sortvalue.bind(this);
1776 this._descriptions=[];
1779 _create: function(gridCell,windowRow) {
1780 this._descriptions[windowRow]=RicoUtil.createFormField(gridCell,'span',null,this.liveGrid.tableId+'_desc_'+this.index+'_'+windowRow);
1781 this._codes[windowRow]=RicoUtil.createFormField(gridCell,'input','hidden',this.liveGrid.tableId+'_code_'+this.index+'_'+windowRow);
1782 this._clear(gridCell,windowRow);
1785 _clear: function(gridCell,windowRow) {
1786 this._codes[windowRow].value=this._defaultCode;
1787 this._descriptions[windowRow].innerHTML=this._defaultDesc;
1790 _sortvalue: function(v) {
1791 return this._getdesc(v).replace(/&/g, '&').replace(/</g,'<').replace(/>/g,'>').replace(/ /g,' ');
1794 _getdesc: function(v) {
1795 var desc=this._map[v];
1796 return (typeof desc=='string') ? desc : this._defaultDesc;
1799 _display: function(v,gridCell,windowRow) {
1800 this._codes[windowRow].value=v;
1801 this._descriptions[windowRow].innerHTML=this._getdesc(v);
1806 Rico.includeLoaded('ricoLiveGrid.js');