1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
|
// expand/collapse button (expander) is added if height of a cell content
// exceeds CLIP_HEIGHT px.
var CLIP_HEIGHT = 135;
// Height in pixels of an expander image.
var EXPANDER_HEIGHT = 13;
// Path to images for an expander.
var imgPath = "./images/expandcollapse/";
// array[group][cell] of { 'height', 'expanded' }.
// group: a number; cells of the same group belong to the same table row.
// cell: a number; unique index of a cell in a group.
// height: a number, px; original height of a cell in a table.
// expanded: boolean; is a cell expanded or collapsed?
var CellsInfo = [];
// Extracts group and cell indices from an id of the form identifier_group_cell.
function getCellIdx(id) {
var idx = id.substr(id.indexOf("_") + 1).split("_");
return { 'group': idx[0], 'cell': idx[1] };
}
// Returns { 'height', 'expanded' } info for a cell with a given id.
function getCellInfo(id) {
var idx = getCellIdx(id);
return CellsInfo[idx.group][idx.cell];
}
// Initialization, add nodes, collect info.
function initExpandCollapse() {
if (!document.getElementById)
return;
var groupCount = 0;
// Examine all table rows in the document.
var rows = document.body.getElementsByTagName("tr");
for (var i=0; i<rows.length; i+=1) {
var cellCount=0, newGroupCreated = false;
// Examine all divs in a table row.
var divs = rows[i].getElementsByTagName("div");
for (var j=0; j<divs.length; j+=1) {
var expandableDiv = divs[j];
if (expandableDiv.className.indexOf("expandable") == -1)
continue;
if (expandableDiv.offsetHeight <= CLIP_HEIGHT)
continue;
// We found a div wrapping a cell content whose height exceeds
// CLIP_HEIGHT.
var originalHeight = expandableDiv.offsetHeight;
// Unique postfix for ids for generated nodes for a given cell.
var idxStr = "_" + groupCount + "_" + cellCount;
// Create an expander and an additional wrapper for a cell content.
//
// --- expandableDiv ----
// --- expandableDiv --- | ------ data ------ |
// | cell content | -> | | cell content | |
// --------------------- | ------------------ |
// | ---- expander ---- |
// ----------------------
var data = document.createElement("div");
data.className = "data";
data.id = "data" + idxStr;
data.innerHTML = expandableDiv.innerHTML;
with (data.style) { height = (CLIP_HEIGHT - EXPANDER_HEIGHT) + "px";
overflow = "hidden" }
var expander = document.createElement("img");
with (expander.style) { display = "block"; paddingTop = "5px"; }
expander.src = imgPath + "ellipses_light.gif";
expander.id = "expander" + idxStr;
// Add mouse calbacks to expander.
expander.onclick = function() {
expandCollapse(this.id);
// Hack for Opera - onmouseout callback is not invoked when page
// content changes dynamically and mouse pointer goes out of an element.
this.src = imgPath +
(getCellInfo(this.id).expanded ? "arrows_light.gif"
: "ellipses_light.gif");
}
expander.onmouseover = function() {
this.src = imgPath +
(getCellInfo(this.id).expanded ? "arrows_dark.gif"
: "ellipses_dark.gif");
}
expander.onmouseout = function() {
this.src = imgPath +
(getCellInfo(this.id).expanded ? "arrows_light.gif"
: "ellipses_light.gif");
}
expandableDiv.innerHTML = "";
expandableDiv.appendChild(data);
expandableDiv.appendChild(expander);
expandableDiv.style.height = CLIP_HEIGHT + "px";
expandableDiv.id = "cell"+ idxStr;
// Keep original cell height and its ecpanded/cpllapsed state.
if (!newGroupCreated) {
CellsInfo[groupCount] = [];
newGroupCreated = true;
}
CellsInfo[groupCount][cellCount] = { 'height' : originalHeight,
'expanded' : false };
cellCount += 1;
}
groupCount += newGroupCreated ? 1 : 0;
}
}
function isElemTopVisible(elem) {
var body = document.body,
html = document.documentElement,
// Calculate expandableDiv absolute Y coordinate from the top of body.
bodyRect = body.getBoundingClientRect(),
elemRect = elem.getBoundingClientRect(),
elemOffset = Math.floor(elemRect.top - bodyRect.top),
// Calculate the absoute Y coordinate of visible area.
scrollTop = html.scrollTop || body && body.scrollTop || 0;
scrollTop -= html.clientTop; // IE<8
if (elemOffset < scrollTop)
return false;
return true;
}
// Invoked when an expander is pressed; expand/collapse a cell.
function expandCollapse(id) {
var cellInfo = getCellInfo(id);
var idx = getCellIdx(id);
// New height of a row.
var newHeight;
// Smart page scrolling may be done after collapse.
var mayNeedScroll;
if (cellInfo.expanded) {
// Cell is expanded - collapse the row height to CLIP_HEIGHT.
newHeight = CLIP_HEIGHT;
mayNeedScroll = true;
}
else {
// Cell is collapsed - expand the row height to the cells original height.
newHeight = cellInfo.height;
mayNeedScroll = false;
}
// Update all cells (height and expanded/collapsed state) in a row according
// to the new height of the row.
for (var i = 0; i < CellsInfo[idx.group].length; i++) {
var idxStr = "_" + idx.group + "_" + i;
var expandableDiv = document.getElementById("cell" + idxStr);
expandableDiv.style.height = newHeight + "px";
var data = document.getElementById("data" + idxStr);
var expander = document.getElementById("expander" + idxStr);
var state = CellsInfo[idx.group][i];
if (state.height > newHeight) {
// Cell height exceeds row height - collapse a cell.
data.style.height = (newHeight - EXPANDER_HEIGHT) + "px";
expander.src = imgPath + "ellipses_light.gif";
CellsInfo[idx.group][i].expanded = false;
} else {
// Cell height is less then or equal to row height - expand a cell.
data.style.height = "";
expander.src = imgPath + "arrows_light.gif";
CellsInfo[idx.group][i].expanded = true;
}
}
if (mayNeedScroll) {
var idxStr = "_" + idx.group + "_" + idx.cell;
var clickedExpandableDiv = document.getElementById("cell" + idxStr);
// Scroll page up if a row is collapsed and the rows top is above the
// viewport. The amount of scroll is the difference between a new and old
// row height.
if (!isElemTopVisible(clickedExpandableDiv)) {
window.scrollBy(0, newHeight - cellInfo.height);
}
}
}
|