All Downloads are FREE. Search and download functionalities are using the official Maven repository.

echopointng.ui.resource.js.tableex.js Maven / Gradle / Ivy

Go to download

Echo2 bundled with Echo2_Extras, Echo2_FileTransfer and echopointing and various improvements/bugfixes

There is a newer version: 2.0.4
Show newest version
/*
 * This file is part of the Echo Point Project. This project is a collection of
 * Components that have extended the Echo Web Application Framework.
 * 
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 * 
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (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.mozilla.org/MPL/
 * 
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
 * the specific language governing rights and limitations under the License.
 * 
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or the
 * GNU Lesser General Public License Version 2.1 or later (the "LGPL"), in which
 * case the provisions of the GPL or the LGPL are applicable instead of those
 * above. If you wish to allow use of your version of this file only under the
 * terms of either the GPL or the LGPL, and not to allow others to use your
 * version of this file under the terms of the MPL, indicate your decision by
 * deleting the provisions above and replace them with the notice and other
 * provisions required by the GPL or the LGPL. If you do not delete the
 * provisions above, a recipient may use your version of this file under the
 * terms of any one of the MPL, the GPL or the LGPL.
 */
 /*
 * This file was made part of the EchoPointNG project on the 8th of June 2006
 * It was orginally developed by NextApp and hence..
 */
/* 
 * This file is part of the Echo Web Application Framework (hereinafter "Echo").
 * Copyright (C) 2002-2005 NextApp, Inc.
 *
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 */
  
//_________________
// Object EchoTable

