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