// Begin jquery.alignPseudoTables.js // jQuery Plug-in "alignPseudoTables" // Version: 1.0 (2010-02-23) // Examples and documentation at: http://www.webmanwalking.org/projects/jquery/jquery.alignPseudoTables/ // Copyright (c) 2011 by WebManWalking // Dually licensed under the MIT and GPL licenses: // http://www.opensource.org/licenses/mit-license.php // http://www.gnu.org/licenses/gpl.html // Internal variables use "scope notation", to more clearly show the scope of their definition: // "f" prefix means "field" property of an object // "p" prefix means "parameter" defined among the arguments to a function // "s" prefix means "stack" defined at the level of a function // Braces are part of the block and are therefore indented the same as the block. // A single line isn't a block unless it has to be. (function($) { // *************************************************************************************************************** // *************************************************************************************************************** // Note: The following name is singular (= just one table) and not chainable. It's used by the chainable routine, below. $.alignPseudoTable = function (pSelectorTable, pSelectorRow, pSelectorCell, pEltTable) { // Predeclare all stack variables in alphabetical order, even those that are only used in nested anonymous // functions. (Nested anonymous functions can still reference them as closures.) Allows easier debugging. var sBoxModel = $.support.boxModel; // Cache property lookups in the stack scope. var sCol = null; var sColHeight = 0; var sColWidth = 0; var sCols = null; var sCSS2Number = function(pCSS) // Gets rid of "px", "auto", "none", etc. { var sInt = 0; // For use with assignment in isNan(). return pCSS ? (pCSS.toString().length ? (isNaN(sInt = parseInt(pCSS,10)) ? 0 : sInt) : 0) : 0; } var sHeight = 0; var sInnerTrimHorzontal = function(pElt) // Inside-the-box border, margin and padding. { if (sBoxModel) // In the W3C box model, all trim properties reside outside of the box. return 0; // In the W3C box model, all trim properties reside outside of the box. return sCSS2Number(pElt.css("border-right-width")) + sCSS2Number(pElt.css("border-left-width")) + sCSS2Number(pElt.css("margin-right")) + sCSS2Number(pElt.css("margin-left")) + sCSS2Number(pElt.css("padding-right")) + sCSS2Number(pElt.css("padding-left")); } var sInnerTrimVertical = function(pElt) // Inside-the-box border, margin and padding. { if (sBoxModel) // In the W3C box model, all trim properties reside outside of the box. return 0; // In the W3C box model, all trim properties reside outside of the box. return sCSS2Number(pElt.css("border-top-width")) + sCSS2Number(pElt.css("border-bottom-width")) + sCSS2Number(pElt.css("margin-top")) + sCSS2Number(pElt.css("margin-bottom")) + sCSS2Number(pElt.css("padding-top")) + sCSS2Number(pElt.css("padding-bottom")); } var i = -1; // Traditionally the nominal index of rows. var j = -1; // Traditionally the nominal index of cols. var sLeft = 0; // Accumulator for "left:" in the second sCols loop, below. var sMaxHeights = []; var sMaxWidths = []; var sNamespace = "alignPseudoTables"; // Avoids conflicts with other users of .data(). var sOrigPosition = ""; var sRow = null; var sRowHeight = 0; var sRowWidth = 0; var sRows = null; var sTable = null; var sTableHeight = 0; var sTableWidth = 0; var sTop = 0; // Accumulator for "top:" in the second sRows loop, below. var sWidth = 0; // End of stack variable declarations. sTable = $(pEltTable); sRows = sTable.findClosest(pSelectorTable, pSelectorRow); sRows.each(function(i) { sRow = $(this); sCols = sRow.findClosest(pSelectorRow, pSelectorCell); sCols.each(function(j) { sCol = $(this); sHeight = sCol.height(); sWidth = sCol.width(); sMaxHeights [i] = ((i == sMaxHeights.length) ? sHeight : Math.max(sMaxHeights [i], sHeight)); sMaxWidths [j] = ((j == sMaxWidths .length) ? sWidth : Math.max(sMaxWidths [j], sWidth)); }); sRow.data(sNamespace, {fCols: sCols});// Cache this sRow's ".findClosest()" for use in the next sRows loop. }); sRows.each(function(i) { sRow = $(this); // This plug-in is normally needed only in the cases of MSIE 6 or 7, neither of which supports display:table-row, // not even in standards-compliant mode. In the cases of Firefox, Google Chrome, Opera and Safari, it's possible // for the row's display to be table-row. According to http://www.w3.org/TR/CSS2/visuren.html#choose-position, // "The effect of 'position:relative' on table-row-group, table-header-group, table-footer-group, table-row, // table-column-group, table-column, table-cell, and table-caption elements is undefined." // // The problem is that all 4 of the standards-compliant browsers have chosen to do nothing when position:relative // is applied to display:table-row, not even the change of frame-of-reference aspect for position:absolute. // That results in the cells being absolutely-positioned within something higher up in the DOM tree, usually // the element. That looks REALLY horrible. So, if this plug-in (and the call to it) are not within MSIE // "conditional comments", we need to throw away table-row. (A better solution is to use conditional comments // to prevent this plug-in from doing anything in a browser that supports table-row.) if (sRow.css("display") == "table-row") sRow.css("display", "block"); if (sRow.css("position") == "static") // Don't change if already positioned (frame-of-reference for cells). sRow.css ({ position: "relative", // Forces sCol's absolute positioning, below, to be within sRow. top: "0px", // (But don't actually offset vertically.) left: "0px" // (And don't actually offset horizontally.) }); sCols = sRow.data(sNamespace).fCols; // Cached ".findClosest()" from prev sRows loop. sLeft = 0; sCols.each(function(j) { sCol = $(this); if (sCol.css("display") == "table-cell") sCol.css("display", "block"); sCol.css ({ position: "absolute", top: 0, left: sLeft, height: sMaxHeights [i], width: sMaxWidths [j] }); sColHeight = sCol.outerHeight (true); sColWidth = sCol.outerWidth (true); sLeft += sColWidth; }); sRowHeight = sColHeight; sRowWidth = sLeft; if (!sBoxModel) // Internet Explorer 6 and 7 in QuirksMode: { sRowHeight += sInnerTrimVertical (sRow); sRowWidth += sInnerTrimHorzontal (sRow); } sRow.css ({ height: sRowHeight, width: sRowWidth }); sTableHeight += (sBoxModel ? sRow.outerHeight(true) : sRowHeight); sTableWidth = (sBoxModel ? sRow.outerWidth (true) : sRowWidth); }); if (!sBoxModel) // Internet Explorer 6 and 7 in QuirksMode: { sTableHeight += sInnerTrimVertical (sTable); sTableWidth += sInnerTrimHorzontal (sTable); } if (sTable.css("display") == "table") sTable.css("display", "block"); sTable.css ({ height: sTableHeight, width: sTableWidth }); };// End of $.alignPseudoTables // *************************************************************************************************************** // *************************************************************************************************************** $.fn.alignPseudoTables = function (pSelectorTable, pSelectorRow, pSelectorCell) { return this.each(function() { $.alignPseudoTable(pSelectorTable, pSelectorRow, pSelectorCell, this); }); }; // *************************************************************************************************************** // *************************************************************************************************************** })(jQuery); // End jquery.alignPseudoTables.js