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