2 * (c) 2005-2009 Richard Cowin (http://openrico.org)
3 * (c) 2005-2009 Matt Brown (http://dowdybrown.com)
5 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
6 * file except in compliance with the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software distributed under the
11 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
12 * either express or implied. See the License for the specific language governing permissions
13 * and limitations under the License.
16 if(typeof Rico=='undefined') throw("SimpleGrid requires the Rico JavaScript framework");
18 Rico.SimpleGrid = function(tableId, options) {
19 this.initialize(tableId, options);
22 Rico.SimpleGrid.prototype = {
24 * @class Create & manage an unbuffered grid.
25 * Supports: frozen columns & headings, resizable columns.
26 * @extends Rico.GridCommon
29 initialize: function( tableId, options ) {
30 Rico.extend(this, Rico.GridCommon);
32 Rico.setDebugArea(tableId+"_debugmsgs"); // if used, this should be a textarea
33 Rico.extend(this.options, options || {});
34 this.tableId = tableId;
35 Rico.log("SimpleGrid initialize start: "+tableId);
\r
37 this.hdrTabs=new Array(2);
38 this.simpleGridInit();
39 Rico.log("SimpleGrid initialize end: "+tableId);
\r
42 simpleGridInit: function() {
45 Rico.log("simpleGridInit "+i);
\r
46 this.tabs[i]=document.getElementById(this.tableId+'_tab'+i);
47 if (!this.tabs[i]) return;
48 this.hdrTabs[i]=document.getElementById(this.tableId+'_tab'+i+'h');
49 if (!this.hdrTabs[i]) return;
50 //if (i==0) this.tabs[i].style.position='absolute';
51 //if (i==0) this.tabs[i].style.left='0px';
52 //this.hdrTabs[i].style.position='absolute';
53 //this.hdrTabs[i].style.top='0px';
54 //this.hdrTabs[i].style.zIndex=1;
55 this.thead[i]=this.hdrTabs[i];
56 this.tbody[i]=this.tabs[i];
57 this.headerColCnt = this.getColumnInfo(this.hdrTabs[i].rows);
58 if (i==0) this.options.frozenColumns=this.headerColCnt;
59 if (Rico.theme.gridheader) Rico.addClass(this.thead[i],Rico.theme.gridheader);
60 if (Rico.theme.gridcontent) Rico.addClass(this.tbody[i],Rico.theme.gridcontent);
62 if (this.headerColCnt==0) {
63 alert('ERROR: no columns found in "'+this.tableId+'"');
66 //this.hdrHt=Math.max(Rico.nan2zero(this.hdrTabs[0].offsetHeight),this.hdrTabs[1].offsetHeight);
67 //for (i=0; i<2; i++) {
68 // if (i==0) this.tabs[i].style.top=this.hdrHt+'px';
70 this.createColumnArray('SimpleGridColumn');
71 this.pageSize=this.columns[0].dataColDiv.childNodes.length;
73 if (typeof(this.options.FilterLocation)=='number')
74 this.createFilters(this.options.FilterLocation);
75 this.attachMenuEvents();
76 this.scrollEventFunc=Rico.eventHandle(this,'handleScroll');
78 if (this.options.windowResize)
79 Rico.eventBind(window,"resize", Rico.eventHandle(this,'sizeDivs'), false);
82 // return id string for a filter element
83 filterId: function(colnum) {
84 return 'RicoFilter_'+this.tableId+'_'+colnum;
87 // create filter elements on heading row r
88 createFilters: function(r) {
90 r=this.addHeadingRow();
93 for( var c=0; c < this.headerColCnt; c++ ) {
94 var col=this.columns[c];
96 if (typeof fmt.filterUI!='string') continue;
97 var cell=this.hdrCells[r][c].cell;
98 var field,name=this.filterId(c);
\r
99 var divs=cell.getElementsByTagName('div');
100 switch (fmt.filterUI.charAt(0)) {
102 field=Rico.createFormField(divs[1],'input','text',name,name);
103 var size=fmt.filterUI.match(/\d+/);
104 field.maxLength=fmt.Length || 50;
\r
105 field.size=size ? parseInt(size,10) : 10;
106 Rico.eventBind(field,'keyup',Rico.eventHandle(col,'filterKeypress'),false);
\r
109 field=Rico.createFormField(divs[1],'select',null,name);
\r
110 Rico.addSelectOption(field,this.options.FilterAllToken,Rico.getPhraseById("filterAll"));
111 this.getFilterValues(col);
112 var keys=Rico.keys(col.filterHash);
114 for (var i=0; i<keys.length; i++)
115 Rico.addSelectOption(field,keys[i],keys[i] || Rico.getPhraseById("filterBlank"));
\r
116 Rico.eventBind(field,'change',Rico.eventHandle(col,'filterChange'),false);
\r
120 this.initFilterImage(r);
123 getFilterValues: function(col) {
126 for (var i=0; i<n; i++) {
127 var v=Rico.getInnerText(col.cell(i));
137 // hide filtered rows
138 applyFilters: function() {
139 // rows to display will be the intersection of all filterRows arrays
141 for (var c=0; c<this.columns.length; c++) {
142 if (this.columns[c].filterRows)
143 fcols.push(this.columns[c].filterRows);
145 if (fcols.length==0) {
146 // no filters are set
150 for (var r=0; r<this.pageSize; r++) {
152 for (var j=0; j<fcols.length; j++) {
153 if (fcols[j].indexOf(r)==-1) {
166 handleScroll: function(e) {
167 var newTop=(-this.scrollDiv.scrollTop)+'px';
168 this.tabs[0].style.marginTop=newTop;
169 this.setHorizontalScroll();
173 * Register a menu that will only be used in the scrolling part of the grid.
174 * If submenus are used, they must be registered after the main menu.
176 registerScrollMenu: function(menu) {
177 if (!this.menu) this.menu=menu;
179 menu.showmenu=menu.showSimpleMenu;
180 menu.showSubMenu=menu.showSimpleSubMenu;
181 menu.createDiv(this.outerDiv);
184 handleMenuClick: function(e) {
185 if (!this.menu) return;
187 this.menuCell=Rico.getParentByTagName(Rico.eventElement(e),'div');
188 this.highlightEnabled=false;
189 if (this.hideScroll) this.scrollDiv.style.overflow="hidden";
190 if (this.menu.buildGridMenu) this.menu.buildGridMenu(this.menuCell);
191 this.menu.showmenu(e,this.closeMenu.bind(this));
194 closeMenu: function() {
195 if (this.hideScroll) this.scrollDiv.style.overflow="";
196 this.highlightEnabled=true;
199 sizeDivs: function() {
200 if (this.outerDiv.offsetParent.style.display=='none') return;
202 var maxHt=Math.max(this.options.maxHt || this.availHt(), 50);
203 var totHt=Math.min(this.hdrHt+this.dataHt, maxHt);
204 Rico.log('sizeDivs '+this.tableId+': hdrHt='+this.hdrHt+' dataHt='+this.dataHt);
205 this.dataHt=totHt-this.hdrHt;
206 if (this.scrWi>0) this.dataHt+=this.options.scrollBarWidth;
207 this.scrollDiv.style.height=this.dataHt+'px';
208 this.frozenTabs.style.height=this.scrollDiv.clientHeight+'px';
210 this.innerDiv.style.width=(this.scrWi-this.options.scrollBarWidth+divAdjust)+'px';
211 //this.innerDiv.style.height=(this.hdrHt+1)+'px';
213 this.resizeDiv.style.height=totHt+'px';
215 //this.outerDiv.style.height=(totHt+this.options.scrollBarWidth)+'px';
220 * Copies all rows to a new window as a simple html table.
222 printVisible: function() {
223 this.showMsg(Rico.getPhraseById('exportInProgress'));
224 Rico.runLater(10,this,'_printVisible'); // allow message to paint
227 _printVisible: function() {
229 var exportStyles=this.getExportStyles(this.tbody[0]);
230 for(var r=0; r < this.pageSize; r++) {
231 if (this.columns[0].cell(r).style.display=='none') continue;
233 for (var c=0; c<this.columns.length; c++) {
234 var col=this.columns[c];
236 var v=col.getFormattedValue(r, !this.options.exportImgTags, !this.options.exportFormFields, 'NoExport');
237 if (col.format.exportPrefix) v=col.format.exportPrefix+v;
238 if (v=='') v=' ';
239 exportText+="<td style='"+this.exportStyle(col.cell(r),exportStyles)+"'>"+v+"</td>";
242 this.exportRows.push(exportText);
244 this.exportFinish(exportType);
248 * Hide a row in the grid.
249 * sizeDivs() should be called after this function has completed.
251 hideRow: function(rownum) {
252 if (this.columns[0].cell(rownum).style.display=='none') return;
253 for (var i=0; i<this.columns.length; i++)
254 this.columns[i].cell(rownum).style.display='none';
258 * Unhide a row in the grid.
259 * sizeDivs() should be called after this function has completed.
261 showRow: function(rownum) {
262 if (this.columns[0].cell(rownum).style.display=='') return;
263 for (var i=0; i<this.columns.length; i++)
264 this.columns[i].cell(rownum).style.display='';
268 * Search for rows that contain SearchString in column ColIdx.
269 * If ShowMatch is false, then matching rows are hidden, if true then mismatching rows are hidden.
271 searchRows: function(ColIdx,SearchString,ShowMatch) {
\r
272 if (!SearchString) return;
\r
273 var re=new RegExp(SearchString);
\r
274 var rowcnt=this.columns[ColIdx].numRows();
\r
275 for(var r=0; r<rowcnt; r++) {
\r
276 var txt=this.cell(r,ColIdx).innerHTML;
\r
277 var matched=(txt.match(re) != null);
\r
278 if (matched != ShowMatch) this.hideRow(r);
\r
281 this.handleScroll();
\r
285 * Unhide all rows in the grid
287 showAllRows: function() {
288 for (var i=0; i<this.pageSize; i++)
293 openPopup: function(elem,popupobj) {
294 while (elem && !Rico.hasClass(elem,'ricoLG_cell'))
295 elem=elem.parentNode;
296 if (!elem) return false;
297 var td=Rico.getParentByTagName(elem,'td');
299 var newLeft=Math.floor(td.offsetLeft-this.scrollDiv.scrollLeft+td.offsetWidth/2);
300 if (this.direction == 'rtl') {
301 if (newLeft > this.width) newLeft-=this.width;
303 if (newLeft+this.width+this.options.margin > this.scrollDiv.clientWidth) newLeft-=this.width;
305 popupobj.divPopup.style.visibility="hidden";
306 popupobj.divPopup.style.display="block";
307 var contentHt=popupobj.divPopup.offsetHeight;
308 var newTop=Math.floor(elem.offsetTop-this.scrollDiv.scrollTop+elem.offsetHeight/2);
309 if (newTop+contentHt+popupobj.options.margin > this.scrollDiv.clientHeight)
310 newTop=Math.max(newTop-contentHt,0);
311 popupobj.openPopup(this.frzWi+newLeft,this.hdrHt+newTop);
312 popupobj.divPopup.style.visibility ="visible";
319 Rico.extend(Rico.Menu.prototype, {
321 showSimpleMenu: function(e,hideFunc) {
323 this.hideFunc=hideFunc;
324 if (this.div.childNodes.length==0) {
328 var elem=Rico.eventElement(e);
329 this.grid.openPopup(elem,this);
333 showSimpleSubMenu: function(a,submenu) {
334 if (this.openSubMenu) this.hideSubMenu();
335 this.openSubMenu=submenu;
336 this.openMenuAnchor=a;
337 if (a.className=='ricoSubMenu') a.className='ricoSubMenuOpen';
338 var top=parseInt(this.div.style.top,10);
339 var left=parseInt(this.div.style.left,10);
340 submenu.openPopup(left+a.offsetWidth,top+a.offsetTop);
341 submenu.div.style.visibility ="visible";
348 Rico.SimpleGridColumn = function(grid,colIdx,hdrInfo,tabIdx) {
349 this.initialize(grid,colIdx,hdrInfo,tabIdx);
352 Rico.SimpleGridColumn.prototype = {
354 * @class Implements a SimpleGrid column
355 * @extends Rico.TableColumnBase
358 initialize: function(grid,colIdx,hdrInfo,tabIdx) {
359 Rico.extend(this, new Rico.TableColumnBase());
360 this.baseInit(grid,colIdx,hdrInfo,tabIdx);
363 setUnfiltered: function() {
364 this.filterRows=null;
367 filterChange: function(e) {
\r
368 var selbox=Rico.eventElement(e);
369 if (selbox.value==this.liveGrid.options.FilterAllToken)
\r
370 this.setUnfiltered();
\r
372 this.filterRows=this.filterHash[selbox.value];
373 this.liveGrid.applyFilters();
376 filterKeypress: function(e) {
\r
377 var txtbox=Rico.eventElement(e);
378 if (typeof this.lastKeyFilter != 'string') this.lastKeyFilter='';
\r
379 if (this.lastKeyFilter==txtbox.value) return;
\r
380 var v=txtbox.value;
\r
381 Rico.log("filterKeypress: "+this.index+' '+v);
\r
382 this.lastKeyFilter=v;
384 v=v.replace('\\','\\\\');
385 v=v.replace('(','\\(').replace(')','\\)');
386 v=v.replace('.','\\.');
387 if (this.format.filterUI.indexOf('^') > 0) v='^'+v;
388 var re=new RegExp(v,'i');
\r
390 var n=this.numRows();
391 for (var i=0; i<n; i++) {
392 var celltxt=Rico.getInnerText(this.cell(i));
393 if (celltxt.match(re)) this.filterRows.push(i);
396 this.setUnfiltered();
\r
398 this.liveGrid.applyFilters();