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