/**
 * Static object/namespace for TableEx support.
 * This object/namespace should not be used externally.
 * 

* Constructor to create new EPTableEx instance. * * @param element the supported TABLE DOM element */ EPTableEx = function(elementId) { this.multipleSelect = false; this.rolloverEnabled = false; this.rolloverStyle = null; this.selectionEnabled = false; this.selectionStyle = null; this.rowCount = 0; this.selectionState = null; this.headerVisible = false; this.footerVisible = false; this.lastSelectedIndex = -1; this.ignoreMetaKeys = false; this.elementId = elementId; EP.ObjectMap.put(elementId, this); }; /** * Static object/namespace for TableEx MessageProcessor * implementation. */ EPTableEx.MessageProcessor = function() { }; /** * MessageProcessor process() implementation * (invoked by ServerMessage processor). * * @param messagePartElement the message-part element to process. */ EPTableEx.MessageProcessor.process = function(messagePartElement) { for (var i = 0; i < messagePartElement.childNodes.length; ++i) { if (messagePartElement.childNodes[i].nodeType == 1) { switch (messagePartElement.childNodes[i].tagName) { case "init": EPTableEx.MessageProcessor.processInit(messagePartElement.childNodes[i]); break; case "dispose": EPTableEx.MessageProcessor.processDispose(messagePartElement.childNodes[i]); break; } } } }; /** * Processes a dispose message to finalize the state of a * Table component that is being removed. * * @param disposeMessageElement the dispose element to process */ EPTableEx.MessageProcessor.processDispose = function(disposeMessageElement) { for (var item = disposeMessageElement.firstChild; item; item = item.nextSibling) { var elementId = item.getAttribute("eid"); EP.ObjectMap.destroy(elementId); } }; /** * Processes an init message to initialize the state of a * TableEx component that is being added. * * @param initMessageElement the init element to process */ EPTableEx.MessageProcessor.processInit = function(initMessageElement) { var rolloverStyle = initMessageElement.getAttribute("rollover-style"); var selectionStyle = initMessageElement.getAttribute("selection-style"); for (var item = initMessageElement.firstChild; item; item = item.nextSibling) { var tableElementId = item.getAttribute("eid"); var table = new EPTableEx(tableElementId); table.enabled = item.getAttribute("enabled") != "false"; table.rowCount = parseInt(item.getAttribute("rowCount"),10); table.headerVisible = item.getAttribute("header-visible") == "true"; table.footerVisible = item.getAttribute("footer-visible") == "true"; table.rolloverEnabled = item.getAttribute("rollover-enabled") == "true"; if (table.rolloverEnabled) { table.rolloverStyle = rolloverStyle; } table.selectionEnabled = item.getAttribute("selection-enabled") == "true"; if (table.selectionEnabled) { table.selectionStyle = selectionStyle; table.multipleSelect = item.getAttribute("selection-mode") == "multiple"; table.serverNotify = item.getAttribute("server-notify") == "true"; } table.ignoreMetaKeys = item.getAttribute("ignoreMetaKeys") == "true"; // perform xhtml fragment fixups var xhtmlfragments = initMessageElement.getElementsByTagName('xhtmlfragment'); for(var index=0; index< xhtmlfragments.length; index++) { var cellE = document.getElementById(xhtmlfragments[index].getAttribute('cellid')); if (cellE) { var cdata = xhtmlfragments[index].firstChild; if (cdata && cdata.data) { cellE.innerHTML = cdata.data; } } } table.scrollable = item.getAttribute("scrollable") == "true"; table.resizeable = item.getAttribute("resizeable") == "true"; table.resizeDragBarUsed = item.getAttribute("resizeDragBarUsed") == "true"; table.resizeGrowsTable = item.getAttribute("resizeGrowsTable") == "true"; table.heightStretched = EP.DOM.getBooleanAttr(item,"heightStretched",false); if (table.heightStretched) { table.minHeight = EP.DOM.getIntAttr(item,"minimumStretchedHeight",null); table.maxHeight = EP.DOM.getIntAttr(item,"maximumStretchedHeight",null); } table.actionCausingCells = {}; var elems = item.getElementsByTagName('actionCausingCell'); for(var i = 0; i < elems.length; i++) { var e = elems[i]; var row = e.getAttribute('row'); var cells = e.getAttribute('cells'); table.actionCausingCells[row] = cells; } table.selectionCausingCells = {}; var elems = item.getElementsByTagName('selectionCausingCell'); for(var i = 0; i < elems.length; i++) { var e = elems[i]; var row = e.getAttribute('row'); var cells = e.getAttribute('cells'); table.selectionCausingCells[row] = cells; } // init the table object table.init(); var rowElements = item.getElementsByTagName("row"); for (var rowIndex = 0; rowIndex < rowElements.length; ++rowIndex) { var tableRowIndex = parseInt(rowElements[rowIndex].getAttribute("index")); table.setSelected(tableRowIndex, true); // // should we record this into the last selection index if (table.multipleSelect && ! this.ignoreMetaKeys) { table.lastSelectedIndex = tableRowIndex; } } } }; /** * Deselects all selected rows in a Table. */ EPTableEx.prototype.clearSelected = function() { for (var i = 0; i < this.rowCount; ++i) { if (this.isSelected(i)) { this.setSelected(i, false); } } }; /** * Disposes of an EPTableEx instance, de-registering * listeners and cleaning up resources. */ EPTableEx.prototype.destroy = function() { var offset = 0; if (this.scrollable == false && this.headerVisible) { offset = 1; } if (this.rolloverEnabled || this.selectionEnabled) { for (var rowIndex = 0; rowIndex < this.rowCount; ++rowIndex) { var trElement = this.tableE.rows[rowIndex + offset]; if (this.rolloverEnabled) { EP.Event.removeHandler('mouseout',trElement); EP.Event.removeHandler('mouseover',trElement); } if (this.selectionEnabled) { EP.Event.removeHandler('click',trElement); } } } if (this.scrollable) { if (this.resizeable) { if (this.headerVisible) { EP.Event.removeHandler('mousedown',this.headerDivE); EP.Event.removeHandler('mousemove',this.headerDivE); } } EP.Event.removeHandler('scroll',this.contentDivE); EP.Event.removeHandler('resize',this.contentDivE); if (EP.isGecko) { EP.DocumentEvent.removeHandler('resize', 'EPTableEx'); } } this.tableE = null; }; /** * Redraws a row in the appropriate style (i.e., selected or deselected). * * @param rowIndex the index of the row to redraw */ EPTableEx.prototype.drawRowStyle = function(rowIndex) { var selected = this.isSelected(rowIndex); var trElement = this.getRowElement(rowIndex); for (var i = 0; i < trElement.cells.length; ++i) { if (selected) { EchoCssUtil.restoreOriginalStyle(trElement.cells[i]); EchoCssUtil.applyTemporaryStyle(trElement.cells[i], this.selectionStyle); } else { EchoCssUtil.restoreOriginalStyle(trElement.cells[i]); } } }; /** * Returns the TR element associated with a specific * row index. * * @param rowIndex the row index * @return the relevant TR element */ EPTableEx.prototype.getRowElement = function(rowIndex) { if (this.headerVisible && this.scrollable == false) { if (rowIndex == -1) { return this.tableE.rows[0]; } else if (rowIndex >= 0 && rowIndex < this.rowCount) { return this.tableE.rows[rowIndex + 1]; } } else { if (rowIndex >= 0 && rowIndex < this.rowCount) { return this.tableE.rows[rowIndex]; } } return null; }; /** * Determines the index of a table row based on a * TR element. This method is used for * processing events. * * @param trElement the TR element to evaluate * @return the row index */ EPTableEx.prototype.getRowIndex = function(trElement) { var stringIndex = trElement.id.lastIndexOf("_tr_") + 4; var rowIndex = trElement.id.substring(stringIndex); if (rowIndex == "header" || rowIndex == "emptyrow") { return -1; } else if (rowIndex == "footer") { return -2; } else { return parseInt(rowIndex); } }; /** * Initializes the state of an EPTableEx instance, * registering event handlers and binding it to it target * TABLE DOM element. */ EPTableEx.prototype.init = function() { // who are acting as - a scrollable or non scrollable table if (this.scrollable) { this.initScrollable(this.elementId); this.tableE = this.contentTableE; } else { this.tableE = document.getElementById(this.elementId); } var offset = 0; if (this.scrollable == false && this.headerVisible) { offset = 1; } this.selectionState = new Array(); if (this.rolloverEnabled || this.selectionEnabled) { var mouseEnterLeaveSupport = EchoClientProperties.get("proprietaryEventMouseEnterLeaveSupported"); for (var rowIndex = 0; rowIndex < this.rowCount; ++rowIndex) { var trElement = this.tableE.rows[rowIndex + offset]; if (this.rolloverEnabled) { EP.Event.addHandler('mouseout',trElement,this); EP.Event.addHandler('mouseover',trElement,this); } if (this.selectionEnabled) { EP.Event.addHandler('click',trElement,this); } } } }; /** * Determines if a row is selected. * * @param index the index of the row to evaluate * @return true if the row is selected */ EPTableEx.prototype.isSelected = function(index) { if (this.selectionState.length <= index) { return false; } else { return this.selectionState[index]; } }; /** * Returns the top level TR for that has been the ultimate cause of an Table event. * This will be the TR that is in a TBODY that is contained within this TABLE element. */ EPTableEx.prototype.findRowForEvent = function(echoEvent) { var trElement = EP.Event.findIdentifiedElement(echoEvent); while (trElement) { if (trElement.tagName.toLowerCase() == 'tr') { if (trElement.parentNode.parentNode == this.tableE) { return trElement; } } trElement = trElement.parentNode; } return null; } /** * Returns the top level cell TD for that has been the ultimate cause of an Table event. * This will be the TD that is in a TR/ TBODY that is contained within this TABLE element. */ EPTableEx.prototype.findCellForEvent = function(echoEvent) { var tdElement = EP.Event.findIdentifiedElement(echoEvent); while (tdElement) { if (tdElement.tagName.toLowerCase() == 'td') { if (tdElement.parentNode.parentNode.parentNode == this.tableE) { return tdElement; } } tdElement = tdElement.parentNode; } return null; } /** * Processes a row selection (click) event. * * @param echoEvent the event, preprocessed by the * EchoEventProcessor */ EPTableEx.prototype.processClick = function(echoEvent) { if (!this.enabled || !EchoClientEngine.verifyInput(this.tableE)) { return; } if (!this.selectionEnabled) { return; } var trElement = this.findRowForEvent(echoEvent); var rowIndex = this.getRowIndex(trElement); if (rowIndex == -1) { return; } var tdElement = this.findCellForEvent(echoEvent); var colIndex = parseInt(tdElement.getAttribute('c'),10); var colKey = 'c:' + colIndex + ';'; var actionCells = this.actionCausingCells[rowIndex]; var selectionCells = this.selectionCausingCells[rowIndex]; // // the list of cells per row is the list of cells that DONT cause an action/selection // so if we have none, the its OK and if we have some then we need to NOT find it // in the list. // var isActionCausing = (actionCells == null ? true : actionCells.indexOf(colKey) == -1); var isSelectionCausing = (selectionCells == null ? true : selectionCells.indexOf(colKey) == -1); if (isSelectionCausing) { // // Should we do this. What about a TreeTable that needs the click? EchoDomUtil.preventEventDefault(echoEvent); if (document.selection && document.selection.empty) { document.selection.empty(); } if (! this.multipleSelect) { this.clearSelected(); } else { // // IF we are using MetaKeys and they have not pressed a MetaKey then clear the selection // if (! this.ignoreMetaKeys) { if (! (echoEvent.shiftKey || echoEvent.ctrlKey || echoEvent.metaKey || echoEvent.altKey)) { this.clearSelected(); } } } // // IF we are multi mode and we are using the meta keys and they pressed SHIFT and there was a previous // last selection, THEN we can do a "shift" style selection, OTHERWISE is a simple // "single" row style selection. // if (this.multipleSelect && (! this.ignoreMetaKeys) && echoEvent.shiftKey && this.lastSelectedIndex != -1) { if (this.lastSelectedIndex < rowIndex) { startIndex = this.lastSelectedIndex; endIndex = rowIndex; } else { startIndex = rowIndex; endIndex = this.lastSelectedIndex; } for (var i = startIndex; i <= endIndex; ++i) { this.setSelected(i, true); } } else { var rowIsSelected = this.isSelected(rowIndex); this.setSelected(rowIndex, ! rowIsSelected); this.lastSelectedIndex = rowIndex; } // Update ClientMessage. this.updateSelectionState(); } // Notify server if required. if (this.serverNotify && isActionCausing) { var metaKeys = 0; if (echoEvent.altKey) { metaKeys |= 1; } if (echoEvent.shiftKey) { metaKeys |= 2; } if (echoEvent.ctrlKey) { metaKeys |= 4; } if (echoEvent.metaKey) { metaKeys |= 8; } var actionInfo = 'r:' + rowIndex + ';c:'+ colIndex +';' + 'mk:' + metaKeys + ';'; EchoClientMessage.setActionValue(this.elementId, "action", actionInfo); EchoServerTransaction.connect(); } }; /** * Sets the selection state of a table row. * * @param rowIndex the index of the row * @param newValue the new selection state (a boolean value) */ EPTableEx.prototype.setSelected = function(rowIndex, newValue) { this.selectionState[rowIndex] = newValue; // Redraw. this.drawRowStyle(rowIndex); }; /** * Updates the selection state in the outgoing ClientMessage. * If any server-side ActionListeners are registered, an action * will be set in the ClientMessage and a client-server connection initiated. */ EPTableEx.prototype.updateSelectionState = function() { if (this.selectionEnabled) { var propertyElement = EchoClientMessage.createPropertyElement(this.elementId, "selection"); // remove previous values while(propertyElement.hasChildNodes()){ propertyElement.removeChild(propertyElement.firstChild); } for (var i = 0; i < this.rowCount; ++i) { if (this.isSelected(i)) { var rowElement = EchoClientMessage.messageDocument.createElement("row"); rowElement.setAttribute("index", i); propertyElement.appendChild(rowElement); } } } EchoDebugManager.updateClientMessage(); }; /** * Processes a row mouse over event. * * @param echoEvent the event, preprocessed by the * EchoEventProcessor */ EPTableEx.prototype.processRolloverEnter = function(echoEvent) { if (!this.enabled || !EchoClientEngine.verifyInput(this.tableE)) { return; } var trElement = this.findRowForEvent(echoEvent); var rowIndex = this.getRowIndex(trElement); if (rowIndex == -1) { return; } if (this.rolloverStyle) { for (var i = 0; i < trElement.cells.length; ++i) { EchoCssUtil.applyTemporaryStyle(trElement.cells[i], this.rolloverStyle); } } }; /** * Processes a row mouse out event. * * @param echoEvent the event, preprocessed by the * EchoEventProcessor */ EPTableEx.prototype.processRolloverExit = function(echoEvent) { if (!this.enabled || !EchoClientEngine.verifyInput(this.tableE)) { return; } var trElement = this.findRowForEvent(echoEvent); var rowIndex = this.getRowIndex(trElement); if (rowIndex == -1) { return; } this.drawRowStyle(rowIndex); }; /** * The common EPNG eventHandler for TableEx. Relates an event back to its * JS object instance. * * @param echoEvent the event, preprocessed by the EP.js event handling */ EPTableEx.prototype.eventHandler = function(echoEvent) { if (echoEvent.type == "click") { this.processClick(echoEvent); } if (echoEvent.type == "mouseover") { this.processRolloverEnter(echoEvent); } if (echoEvent.type == "mouseout") { this.processRolloverExit(echoEvent); } if (echoEvent.type == 'mousedown') { this.onMouseDown(echoEvent); } if (echoEvent.type == 'mousemove') { this.onMouseMove(echoEvent); } if (echoEvent.type == 'scroll') { this.onScroll(echoEvent); } if (echoEvent.type == 'resize') { this.onResize(echoEvent); } } /*=====================================================================*/ /** * Global document event handler */ EPTableEx.documentEventHandler = function(echoEvent) { if (echoEvent.type == 'resize') { // tell every EpTableEx that the document has re-sized for (x in EP.ObjectMap.objectMap) { var obj = EP.ObjectMap.objectMap[x]; if (obj.componentType && obj.componentType == 'EPTableEx') { obj.onResize(echoEvent); } } } }; /** * Internal use only - used for internal debugging purposes */ EPTableEx.getReportE = function() { var reportE = EPTableEx.reportE; if (! reportE) { reportE = document.createElement('pre'); reportE.style.position = 'absolute'; reportE.style.left = '450px'; reportE.style.top = '70px'; reportE.style.backgroundColor = '#F0E68C'; reportE.style.zIndex = '1000'; reportE.style.fontFamily = 'monospace'; reportE.style.fontSize = '8pt'; EPTableEx.reportE = reportE; document.body.insertBefore(reportE,document.body.firstChild); } return reportE; }; /** * Internal use only - used for internal debugging purposes */ EPTableEx.report = function(str) { var reportE = EPTableEx.getReportE(); reportE.innerHTML = reportE.innerHTML + '
' + str; }; /** * Internal use only - used for internal debugging purposes */ EPTableEx.report_clear = function() { var reportE = EPTableEx.getReportE(); reportE.innerHTML = ''; }; /** * Internal use only - used for internal debugging purposes */ EPTableEx.prototype.reportOnTable = function() { return; EPTableEx.report_clear(); var report = EPTableEx.report; if (this.contentCells) { var totalcolgroupWidth = 0; var totalcontentWidth = 0; for (var i = 0; i < this.contentCells.length; i++) { var cell = this.contentCells[i]; EPTableEx.report('content cell['+ i + '] ow =' + cell.offsetWidth + ' sw=' + cell.style.width); totalcontentWidth += cell.offsetWidth; if (this.headerCells) { cell = this.headerCells[i]; report('header cell['+ i + '] ow =' + cell.offsetWidth + ' sw=' + cell.style.width); } cell = this.getColGroupCell(i); if (cell) { totalcolgroupWidth += this.getWidth(cell); report('colgroup cell['+ i + '] ow =' + cell.offsetWidth + ' sw=' + cell.style.width); } } report('totalcontentWidth = ' + totalcontentWidth); report('totalcolgroupWidth = ' + totalcolgroupWidth); report('---'); } if (this.headerVisible) { report('headerTableE'); report(' ow =' + this.headerTableE.offsetWidth + ' sw = ' + this.headerTableE.style.width); report(' sx =' + this.headerTableE.scrollLeft); report('headerDivE'); report(' ow =' + this.headerDivE.offsetWidth + ' oh=' + this.headerDivE.offsetHeight); report(' sw = ' + this.headerDivE.style.width + ' sh=' + this.headerDivE.style.height); report(' sx =' + this.headerDivE.scrollLeft); report('headerScrollerDivE'); report(' ow =' + this.headerScrollerDivE.offsetWidth + ' sw = ' + this.headerScrollerDivE.style.width); report(' sx =' + this.headerScrollerDivE.scrollLeft); report(' left=' + this.headerScrollerDivE.style.left); report('---'); } report('tableE'); report(' ow =' + this.contentTableE.offsetWidth + ' sw = ' + this.contentTableE.style.width); report(' sx =' + this.contentTableE.scrollLeft); report('contentDivE'); report(' ow =' + this.contentDivE.offsetWidth + ' oh=' + this.contentDivE.offsetHeight); report(' sw = ' + this.contentDivE.style.width + ' sh=' + this.contentDivE.style.height); report(' sx =' + this.contentDivE.scrollLeft); report('outerDivE'); report(' ow =' + this.outerDivE.offsetWidth + ' oh=' + this.outerDivE.offsetHeight); report(' sw = ' + this.outerDivE.style.width + ' sh=' + this.outerDivE.style.height); report('elementId = ' + this.elementId); }; /** * Gets the COLGROUP cell for the given cellIndex. */ EPTableEx.prototype.getColGroupCell = function(cellIndex) { return this._getCellImpl(this.colgroupCells,cellIndex); }; /** * Returns the content cell for a given cell index */ EPTableEx.prototype.getContentCell = function(cellIndex) { return this._getCellImpl(this.contentCells,cellIndex); }; /** * Returns the header cell for a given cell index */ EPTableEx.prototype.getHeaderCell = function(cellIndex) { return this._getCellImpl(this.headerCells,cellIndex); }; /** * Returns the footer cell for a given cell index */ EPTableEx.prototype.getFooterCell = function(cellIndex) { return this._getCellImpl(this.footerCells,cellIndex); }; /** * Private implementation function */ EPTableEx.prototype._getCellImpl = function(cellArray, cellIndex) { if (cellArray == null || cellIndex > cellArray.length-1) return null; return cellArray[cellIndex]; }; /** * Returns the Nth child of htmlE that has the specified tag name * such as 'DIV'. */ EPTableEx.prototype.getNthChild = function(htmlE, index, tagName) { var foundCount = 0; for(var index=0; index leftEdgeX && x < (rightEdgeX+5)) { // its inside this cell. But it must be in the // the last 5 pixels of the cell to be resizing if (x >= (rightEdgeX-5)) { cellIndex = i; break; } // ok it might be in the first 5 pixels of the cell in which // case the "resize operation" is really for the previous cell. One // exception is the first cell which has no previous cell. if (x <= leftEdgeX+5 && i > 0) { cellIndex = i-1; break; } break; } } // we dont allow a hit on the last cell index. At present we dont allow them to // resize the end of the last cell. Maybe one day we will if (cellIndex == this.headerCells.length) { cellIndex = -1; } } return cellIndex; }; /** * Updates the column width state in the outgoing ClientMessage. */ EPTableEx.prototype.updateColumnWidthState = function() { if (this.resizeable) { var propertyElement = EchoClientMessage.createPropertyElement(this.elementId, "columnWidths"); // remove previous values while(propertyElement.hasChildNodes()){ propertyElement.removeChild(propertyElement.firstChild); } var cellArray = this.contentCells; if (cellArray == null) { cellArray = this.headerCells; } if (cellArray) { for (var i = 0; i < cellArray.length; ++i) { var width = this.getWidth(cellArray[i]); var widthElement = EchoClientMessage.messageDocument.createElement("columnWidth"); widthElement.setAttribute("width", width); propertyElement.appendChild(widthElement); } } } EchoDebugManager.updateClientMessage(); }; EPTableEx.prototype.synchronizeTableColumnsAsych = function() { var that = this; window.setTimeout(function() { that.synchronizeTableColumns(); }, 100); }; /** * Called to synchronise the width of the header cells * to the width of the content cells. */ EPTableEx.prototype.synchronizeTableColumns = function() { if (this.rowCount > 0) { if (this.headerVisible) { this.headerDivE.style.width = this.contentDivE.style.width; } if (this.footerVisible) { this.footerDivE.style.width = this.contentDivE.style.width; } // // a little border appearance issue. We get double right borders if the table is 1 border width // less than the content area so we turn them off when its butted up agianst the content area // and we turned it back on when its not against it var contentBorderWidth = this.getBorderWidths(this.contentDivE); var rightBorderWidth = this.getBorderRightWidth(this.contentDivE); var doBorderAdjusting = true; if (this.headerVisible && EP.isGecko && this.contentOriginalWidth.indexOf('%') > -1) { this.headerDivE.style.width = (this.contentDivE.offsetWidth-contentBorderWidth) + 'px'; } if (this.footerVisible && EP.isGecko && this.contentOriginalWidth.indexOf('%') > -1) { this.footerDivE.style.width = (this.contentDivE.offsetWidth-contentBorderWidth) + 'px'; } if (doBorderAdjusting) { if (this.contentTableE.offsetWidth+contentBorderWidth == this.contentDivE.offsetWidth) { this.contentTableE.style.borderRightWidth = '0px'; if (this.headerVisible) { this.headerTableE.style.borderRightWidth = '0px'; } if (this.footerVisible) { this.footerTableE.style.borderRightWidth = '0px'; } } else { this.contentTableE.style.borderRightWidth = this.contentDivE.style.borderRightWidth; if (this.headerVisible) { this.headerTableE.style.borderRightWidth = this.headerDivE.style.borderRightWidth; } if (this.footerVisible) { this.footerTableE.style.borderRightWidth = this.footerDivE.style.borderRightWidth; } } } // // make the header table the same size var width = this.contentTableE.offsetWidth; if (this.headerVisible) { this.setWidth(this.headerTableE,width); } if (this.footerVisible) { this.setWidth(this.footerTableE,width); } if (this.contentCells) { for (var i = 0; i < this.contentCells.length; ++i) { if (this.headerVisible) { this.synchroniseHeaderCell(i); } if (this.footerVisible) { this.synchroniseFooterCell(i); } } } } // update our client message this.updateColumnWidthState(); }; /** * This is called to set the header cell to to be the same width * as the content cell at the specified cellIndex. This can take into * account browsers differences in borders and the like around cells. */ EPTableEx.prototype.synchroniseHeaderCell = function(cellIndex) { var contentCell = this.getContentCell(cellIndex); var headerCell = this.getHeaderCell(cellIndex); var width = parseInt(this.getWidth(contentCell),10); // // if some browsers differ in cell border's setting etc.. so take this into account here this.setWidth(headerCell,width); }; EPTableEx.prototype.synchroniseFooterCell = function(cellIndex) { var contentCell = this.getContentCell(cellIndex); var footerCell = this.getFooterCell(cellIndex); var width = parseInt(this.getWidth(contentCell),10); // // if some browsers differ in cell border's setting etc.. so take this into account here this.setWidth(footerCell,width); }; /** * Sets both the cell and colgroup cell to a new width and returns * the width of the new cell. */ EPTableEx.prototype.adjustCellWidths = function(cellIndex, newWidth) { var colgroupCell = this.getColGroupCell(cellIndex); if (colgroupCell) { this.setWidth(colgroupCell,newWidth); } var cellE; if (this.rowCount > 0) { cellE = this.getContentCell(cellIndex); } else { cellE = this.getHeaderCell(cellIndex); } var actualWidth = this.setWidth(cellE,newWidth); return actualWidth; }; EPTableEx.MIN_CELL_WIDTH = 5; /** * Resizes the column cells for the current drag operation. */ EPTableEx.prototype.resizeDraggedColumn = function(echoEvent,dragContext, deltaX, forceMinWidth) { this.hasBeenResized = true; var startWidth = dragContext.startWidth; var startTableWidth = dragContext.startTableWidth; var cellIndex = dragContext.cellIndex; var newWidth = 0; var newTableWidth = 0; if (deltaX >= 0) { // dragging right newWidth = startWidth + deltaX; newTableWidth = startTableWidth + deltaX; } else { // dragging left deltaX = Math.abs(deltaX); newWidth = startWidth - deltaX; newTableWidth = startTableWidth - deltaX; if (newWidth <= EPTableEx.MIN_CELL_WIDTH) { newWidth = -1; if (forceMinWidth) { newWidth = EPTableEx.MIN_CELL_WIDTH; } } } var widthDifference = newWidth - startWidth; // set the new widths if (newWidth != -1) { var actualWidth = newWidth; try { actualWidth = this.adjustCellWidths(cellIndex,newWidth); // in the fixed column width scenario, we make one smaller/bigger // and make its right hand side neighbour bigger/smaller if (this.resizeGrowsTable == false) { var neighbourCellE = dragContext.dragNeighbor; var neighbourWidth = dragContext.startNeighborWidth; if (widthDifference < 0) { neighbourWidth += Math.abs(widthDifference); } else { neighbourWidth -= widthDifference; } this.adjustCellWidths(cellIndex+1,neighbourWidth); } } catch(ex) { EP.debug('EPTableEx - exception thrown during cell width set' + newWidth); } if (this.resizeGrowsTable) { // adjust the table with by the new amount if the cell width actually stuck! if (actualWidth == newWidth) { try { // set the table Width var targetTableE = this.contentTableE; if (this.rowCount <= 0) { targetTableE = this.headerTableE; } this.setWidth(targetTableE,newTableWidth); } catch(ex) { EP.debug('EPTableEx - exception thrown during table width set' + newWidth); } } } } // adjust the header cells to match //this.synchroniseHeaderCell(cellIndex); this.synchronizeTableColumns(); this.synchroniseScrollPositions(); this.reportOnTable(); } /** * Returns the x co--ord of the mouse drag operation within allowable bounds * for the current mode of operation. */ EPTableEx.prototype.getValidDragMovement = function(echoEvent,dragContext) { var targetTableE = this.contentTableE; var cellE = this.getContentCell(dragContext.cellIndex); if (this.rowCount <= 0) { cellE = this.getHeaderCell(dragContext.cellIndex); targetTableE = this.headerTableE; } var fudge = 0; var leftX = this.getClientX(cellE); if (echoEvent.clientX < (leftX+fudge+EPTableEx.MIN_CELL_WIDTH)) { return leftX+fudge+EPTableEx.MIN_CELL_WIDTH; } if (this.resizeGrowsTable == false) { var maxRightX = 0; var neighbourCell = null; if (this.rowCount > 0) { neighbourCell = this.getContentCell(dragContext.cellIndex+1); } else { neighbourCell = this.getHeaderCell(dragContext.cellIndex+1); } if (neighbourCell == null) { // must be the last cell and hence it doesnt have a right hand side neighbour cell // but the boundary now is the table edge maxRightX = this.getClientX(targetTableE) + this.getWidth(targetTableE); } else { maxRightX = this.getClientX(neighbourCell) + this.getWidth(neighbourCell); } if (echoEvent.clientX > (maxRightX-fudge-EPTableEx.MIN_CELL_WIDTH)) { return maxRightX-fudge-EPTableEx.MIN_CELL_WIDTH; } } return echoEvent.clientX; }; /** * Called to move the drag bar to the specified position */ EPTableEx.prototype.moveDragBar = function(echoEvent,dragContext, clientX) { dragContext.dragBarE.style.left = (clientX-0)+'px'; dragContext.dragBarE.style.display = ''; }; /** * Creates a drag bar to visually indicate where a drag is occurring. */ EPTableEx.prototype.createDragBar = function(echoEvent, cellIndex) { var targetTableE = this.contentTableE; var cellE = this.getContentCell(cellIndex); if (this.rowCount <= 0) { cellE = this.getHeaderCell(cellIndex); targetTableE = this.headerTableE; } var cellX = this.getClientX(cellE); var leftX = this.getClientX(this.headerDivE); var topY = this.getClientY(this.headerDivE); var borderTopWidth = parseInt(this.headerDivE.style.borderTopWidth,10); var borderBottomWidth = parseInt(this.contentDivE.style.borderBottomWidth,10); borderTopWidth = (isNaN(borderTopWidth) ? 1 : borderTopWidth + 1); borderBottomWidth = (isNaN(borderBottomWidth) ? 0 : borderBottomWidth); var dragBarE = document.createElement('div'); dragBarE.style.position = 'absolute'; // snap to start of column not the mouse position dragBarE.style.left = (cellX-0)+'px'; //dragBarE.style.top = (topY + this.headerDivE.offsetHeight) + 'px'; dragBarE.style.top = (topY + borderTopWidth) + 'px'; dragBarE.style.width = '2px'; dragBarE.style.height = (this.outerDivE.offsetHeight-borderBottomWidth)+'px'; dragBarE.style.backgroundColor = '#A19669'; dragBarE.style.display = 'none'; var bodyE = document.getElementsByTagName('body')[0]; bodyE.appendChild(dragBarE); return dragBarE; }; /** * This is a call back from the EPDRAG functionality and allows the * cell widths to be changed as the user drags the captured mouse. */ EPTableEx.prototype.onDragMouseMove = function(echoEvent,dragContext) { var clientX = this.getValidDragMovement(echoEvent,dragContext); var deltaX = clientX - dragContext.eventStartX; if (this.resizeDragBarUsed) { // move the drag bar only this.moveDragBar(echoEvent,dragContext,clientX); } else { this.resizeDraggedColumn(echoEvent,dragContext, deltaX, false); } }; /** * This is called when the the drag operation is ended (mouse up) */ EPTableEx.prototype.onDragEnd = function(echoEvent,dragContext) { this.dragInOperation = false; if (this.resizeDragBarUsed) { // use the left position of the drag bar not the mouse proper var leftX = parseInt(dragContext.dragBarE.style.left,10); // we then need to fudge the deltaX value so that it only goes as far as the // the drag bar not the mouse var deltaX = leftX - dragContext.eventStartX; // now move the columns this.resizeDraggedColumn(echoEvent,dragContext,deltaX,true); // get rid of the drag bar dragContext.dragBarE.parentNode.removeChild(dragContext.dragBarE); } this.synchronizeTableColumnsAsych(); }; /** * Called when the mouse is pressed down in the header cell. If its in a resize * area, it will allow a drag operation to start. */ EPTableEx.prototype.onMouseDown = function(echoEvent) { var cellIndex = this.hitTestHeaderCell(echoEvent); if (cellIndex == -1) { // dont allow mouse down if its not in a resize area or its // not a valid cell for this mode this.dragInOperation = false; return; } var targetTableE; this.dragInOperation = true; var dragContext = {}; dragContext.cellIndex = cellIndex; // // do we have any content, if not then our target is the header cells if (this.rowCount > 0) { dragContext.dragCellE = this.getContentCell(cellIndex); dragContext.dragNeighbor = this.getContentCell(cellIndex+1); targetTableE = this.contentTableE; } else { dragContext.dragCellE = this.getHeaderCell(cellIndex); dragContext.dragNeighbor = this.getHeaderCell(cellIndex+1); targetTableE = this.headerTableE; } dragContext.startWidth = this.getWidth(dragContext.dragCellE); dragContext.startTableWidth = this.getWidth(targetTableE); if (dragContext.dragNeighbor) { dragContext.startNeighborWidth = this.getWidth(dragContext.dragNeighbor); } if (this.resizeDragBarUsed) { // create a drag bar DIV dragContext.dragBarE = this.createDragBar(echoEvent); } EP.Drag.startDragOperation(echoEvent,null,this,dragContext); }; /** * Called when the mouse is moved in the header div area. It decides whether to * change the mouse cursor or not */ EPTableEx.prototype.onMouseMove = function(echoEvent) { if (this.dragInOperation) { return; } var cellIndex = this.hitTestHeaderCell(echoEvent); if (cellIndex == -1) { this.headerDivE.style.cursor = 'auto'; return; } // we dont allow the last cell to be resized if its not allowed to grow the // table while dragging. if (this.headerCells) { if (this.resizeGrowsTable == false && cellIndex == this.headerCells.length-1) { this.headerDivE.style.cursor = 'auto'; return; } } this.headerDivE.style.cursor = 'e-resize'; }; /** * Called to make the header scroll X the same as the content area */ EPTableEx.prototype.synchroniseScrollPositions = function() { var scrollX = this.contentDivE.scrollLeft; if (this.headerVisible) { this.synchroniseScrollPositionsTo(scrollX,this.headerDivE,this.headerScrollerDivE); } if (this.footerVisible) { this.synchroniseScrollPositionsTo(scrollX,this.footerDivE,this.footerScrollerDivE); } }; EPTableEx.prototype.synchroniseScrollPositionsTo = function(scrollX, divE, scrollerDivE) { divE.scrollLeft = scrollX; var overScrollX = scrollX - divE.scrollLeft; if (overScrollX > 0) { scrollerDivE.style.left = -(overScrollX) + 'px'; } else { scrollerDivE.style.left = 0 + 'px'; } } /** * Called when the content DIV is scrolled. */ EPTableEx.prototype.onScroll = function(echoEvent) { this.synchroniseScrollPositions(); //this.reportOnTable(); }; /** * Called when the content DIV is resized. */ EPTableEx.prototype.onResize = function(echoEvent) { // we only reset if its not been adjusted by the user. if (! this.hasBeenResized) { this.initContainers(); } this.synchroniseScrollPositions(); this.synchronizeTableColumns(); }; /** * Gecko has some issues when the logical size of the table is > than the content div size. It * re-adjusts the table so that it fits into the content div instead of letting it be a "natural size" * and have it scroll as appropriate. So we clone it and gets its "natural width" as it would layout if * it has not width specified. * */ EPTableEx.prototype.getNaturalTableWidth = function(tableE) { var startWidth = tableE.offsetWidth; var naturalWidth = startWidth; if (true) { var cloneTableE = tableE.cloneNode(true); var bodyE = document.getElementsByTagName('body')[0]; cloneTableE.style.width = ''; cloneTableE.className = ''; var cloneHolderE = document.createElement('div'); cloneHolderE.style.position = 'absolute'; cloneHolderE.style.visibility = 'hidden'; cloneHolderE.appendChild(cloneTableE); bodyE.insertBefore(cloneHolderE,bodyE.firstChild); naturalWidth = cloneTableE.offsetWidth; bodyE.removeChild(cloneHolderE); } return naturalWidth; }; /** * Called to build our internal the cells of the table */ EPTableEx.prototype.initCells = function() { this.headerCells = null; if (this.headerVisible && this.headerTableE.tBodies[0].rows.length > 0) { this.headerCells = this.headerTableE.tBodies[0].rows[0].cells; } this.footerCells = null; if (this.footerVisible && this.footerTableE.tBodies[0].rows.length > 0) { this.footerCells = this.footerTableE.tBodies[0].rows[0].cells; } this.contentCells = null; if (this.contentTableE.tBodies[0].rows.length > 0) { this.contentCells = this.contentTableE.tBodies[0].rows[0].cells; } this.colgroupCells = []; var colgroup = this.contentTableE.getElementsByTagName('colgroup')[0]; if (colgroup && colgroup.parentNode == this.contentTableE) { this.colgroupCells = []; for (var i = 0; i < colgroup.childNodes.length; i++) { var col = colgroup.childNodes.item(i); if (col.tagName && col.tagName.toUpperCase() == 'COL') { this.colgroupCells[this.colgroupCells.length] = col; } } } }; /** * Allows partial styling of som rows within the content area */ EPTableEx.prototype.initContentStyles = function(startRow, endRow) { if (this.contentCells) { var maxCol = this.contentCells.length-1; var style = EP.StyleSheet.getComputedStyle(this.contentDivE); var styleHeader = EP.StyleSheet.getComputedStyle(this.headerDivE); if (style.borderLeftWidth != '') { // if we have a border then we need to apply that to the contents cells // in a special way to make sure we have cell border width var rows = this.contentTableE.tBodies[0].rows for(var row=startRow; row < rows.length && row <= endRow; row++) { var cells = this.contentTableE.tBodies[0].rows[row].cells; for(var col=0; col < cells.length; col++) { var cellE = this.getNthChild(cells[col],1,'div'); // right hand border of content cells if (col > 0) { cellE.style.borderLeftWidth = style.borderLeftWidth; cellE.style.borderLeftColor = style.borderLeftColor; cellE.style.borderLeftStyle = style.borderLeftStyle; if (col == maxCol) { // dragging over this edge wont make it disappear this way this.contentTableE.style.borderRightWidth = style.borderLeftWidth; this.contentTableE.style.borderRightColor = style.borderLeftColor; this.contentTableE.style.borderRightStyle = style.borderLeftStyle; } } if (row < rows.length-1) { cellE.style.borderBottomWidth = style.borderBottomWidth; cellE.style.borderBottomColor = style.borderBottomColor; cellE.style.borderBottomStyle = style.borderBottomStyle; } else { // normally we dont put a bottom border on the last row because // it will "clash" with the outer div border. But if the // this doesnt fill the div, then we need to put one on there var tHeight = this.contentTableE.offsetHeight; var dHeight = this.contentDivE.offsetHeight; if (dHeight-tHeight > 2) { cellE.style.borderBottomWidth = style.borderBottomWidth; cellE.style.borderBottomColor = style.borderBottomColor; cellE.style.borderBottomStyle = style.borderBottomStyle; } } // IE6 has an issue where is collapse the border together even // if we havent asked it to. So we inject them back again by hand. if (row == 0 && document.all) { cellE.style.borderTopWidth = styleHeader.borderTopWidth; cellE.style.borderTopColor = styleHeader.borderTopColor; cellE.style.borderTopStyle = styleHeader.borderTopStyle; } } } } } } /** * Called to tweak some of the style settings to make it look nicer. * * When the row count gets great then some value, then this becomes an time consuming operation * so we do it via a "delayed" strategy when applicable */ EPTableEx.MAGIG_STYLE_ROW_COUNT = 50; EPTableEx.prototype.initStyles = function() { var table = this; // take this out of the main Echo2 processing loop because it can take so long if the // rowCount is large. Just how large is debatable. if (table.rowCount > EPTableEx.MAGIG_STYLE_ROW_COUNT) { // // do the first n now so it looks nice and then do the rest later table.initContentStyles(0,EPTableEx.MAGIG_STYLE_ROW_COUNT); var initStylesClosure = function() { table.initContentStyles(EPTableEx.MAGIG_STYLE_ROW_COUNT,table.rowCount); table.synchronizeTableColumns(); }; window.setTimeout(initStylesClosure,10); } else { table.initContentStyles(0,table.rowCount); } if (table.headerCells) { var maxCol = table.headerCells.length-1; var style = EP.StyleSheet.getComputedStyle(table.headerDivE); for(var col=0; col 0) { cellE.style.borderLeftWidth = style.borderLeftWidth; cellE.style.borderLeftColor = style.borderLeftColor; cellE.style.borderLeftStyle = style.borderLeftStyle; if (col == maxCol) { table.headerTableE.style.borderRightWidth = style.borderLeftWidth; table.headerTableE.style.borderRightColor = style.borderLeftColor; table.headerTableE.style.borderRightStyle = style.borderLeftStyle; } } } } if (table.footerCells) { var maxCol = table.footerCells.length-1; var style = EP.StyleSheet.getComputedStyle(table.footerDivE); for(var col=0; col 0) { cellE.style.borderLeftWidth = style.borderLeftWidth; cellE.style.borderLeftColor = style.borderLeftColor; cellE.style.borderLeftStyle = style.borderLeftStyle; if (col == maxCol) { table.footerTableE.style.borderRightWidth = style.borderLeftWidth; table.footerTableE.style.borderRightColor = style.borderLeftColor; table.footerTableE.style.borderRightStyle = style.borderLeftStyle; } } } } }; /** * Called to initialise the container elements of the TableEx */ EPTableEx.prototype.initContainers = function() { var targetDivE = this.contentDivE; var targetTableE = this.contentTableE; // // and exception is where we have a header visible but now rows of table data // in this case we only want to resize the header not the content area if (this.rowCount == 0 && this.headerVisible) { targetDivE = this.headerDivE; targetTableE = this.headerTableE; } // For some reason Gecko INSISTS the content table must be 100% in order for the // cells to overlap when dragging and from the testing I sense that this MUST be the FIRST // ever setting of the style value. If its doesnt start with 100% as its value it refuses // to allow the cells to overlap later. So make sure its set in the style="" in the rendering peer if (EP.isGecko) { targetTableE.style.width = '100%'; } // Initial layout. We have 2 elements that control the width of the logical TableEx // the content Div (which is scrollable) and the content table itself but we only have // one "width" specified on the component. Therefore we make the 1 value apply magically // to 2 elements in a special way. // // Also in order to be "scrollable" the contentDiv must end up a fixed pixel size to ensure // that it acts as a viewport for content table part. // //debugger; var naturalWidth = this.getNaturalTableWidth(targetTableE); var contentWidth = targetDivE.offsetWidth; var tableWidth = targetTableE.offsetWidth; var contentBorderWidth = this.getBorderWidths(targetDivE); if (this.contentOriginalWidth == '' || this.contentOriginalWidth == 'auto') { // No width has been specified. We let the table lay itself out // as it sees fit and if the resulting table is smaller then the // content div area, we shrink the content div to match the table. This is table // like behaviour in that it makes itself as wide as it wants to an no more. targetTableE.style.width = naturalWidth + 'px'; if (targetDivE.offsetWidth > naturalWidth) { targetDivE.style.width = naturalWidth+'px'; // we dont want a horizontal scroll bar if we dont need it if (this.hasHorzScrollBar(targetDivE)) { targetDivE.style.width = naturalWidth+(this.contentDivE.offsetWidth-contentBorderWidth-this.contentDivE.clientWidth)+'px'; } } // size the header div to match var newWidth = (targetDivE.offsetWidth-contentBorderWidth) + 'px'; if (this.headerVisible) { this.headerDivE.style.width = newWidth; } if (this.footerVisible) { this.footerDivE.style.width = newWidth; } } else if (this.contentOriginalWidth.indexOf('%') != -1) { // // the table must be made to fit its content div as the content div is trying to fit // all or part of its of its container. It may need to be stretched sideways to // fit the container. if (EP.isIE && this.contentOriginalWidth == '100%') { targetDivE.style.width = '99%'; } targetTableE.style.width = '100%'; tableWidth = targetTableE.offsetWidth; if (EP.isGecko) { // Gecko will cause the table to shrink to fit the container // which is not what we want regardless once it 100%. // We want at least its natural width if (naturalWidth > targetDivE.offsetWidth) { // force it to go to its natural width targetTableE.style.width = naturalWidth+'px'; } else { if (this.hasHorzScrollBar(targetDivE)) { targetTableE.style.width = tableWidth+'px'; } else { targetTableE.style.width = tableWidth+'px'; } } } else if (EP.isIE) { // HACK - IE ends up showing a unwanted horizontal scroll bar here if // a height is specified if (this.hasHorzScrollBar(targetDivE)) { if (this.contentOriginalHeight.indexOf('px') != -1) { var scrollBarWidth = targetDivE.offsetWidth - targetDivE.clientWidth; targetTableE.style.width = (targetDivE.offsetWidth-scrollBarWidth)+'px'; } else { targetTableE.style.width = tableWidth+'px'; } } } } else { // it must be in pixels or em or some such fixed value. The table should layout as it wants // to and the possibly "scroll" within the fixed size content div. targetTableE.style.width = naturalWidth + 'px'; // size the header div to match var newWidth = (targetDivE.offsetWidth-contentBorderWidth) + 'px'; if (this.headerVisible) { this.headerDivE.style.width = newWidth; } if (this.footerVisible) { this.footerDivE.style.width = newWidth; } } // // now the final step is to "constrict the table down so fits sinde its parent container". We only // do this if the user has not asked for an explictly sized table if (this.contentOriginalHeight.indexOf('px') == -1) { // we want to tap into the EP stretch function so that we knwo when containers are resized // but we dont want the default implementation. So we replace it and call our own if (this.heightStretched && ! this.verticalStretcher) { // save away our natural height so we can grow and shrink as need be later var that = this; this.verticalStretcher = EP.Stretch.verticalStretcher.getInstance(this.contentDivE.id, this.minHeight, this.maxHeight); this.verticalStretcher.stretch = function() { that.constrictHeightToContainer(); that.synchroniseScrollPositions(); that.synchronizeTableColumns(); } } } }; /** * Called to allow the content to shrink and expand into the container */ EPTableEx.prototype.constrictHeightToContainer = function() { // find the first positioned parent with height var elementE = this.contentDivE; for (var parentE = elementE.parentNode; parentE;) { parentStyle = parentE.style; if (parentStyle) { if (parentStyle.height || parentStyle.position == "absolute" || parentStyle.position == "fixed") { break; } } parentE = parentE.parentNode; } if (parentE == null) { return; } var allContentHeight = 0; for (var i = 0; i < parentE.childNodes.length; i++) { var node = parentE.childNodes[i]; // Ignore text, floated, and absolutely positioned nodes if (node.nodeType === 1) { s = node.style; if (!s.styleFloat && !s.cssFloat && s.position != "absolute" && s.position != "fixed") { allContentHeight += node.offsetHeight; } } } var availableHeight = parentE.clientHeight; var contentDivHeight = this.contentDivE.clientHeight; var contentTableHeight = this.contentTableE.offsetHeight; // // ok we are naturally bigger then the containers available space, so we should srinky by enough to just fit // the container //var newHeight = Math.max(0, this.contentNaturalHeight + availableHeight - allContentHeight); var newHeight = Math.max(0, contentDivHeight + availableHeight - allContentHeight); // // if the natural height of the content table is less then the container // height then we dont need to stretch it forward if (contentTableHeight < newHeight) { return; } var respectedLimits = false; if (this.minHeight && ! isNaN(this.minHeight) && newHeight < this.minHeight) { newHeight = this.minHeight; respectedLimits = true; } if (this.maxHeight && ! isNaN(this.maxHeight) && newHeight > this.maxHeight) { newHeight = this.maxHeight; respectedLimits = true; } if (newHeight > 0) { // // prevent "flash" of scroll bars var saveOverflow = this.contentDivE.style.overflow; var saveParentOverflow = parentE.style.overflow; this.contentDivE.style.overflow = parentE.style.overflow = "hidden"; this.contentDivE.style.height = newHeight + 'px'; if (! respectedLimits) { // Firefox sometimes does not include margins in offsetHeight so adjust again if we created some overflow this.contentDivE.style.height = Math.max(0, newHeight + parentE.offsetHeight - parentE.scrollHeight) + "px"; } // restore overflow settings this.contentDivE.style.overflow = saveOverflow; parentE.style.overflow = saveParentOverflow; } }; /** * Initialises the TableEx for scrollable support. Some basic design assumptions have been * made here. First off if the header/content div strategy is being used then the * TableEx is being made scrollable, otherwise we would simple have 1 table without all * the containing DIV malarchy. */ EPTableEx.prototype.initScrollable = function(elementId) { //debugger; this.outerDivE = document.getElementById(elementId); this.headerDivE = document.getElementById(elementId+'_headerDiv'); this.headerScrollerDivE = document.getElementById(elementId+'_headerScrollerDiv'); this.headerTableE = document.getElementById(elementId+'_headerTable'); this.footerDivE = document.getElementById(elementId+'_footerDiv'); this.footerScrollerDivE = document.getElementById(elementId+'_footerScrollerDiv'); this.footerTableE = document.getElementById(elementId+'_footerTable'); this.contentDivE = document.getElementById(elementId+'_contentDiv'); this.contentTableE = document.getElementById(elementId+'_contentTable'); // save our originally specified widths and heights this.contentOriginalWidth = this.contentDivE.style.width; this.contentOriginalHeight = this.contentDivE.style.height; this.initCells(); this.initStyles(); this.initContainers(); if (this.resizeable) { if (this.headerVisible) { EP.Event.addHandler("mousedown", this.headerDivE, this); EP.Event.addHandler("mousemove", this.headerDivE, this); } // // If the table columns are resizeable then they must be // expressed in pixels. So we run through their offsetWidths // and set the cell widths to those pixels sizes. They will not // resize consistently if we dont. // if (this.contentCells) { for(var index=0; index 0 && this.headerVisible && this.contentOriginalHeight.indexOf('px') != -1) { var headerHeight = this.headerDivE.offsetHeight; if (this.footerVisible) { headerHeight += this.footerDivE.offsetHeight; } var currentHeight = this.contentDivE.offsetHeight; this.contentDivE.style.height = (currentHeight - headerHeight) + 'px'; } // save away our natural height for later use this.contentNaturalHeight = this.contentDivE.clientHeight; // synch the header cells with the table cells this.synchronizeTableColumns(); this.synchroniseScrollPositions(); if (this.rowCount > 0) { // it must have a scroll handler if its scrollable EP.Event.addHandler("scroll", this.contentDivE, this); // if should respond to changes in the browser size EP.Event.addHandler("resize", this.contentDivE, this); if (EP.isGecko) { // resize doesnt seem to work on Gecko at the element level! EP.DocumentEvent.addHandler('resize', 'EPTableEx', EPTableEx.documentEventHandler); } } this.reportOnTable(); };





© 2015 - 2025 Weber Informatics LLC | Privacy Policy