2 * (c) 2005-2011 Richard Cowin (http://openrico.org)
3 * (c) 2005-2011 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 this.thead[i]=this.hdrTabs[i];
51 this.tbody[i]=this.tabs[i];
52 this.headerColCnt = this.getColumnInfo(this.hdrTabs[i].rows);
53 if (i==0) this.options.frozenColumns=this.headerColCnt;
54 if (Rico.theme.gridheader) Rico.addClass(this.thead[i],Rico.theme.gridheader);
55 if (Rico.theme.gridcontent) Rico.addClass(this.tbody[i],Rico.theme.gridcontent);
57 if (this.headerColCnt==0) {
58 alert('ERROR: no columns found in "'+this.tableId+'"');
61 this.createColumnArray('SimpleGridColumn');
62 this.pageSize=this.columns[0].dataColDiv.childNodes.length;
64 if (typeof(this.options.FilterLocation)=='number')
65 this.createFilters(this.options.FilterLocation);
66 this.attachMenuEvents();
67 this.scrollEventFunc=Rico.eventHandle(this,'handleScroll');
69 if (this.options.windowResize)
70 Rico.eventBind(window,"resize", Rico.eventHandle(this,'sizeDivs'), false);
73 // return id string for a filter element
74 filterId: function(colnum) {
75 return 'RicoFilter_'+this.tableId+'_'+colnum;
78 // create filter elements on heading row r
79 createFilters: function(r) {
81 r=this.addHeadingRow();
84 for( var c=0; c < this.headerColCnt; c++ ) {
85 var col=this.columns[c];
87 if (typeof fmt.filterUI!='string') continue;
88 var cell=this.hdrCells[r][c].cell;
89 var field,name=this.filterId(c);
\r
90 var divs=cell.getElementsByTagName('div');
91 switch (fmt.filterUI.charAt(0)) {
93 field=Rico.createFormField(divs[1],'input','text',name,name);
94 var size=fmt.filterUI.match(/\d+/);
95 field.maxLength=fmt.Length || 50;
\r
96 field.size=size ? parseInt(size,10) : 10;
97 Rico.eventBind(field,'keyup',Rico.eventHandle(col,'filterKeypress'),false);
\r
100 field=Rico.createFormField(divs[1],'select',null,name);
\r
101 Rico.addSelectOption(field,this.options.FilterAllToken,Rico.getPhraseById("filterAll"));
102 this.getFilterValues(col);
103 var keys=Rico.keys(col.filterHash);
105 for (var i=0; i<keys.length; i++)
106 Rico.addSelectOption(field,keys[i],keys[i] || Rico.getPhraseById("filterBlank"));
\r
107 Rico.eventBind(field,'change',Rico.eventHandle(col,'filterChange'),false);
\r
111 this.initFilterImage(r);
114 getFilterValues: function(col) {
117 for (var i=0; i<n; i++) {
118 var v=Rico.getInnerText(col.cell(i));
128 // hide filtered rows
129 applyFilters: function() {
130 // rows to display will be the intersection of all filterRows arrays
132 for (var c=0; c<this.columns.length; c++) {
133 if (this.columns[c].filterRows)
134 fcols.push(this.columns[c].filterRows);
136 if (fcols.length==0) {
137 // no filters are set
141 for (var r=0; r<this.pageSize; r++) {
143 for (var j=0; j<fcols.length; j++) {
144 if (fcols[j].indexOf(r)==-1) {
157 handleScroll: function(e) {
158 var newTop=(-this.scrollDiv.scrollTop)+'px';
159 this.tabs[0].style.marginTop=newTop;
160 this.setHorizontalScroll();
164 * Register a menu that will only be used in the scrolling part of the grid.
165 * If submenus are used, they must be registered after the main menu.
167 registerScrollMenu: function(menu) {
168 if (!this.menu) this.menu=menu;
170 menu.showmenu=menu.showSimpleMenu;
171 menu.showSubMenu=menu.showSimpleSubMenu;
172 menu.createDiv(this.outerDiv);
175 handleMenuClick: function(e) {
176 if (!this.menu) return;
178 this.menuCell=Rico.getParentByTagName(Rico.eventElement(e),'div');
179 this.highlightEnabled=false;
180 if (this.hideScroll) this.scrollDiv.style.overflow="hidden";
181 if (this.menu.buildGridMenu) this.menu.buildGridMenu(this.menuCell);
182 this.menu.showmenu(e,this.closeMenu.bind(this));
185 closeMenu: function() {
186 if (this.hideScroll) this.scrollDiv.style.overflow="";
187 this.highlightEnabled=true;
190 sizeDivs: function() {
191 if (this.outerDiv.offsetParent.style.display=='none') return;
193 var maxHt=Math.max(this.options.maxHt || this.availHt(), 50);
194 var totHt=Math.min(this.hdrHt+this.dataHt, maxHt);
195 Rico.log('sizeDivs '+this.tableId+': hdrHt='+this.hdrHt+' dataHt='+this.dataHt);
196 this.dataHt=totHt-this.hdrHt;
197 if (this.scrWi>0) this.dataHt+=this.options.scrollBarWidth;
198 this.scrollDiv.style.height=this.dataHt+'px';
199 this.frozenTabs.style.height=this.scrollDiv.clientHeight+'px';
201 this.innerDiv.style.width=(this.scrWi-this.options.scrollBarWidth+divAdjust)+'px';
202 //this.innerDiv.style.height=(this.hdrHt+1)+'px';
204 this.resizeDiv.style.height=totHt+'px';
206 //this.outerDiv.style.height=(totHt+this.options.scrollBarWidth)+'px';
211 * Copies all rows to a new window as a simple html table.
213 printVisible: function() {
214 this.showMsg(Rico.getPhraseById('exportInProgress'));
215 Rico.runLater(10,this,'_printVisible'); // allow message to paint
218 _printVisible: function() {
220 var exportStyles=this.getExportStyles(this.tbody[0]);
221 for(var r=0; r < this.pageSize; r++) {
222 if (this.columns[0].cell(r).style.display=='none') continue;
224 for (var c=0; c<this.columns.length; c++) {
225 var col=this.columns[c];
227 var v=col.getFormattedValue(r, !this.options.exportImgTags, !this.options.exportFormFields, 'NoExport');
228 if (col.format.exportPrefix) v=col.format.exportPrefix+v;
229 if (v=='') v=' ';
230 exportText+="<td style='"+this.exportStyle(col.cell(r),exportStyles)+"'>"+v+"</td>";
233 this.exportRows.push(exportText);
239 * Hide a row in the grid.
240 * sizeDivs() should be called after this function has completed.
242 hideRow: function(rownum) {
243 if (this.columns[0].cell(rownum).style.display=='none') return;
244 for (var i=0; i<this.columns.length; i++)
245 this.columns[i].cell(rownum).style.display='none';
249 * Unhide a row in the grid.
250 * sizeDivs() should be called after this function has completed.
252 showRow: function(rownum) {
253 if (this.columns[0].cell(rownum).style.display=='') return;
254 for (var i=0; i<this.columns.length; i++)
255 this.columns[i].cell(rownum).style.display='';
259 * Search for rows that contain SearchString in column ColIdx.
260 * If ShowMatch is false, then matching rows are hidden, if true then mismatching rows are hidden.
262 searchRows: function(ColIdx,SearchString,ShowMatch) {
\r
263 if (!SearchString) return;
\r
264 var re=new RegExp(SearchString);
\r
265 var rowcnt=this.columns[ColIdx].numRows();
\r
266 for(var r=0; r<rowcnt; r++) {
\r
267 var txt=this.cell(r,ColIdx).innerHTML;
\r
268 var matched=(txt.match(re) != null);
\r
269 if (matched != ShowMatch) this.hideRow(r);
\r
272 this.handleScroll();
\r
276 * Unhide all rows in the grid
278 showAllRows: function() {
279 for (var i=0; i<this.pageSize; i++)
284 openPopup: function(elem,popupobj) {
285 while (elem && !Rico.hasClass(elem,'ricoLG_cell'))
286 elem=elem.parentNode;
287 if (!elem) return false;
288 var td=Rico.getParentByTagName(elem,'td');
290 var newLeft=Math.floor(td.offsetLeft-this.scrollDiv.scrollLeft+td.offsetWidth/2);
291 if (this.direction == 'rtl') {
292 if (newLeft > this.width) newLeft-=this.width;
294 if (newLeft+this.width+this.options.margin > this.scrollDiv.clientWidth) newLeft-=this.width;
296 popupobj.divPopup.style.visibility="hidden";
297 popupobj.divPopup.style.display="block";
298 var contentHt=popupobj.divPopup.offsetHeight;
299 var newTop=Math.floor(elem.offsetTop-this.scrollDiv.scrollTop+elem.offsetHeight/2);
300 if (newTop+contentHt+popupobj.options.margin > this.scrollDiv.clientHeight)
301 newTop=Math.max(newTop-contentHt,0);
302 popupobj.openPopup(this.frzWi+newLeft,this.hdrHt+newTop);
303 popupobj.divPopup.style.visibility ="visible";
310 Rico.extend(Rico.Menu.prototype, {
312 showSimpleMenu: function(e,hideFunc) {
314 this.hideFunc=hideFunc;
315 if (this.div.childNodes.length==0) {
319 var elem=Rico.eventElement(e);
320 this.grid.openPopup(elem,this);
324 showSimpleSubMenu: function(a,submenu) {
325 if (this.openSubMenu) this.hideSubMenu();
326 this.openSubMenu=submenu;
327 this.openMenuAnchor=a;
328 if (a.className=='ricoSubMenu') a.className='ricoSubMenuOpen';
329 var top=parseInt(this.div.style.top,10);
330 var left=parseInt(this.div.style.left,10);
331 submenu.openPopup(left+a.offsetWidth,top+a.offsetTop);
332 submenu.div.style.visibility ="visible";
339 Rico.SimpleGridColumn = function(grid,colIdx,hdrInfo,tabIdx) {
340 this.initialize(grid,colIdx,hdrInfo,tabIdx);
343 Rico.SimpleGridColumn.prototype = {
345 * @class Implements a SimpleGrid column
346 * @extends Rico.TableColumnBase
349 initialize: function(grid,colIdx,hdrInfo,tabIdx) {
350 Rico.extend(this, new Rico.TableColumnBase());
351 this.baseInit(grid,colIdx,hdrInfo,tabIdx);
354 setUnfiltered: function() {
355 this.filterRows=null;
358 filterChange: function(e) {
\r
359 var selbox=Rico.eventElement(e);
360 if (selbox.value==this.liveGrid.options.FilterAllToken)
\r
361 this.setUnfiltered();
\r
363 this.filterRows=this.filterHash[selbox.value];
364 this.liveGrid.applyFilters();
367 filterKeypress: function(e) {
\r
368 var txtbox=Rico.eventElement(e);
369 if (typeof this.lastKeyFilter != 'string') this.lastKeyFilter='';
\r
370 if (this.lastKeyFilter==txtbox.value) return;
\r
371 var v=txtbox.value;
\r
372 Rico.log("filterKeypress: "+this.index+' '+v);
\r
373 this.lastKeyFilter=v;
375 v=v.replace('\\','\\\\');
376 v=v.replace('(','\\(').replace(')','\\)');
377 v=v.replace('.','\\.');
378 if (this.format.filterUI.indexOf('^') > 0) v='^'+v;
379 var re=new RegExp(v,'i');
\r
381 var n=this.numRows();
382 for (var i=0; i<n; i++) {
383 var celltxt=Rico.getInnerText(this.cell(i));
384 if (celltxt.match(re)) this.filterRows.push(i);
387 this.setUnfiltered();
\r
389 this.liveGrid.applyFilters();