2 * (c) 2005-2009 Richard Cowin (http://openrico.org)
3 * (c) 2005-2009 Matt Brown (http://dowdybrown.com)
5 * Rico is licensed under the Apache License, Version 2.0 (the "License"); you may not use this
6 * file except in compliance with the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software distributed under the
11 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
12 * either express or implied. See the License for the specific language governing permissions
13 * and limitations under the License.
17 if(typeof Rico=='undefined') throw("SimpleGrid requires the Rico JavaScript framework");
18 if(typeof RicoUtil=='undefined') throw("SimpleGrid requires the RicoUtil Library");
19 if(typeof RicoTranslate=='undefined') throw("SimpleGrid requires the RicoTranslate Library");
22 Rico.SimpleGrid = Class.create(
23 /** @lends Rico.SimpleGrid# */
26 * @class Create & manage an unbuffered grid.
27 * Supports: frozen columns & headings, resizable columns.
28 * @extends Rico.GridCommon
31 initialize: function( tableId, options ) {
32 Object.extend(this, new Rico.GridCommon);
34 Rico.setDebugArea(tableId+"_debugmsgs"); // if used, this should be a textarea
35 Object.extend(this.options, options || {});
36 this.tableId = tableId;
37 Rico.writeDebugMsg("SimpleGrid initialize start: "+tableId);
39 this.hdrTabs=new Array(2);
40 this.simpleGridInit();
41 Rico.writeDebugMsg("SimpleGrid initialize end: "+tableId);
44 simpleGridInit: function() {
47 Rico.writeDebugMsg("simpleGridInit "+i);
48 this.tabs[i]=$(this.tableId+'_tab'+i);
49 if (!this.tabs[i]) return;
50 this.hdrTabs[i]=$(this.tableId+'_tab'+i+'h');
51 if (!this.hdrTabs[i]) return;
52 if (i==0) this.tabs[i].style.position='absolute';
53 if (i==0) this.tabs[i].style.left='0px';
54 this.hdrTabs[i].style.position='absolute';
55 this.hdrTabs[i].style.top='0px';
56 this.hdrTabs[i].style.zIndex=1;
57 this.thead[i]=this.hdrTabs[i];
58 this.tbody[i]=this.tabs[i];
59 this.headerColCnt = this.getColumnInfo(this.hdrTabs[i].rows);
60 if (i==0) this.options.frozenColumns=this.headerColCnt;
62 if (this.headerColCnt==0) {
63 alert('ERROR: no columns found in "'+this.tableId+'"');
66 this.hdrHt=Math.max(RicoUtil.nan2zero(this.hdrTabs[0].offsetHeight),this.hdrTabs[1].offsetHeight);
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=this.handleScroll.bindAsEventListener(this);
78 if (this.options.windowResize)
79 Event.observe(window,"resize", this.sizeDivs.bindAsEventListener(this), 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);
99 var divs=cell.getElementsByTagName('div');
100 switch (fmt.filterUI.charAt(0)) {
102 field=RicoUtil.createFormField(divs[1],'input','text',name,name);
103 var size=fmt.filterUI.match(/\d+/);
104 field.maxLength=fmt.Length || 50;
105 field.size=size ? parseInt(size,10) : 10;
106 Event.observe(field,'keyup',col.filterKeypress.bindAsEventListener(col),false);
109 field=RicoUtil.createFormField(divs[1],'select',null,name);
110 RicoUtil.addSelectOption(field,this.options.FilterAllToken,RicoTranslate.getPhraseById("filterAll"));
111 this.getFilterValues(col);
112 var keys=col.filterHash.keys();
114 for (var i=0; i<keys.length; i++)
115 RicoUtil.addSelectOption(field,keys[i],keys[i] || RicoTranslate.getPhraseById("filterBlank"));
116 Event.observe(field,'change',col.filterChange.bindAsEventListener(col),false);
120 this.initFilterImage(r);
123 getFilterValues: function(col) {
126 for (var i=0; i<n; i++) {
127 var v=RicoUtil.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.hdrHt-this.scrollDiv.scrollTop)+'px';
168 this.tabs[0].style.top=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=RicoUtil.getParentByTagName(Event.element(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.writeDebugMsg('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';
209 this.innerDiv.style.width=(this.scrWi-this.options.scrollBarWidth+divAdjust)+'px';
210 this.innerDiv.style.height=this.hdrHt+'px';
212 this.resizeDiv.style.height=this.frozenTabs.style.height=totHt+'px';
213 this.outerDiv.style.height=(totHt+this.options.scrollBarWidth)+'px';
217 _printVisible: function(exportType) {
219 for(var r=0; r < this.pageSize; r++) {
220 if (this.columns[0].cell(r).style.display=='none') continue;
222 for (var c=0; c<this.columns.length; c++) {
223 var col=this.columns[c];
225 var v=col.getFormattedValue(r, !this.options.exportImgTags, !this.options.exportFormFields, 'NoExport');
226 if (col.format.exportPrefix) v=col.format.exportPrefix+v;
227 if (v=='') v=' ';
228 exportText+="<td style='"+this.exportStyle(col.cell(r))+"'>"+v+"</td>";
231 this.exportRows.push(exportText);
233 this.exportFinish(exportType);
237 * Hide a row in the grid.
238 * sizeDivs() should be called after this function has completed.
240 hideRow: function(rownum) {
241 if (this.columns[0].cell(rownum).style.display=='none') return;
242 for (var i=0; i<this.columns.length; i++)
243 this.columns[i].cell(rownum).style.display='none';
247 * Unhide a row in the grid.
248 * sizeDivs() should be called after this function has completed.
250 showRow: function(rownum) {
251 if (this.columns[0].cell(rownum).style.display=='') return;
252 for (var i=0; i<this.columns.length; i++)
253 this.columns[i].cell(rownum).style.display='';
257 * Search for rows that contain SearchString in column ColIdx.
258 * If ShowMatch is false, then matching rows are hidden, if true then mismatching rows are hidden.
260 searchRows: function(ColIdx,SearchString,ShowMatch) {
261 if (!SearchString) return;
262 var re=new RegExp(SearchString);
263 var rowcnt=this.columns[ColIdx].numRows();
264 for(var r=0; r<rowcnt; r++) {
265 var txt=this.cell(r,ColIdx).innerHTML;
266 var matched=(txt.match(re) != null);
267 if (matched != ShowMatch) this.hideRow(r);
274 * Unhide all rows in the grid
276 showAllRows: function() {
277 for (var i=0; i<this.pageSize; i++)
282 openPopup: function(elem,popupobj) {
283 while (elem && !Element.hasClassName(elem,'ricoLG_cell'))
284 elem=elem.parentNode;
285 if (!elem) return false;
286 var td=RicoUtil.getParentByTagName(elem,'td');
288 var newLeft=Math.floor(td.offsetLeft-this.scrollDiv.scrollLeft+td.offsetWidth/2);
289 if (this.direction == 'rtl') {
290 if (newLeft > this.width) newLeft-=this.width;
292 if (newLeft+this.width+this.options.margin > this.scrollDiv.clientWidth) newLeft-=this.width;
294 popupobj.divPopup.style.visibility="hidden";
295 popupobj.divPopup.style.display="block";
296 var contentHt=popupobj.divPopup.offsetHeight;
297 var newTop=Math.floor(elem.offsetTop-this.scrollDiv.scrollTop+elem.offsetHeight/2);
298 if (newTop+contentHt+popupobj.options.margin > this.scrollDiv.clientHeight)
299 newTop=Math.max(newTop-contentHt,0);
300 popupobj.openPopup(this.frzWi+newLeft,this.hdrHt+newTop);
301 popupobj.divPopup.style.visibility ="visible";
308 Object.extend(Rico.Menu.prototype, {
310 showSimpleMenu: function(e,hideFunc) {
312 this.hideFunc=hideFunc;
313 if (this.div.childNodes.length==0) {
317 var elem=Event.element(e);
318 this.grid.openPopup(elem,this);
322 showSimpleSubMenu: function(a,submenu) {
323 if (this.openSubMenu) this.hideSubMenu();
324 this.openSubMenu=submenu;
325 this.openMenuAnchor=a;
326 if (a.className=='ricoSubMenu') a.className='ricoSubMenuOpen';
327 var top=parseInt(this.div.style.top,10);
328 var left=parseInt(this.div.style.left,10);
329 submenu.openPopup(left+a.offsetWidth,top+a.offsetTop);
330 submenu.div.style.visibility ="visible";
337 Rico.SimpleGridColumn = Class.create(
338 /** @lends Rico.SimpleGridColumn# */
341 * @class Implements a SimpleGrid column
342 * @extends Rico.TableColumnBase
345 initialize: function(grid,colIdx,hdrInfo,tabIdx) {
346 Object.extend(this, new Rico.TableColumnBase());
347 this.baseInit(grid,colIdx,hdrInfo,tabIdx);
350 setUnfiltered: function() {
351 this.filterRows=null;
354 filterChange: function(e) {
355 var selbox=Event.element(e);
356 if (selbox.value==this.liveGrid.options.FilterAllToken)
357 this.setUnfiltered();
359 this.filterRows=this.filterHash.get(selbox.value);
360 this.liveGrid.applyFilters();
363 filterKeypress: function(e) {
364 var txtbox=Event.element(e);
365 if (typeof this.lastKeyFilter != 'string') this.lastKeyFilter='';
366 if (this.lastKeyFilter==txtbox.value) return;
368 Rico.writeDebugMsg("filterKeypress: "+this.index+' '+v);
369 this.lastKeyFilter=v;
371 v=v.replace('\\','\\\\');
372 v=v.replace('(','\\(').replace(')','\\)');
373 v=v.replace('.','\\.');
374 if (this.format.filterUI.indexOf('^') > 0) v='^'+v;
375 var re=new RegExp(v,'i');
377 var n=this.numRows();
378 for (var i=0; i<n; i++) {
379 var celltxt=RicoUtil.getInnerText(this.cell(i));
380 if (celltxt.match(re)) this.filterRows.push(i);
383 this.setUnfiltered();
385 this.liveGrid.applyFilters();
390 Rico.includeLoaded('ricoSimpleGrid.js');