145056dc5SAnton Yartsev// expand/collapse button (expander) is added if height of a cell content 245056dc5SAnton Yartsev// exceeds CLIP_HEIGHT px. 345056dc5SAnton Yartsevvar CLIP_HEIGHT = 135; 445056dc5SAnton Yartsev 545056dc5SAnton Yartsev// Height in pixels of an expander image. 645056dc5SAnton Yartsevvar EXPANDER_HEIGHT = 13; 745056dc5SAnton Yartsev 845056dc5SAnton Yartsev// Path to images for an expander. 945056dc5SAnton Yartsevvar imgPath = "./images/expandcollapse/"; 1045056dc5SAnton Yartsev 1145056dc5SAnton Yartsev// array[group][cell] of { 'height', 'expanded' }. 1245056dc5SAnton Yartsev// group: a number; cells of the same group belong to the same table row. 1345056dc5SAnton Yartsev// cell: a number; unique index of a cell in a group. 1445056dc5SAnton Yartsev// height: a number, px; original height of a cell in a table. 1545056dc5SAnton Yartsev// expanded: boolean; is a cell expanded or collapsed? 1645056dc5SAnton Yartsevvar CellsInfo = []; 1745056dc5SAnton Yartsev 1845056dc5SAnton Yartsev// Extracts group and cell indices from an id of the form identifier_group_cell. 1945056dc5SAnton Yartsevfunction getCellIdx(id) { 2045056dc5SAnton Yartsev var idx = id.substr(id.indexOf("_") + 1).split("_"); 2145056dc5SAnton Yartsev return { 'group': idx[0], 'cell': idx[1] }; 2245056dc5SAnton Yartsev} 2345056dc5SAnton Yartsev 2445056dc5SAnton Yartsev// Returns { 'height', 'expanded' } info for a cell with a given id. 2545056dc5SAnton Yartsevfunction getCellInfo(id) { 2645056dc5SAnton Yartsev var idx = getCellIdx(id); 2745056dc5SAnton Yartsev return CellsInfo[idx.group][idx.cell]; 2845056dc5SAnton Yartsev} 2945056dc5SAnton Yartsev 3045056dc5SAnton Yartsev// Initialization, add nodes, collect info. 3145056dc5SAnton Yartsevfunction initExpandCollapse() { 3245056dc5SAnton Yartsev if (!document.getElementById) 3345056dc5SAnton Yartsev return; 3445056dc5SAnton Yartsev 3545056dc5SAnton Yartsev var groupCount = 0; 3645056dc5SAnton Yartsev 3745056dc5SAnton Yartsev // Examine all table rows in the document. 3845056dc5SAnton Yartsev var rows = document.body.getElementsByTagName("tr"); 3945056dc5SAnton Yartsev for (var i=0; i<rows.length; i+=1) { 4045056dc5SAnton Yartsev 4145056dc5SAnton Yartsev var cellCount=0, newGroupCreated = false; 4245056dc5SAnton Yartsev 4345056dc5SAnton Yartsev // Examine all divs in a table row. 4445056dc5SAnton Yartsev var divs = rows[i].getElementsByTagName("div"); 4545056dc5SAnton Yartsev for (var j=0; j<divs.length; j+=1) { 4645056dc5SAnton Yartsev 4745056dc5SAnton Yartsev var expandableDiv = divs[j]; 4845056dc5SAnton Yartsev 4945056dc5SAnton Yartsev if (expandableDiv.className.indexOf("expandable") == -1) 5045056dc5SAnton Yartsev continue; 5145056dc5SAnton Yartsev 5245056dc5SAnton Yartsev if (expandableDiv.offsetHeight <= CLIP_HEIGHT) 5345056dc5SAnton Yartsev continue; 5445056dc5SAnton Yartsev 5545056dc5SAnton Yartsev // We found a div wrapping a cell content whose height exceeds 5645056dc5SAnton Yartsev // CLIP_HEIGHT. 5745056dc5SAnton Yartsev var originalHeight = expandableDiv.offsetHeight; 5845056dc5SAnton Yartsev // Unique postfix for ids for generated nodes for a given cell. 5945056dc5SAnton Yartsev var idxStr = "_" + groupCount + "_" + cellCount; 6045056dc5SAnton Yartsev // Create an expander and an additional wrapper for a cell content. 6145056dc5SAnton Yartsev // 6245056dc5SAnton Yartsev // --- expandableDiv ---- 6345056dc5SAnton Yartsev // --- expandableDiv --- | ------ data ------ | 6445056dc5SAnton Yartsev // | cell content | -> | | cell content | | 6545056dc5SAnton Yartsev // --------------------- | ------------------ | 6645056dc5SAnton Yartsev // | ---- expander ---- | 6745056dc5SAnton Yartsev // ---------------------- 6845056dc5SAnton Yartsev var data = document.createElement("div"); 6945056dc5SAnton Yartsev data.className = "data"; 7045056dc5SAnton Yartsev data.id = "data" + idxStr; 7145056dc5SAnton Yartsev data.innerHTML = expandableDiv.innerHTML; 7245056dc5SAnton Yartsev with (data.style) { height = (CLIP_HEIGHT - EXPANDER_HEIGHT) + "px"; 7345056dc5SAnton Yartsev overflow = "hidden" } 7445056dc5SAnton Yartsev 7545056dc5SAnton Yartsev var expander = document.createElement("img"); 7645056dc5SAnton Yartsev with (expander.style) { display = "block"; paddingTop = "5px"; } 7745056dc5SAnton Yartsev expander.src = imgPath + "ellipses_light.gif"; 7845056dc5SAnton Yartsev expander.id = "expander" + idxStr; 7945056dc5SAnton Yartsev 8045056dc5SAnton Yartsev // Add mouse calbacks to expander. 8145056dc5SAnton Yartsev expander.onclick = function() { 8245056dc5SAnton Yartsev expandCollapse(this.id); 8345056dc5SAnton Yartsev // Hack for Opera - onmouseout callback is not invoked when page 84*c5e54ddaSHiroshi Inoue // content changes dynamically and mouse pointer goes out of an element. 8545056dc5SAnton Yartsev this.src = imgPath + 8645056dc5SAnton Yartsev (getCellInfo(this.id).expanded ? "arrows_light.gif" 8745056dc5SAnton Yartsev : "ellipses_light.gif"); 8845056dc5SAnton Yartsev } 8945056dc5SAnton Yartsev expander.onmouseover = function() { 9045056dc5SAnton Yartsev this.src = imgPath + 9145056dc5SAnton Yartsev (getCellInfo(this.id).expanded ? "arrows_dark.gif" 9245056dc5SAnton Yartsev : "ellipses_dark.gif"); 9345056dc5SAnton Yartsev } 9445056dc5SAnton Yartsev expander.onmouseout = function() { 9545056dc5SAnton Yartsev this.src = imgPath + 9645056dc5SAnton Yartsev (getCellInfo(this.id).expanded ? "arrows_light.gif" 9745056dc5SAnton Yartsev : "ellipses_light.gif"); 9845056dc5SAnton Yartsev } 9945056dc5SAnton Yartsev 10045056dc5SAnton Yartsev expandableDiv.innerHTML = ""; 10145056dc5SAnton Yartsev expandableDiv.appendChild(data); 10245056dc5SAnton Yartsev expandableDiv.appendChild(expander); 10345056dc5SAnton Yartsev expandableDiv.style.height = CLIP_HEIGHT + "px"; 10445056dc5SAnton Yartsev expandableDiv.id = "cell"+ idxStr; 10545056dc5SAnton Yartsev 10645056dc5SAnton Yartsev // Keep original cell height and its ecpanded/cpllapsed state. 10745056dc5SAnton Yartsev if (!newGroupCreated) { 10845056dc5SAnton Yartsev CellsInfo[groupCount] = []; 10945056dc5SAnton Yartsev newGroupCreated = true; 11045056dc5SAnton Yartsev } 11145056dc5SAnton Yartsev CellsInfo[groupCount][cellCount] = { 'height' : originalHeight, 11245056dc5SAnton Yartsev 'expanded' : false }; 11345056dc5SAnton Yartsev cellCount += 1; 11445056dc5SAnton Yartsev } 11545056dc5SAnton Yartsev groupCount += newGroupCreated ? 1 : 0; 11645056dc5SAnton Yartsev } 11745056dc5SAnton Yartsev} 11845056dc5SAnton Yartsev 11945056dc5SAnton Yartsevfunction isElemTopVisible(elem) { 12045056dc5SAnton Yartsev var body = document.body, 12145056dc5SAnton Yartsev html = document.documentElement, 12245056dc5SAnton Yartsev // Calculate expandableDiv absolute Y coordinate from the top of body. 12345056dc5SAnton Yartsev bodyRect = body.getBoundingClientRect(), 12445056dc5SAnton Yartsev elemRect = elem.getBoundingClientRect(), 12545056dc5SAnton Yartsev elemOffset = Math.floor(elemRect.top - bodyRect.top), 12645056dc5SAnton Yartsev // Calculate the absoute Y coordinate of visible area. 12745056dc5SAnton Yartsev scrollTop = html.scrollTop || body && body.scrollTop || 0; 12845056dc5SAnton Yartsev scrollTop -= html.clientTop; // IE<8 12945056dc5SAnton Yartsev 13045056dc5SAnton Yartsev 13145056dc5SAnton Yartsev if (elemOffset < scrollTop) 13245056dc5SAnton Yartsev return false; 13345056dc5SAnton Yartsev 13445056dc5SAnton Yartsev return true; 13545056dc5SAnton Yartsev} 13645056dc5SAnton Yartsev 13745056dc5SAnton Yartsev// Invoked when an expander is pressed; expand/collapse a cell. 13845056dc5SAnton Yartsevfunction expandCollapse(id) { 13945056dc5SAnton Yartsev var cellInfo = getCellInfo(id); 14045056dc5SAnton Yartsev var idx = getCellIdx(id); 14145056dc5SAnton Yartsev 14245056dc5SAnton Yartsev // New height of a row. 14345056dc5SAnton Yartsev var newHeight; 14445056dc5SAnton Yartsev // Smart page scrolling may be done after collapse. 14545056dc5SAnton Yartsev var mayNeedScroll; 14645056dc5SAnton Yartsev 14745056dc5SAnton Yartsev if (cellInfo.expanded) { 14845056dc5SAnton Yartsev // Cell is expanded - collapse the row height to CLIP_HEIGHT. 14945056dc5SAnton Yartsev newHeight = CLIP_HEIGHT; 15045056dc5SAnton Yartsev mayNeedScroll = true; 15145056dc5SAnton Yartsev } 15245056dc5SAnton Yartsev else { 15345056dc5SAnton Yartsev // Cell is collapsed - expand the row height to the cells original height. 15445056dc5SAnton Yartsev newHeight = cellInfo.height; 15545056dc5SAnton Yartsev mayNeedScroll = false; 15645056dc5SAnton Yartsev } 15745056dc5SAnton Yartsev 15845056dc5SAnton Yartsev // Update all cells (height and expanded/collapsed state) in a row according 15945056dc5SAnton Yartsev // to the new height of the row. 16045056dc5SAnton Yartsev for (var i = 0; i < CellsInfo[idx.group].length; i++) { 16145056dc5SAnton Yartsev var idxStr = "_" + idx.group + "_" + i; 16245056dc5SAnton Yartsev var expandableDiv = document.getElementById("cell" + idxStr); 16345056dc5SAnton Yartsev expandableDiv.style.height = newHeight + "px"; 16445056dc5SAnton Yartsev var data = document.getElementById("data" + idxStr); 16545056dc5SAnton Yartsev var expander = document.getElementById("expander" + idxStr); 16645056dc5SAnton Yartsev var state = CellsInfo[idx.group][i]; 16745056dc5SAnton Yartsev 16845056dc5SAnton Yartsev if (state.height > newHeight) { 16945056dc5SAnton Yartsev // Cell height exceeds row height - collapse a cell. 17045056dc5SAnton Yartsev data.style.height = (newHeight - EXPANDER_HEIGHT) + "px"; 17145056dc5SAnton Yartsev expander.src = imgPath + "ellipses_light.gif"; 17245056dc5SAnton Yartsev CellsInfo[idx.group][i].expanded = false; 17345056dc5SAnton Yartsev } else { 17445056dc5SAnton Yartsev // Cell height is less then or equal to row height - expand a cell. 17545056dc5SAnton Yartsev data.style.height = ""; 17645056dc5SAnton Yartsev expander.src = imgPath + "arrows_light.gif"; 17745056dc5SAnton Yartsev CellsInfo[idx.group][i].expanded = true; 17845056dc5SAnton Yartsev } 17945056dc5SAnton Yartsev } 18045056dc5SAnton Yartsev 18145056dc5SAnton Yartsev if (mayNeedScroll) { 18245056dc5SAnton Yartsev var idxStr = "_" + idx.group + "_" + idx.cell; 18345056dc5SAnton Yartsev var clickedExpandableDiv = document.getElementById("cell" + idxStr); 18445056dc5SAnton Yartsev // Scroll page up if a row is collapsed and the rows top is above the 18545056dc5SAnton Yartsev // viewport. The amount of scroll is the difference between a new and old 18645056dc5SAnton Yartsev // row height. 18745056dc5SAnton Yartsev if (!isElemTopVisible(clickedExpandableDiv)) { 18845056dc5SAnton Yartsev window.scrollBy(0, newHeight - cellInfo.height); 18945056dc5SAnton Yartsev } 19045056dc5SAnton Yartsev } 19145056dc5SAnton Yartsev} 192