1 // expand/collapse button (expander) is added if height of a cell content
2 // exceeds CLIP_HEIGHT px.
5 // Height in pixels of an expander image.
6 var EXPANDER_HEIGHT
= 13;
8 // Path to images for an expander.
9 var imgPath
= "./images/expandcollapse/";
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?
18 // Extracts group and cell indices from an id of the form identifier_group_cell.
19 function getCellIdx(id
) {
20 var idx
= id
.substr(id
.indexOf("_") + 1).split("_");
21 return { 'group': idx
[0], 'cell': idx
[1] };
24 // Returns { 'height', 'expanded' } info for a cell with a given id.
25 function getCellInfo(id
) {
26 var idx
= getCellIdx(id
);
27 return CellsInfo
[idx
.group
][idx
.cell
];
30 // Initialization, add nodes, collect info.
31 function initExpandCollapse() {
32 if (!document
.getElementById
)
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) {
41 var cellCount
=0, newGroupCreated
= false;
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) {
47 var expandableDiv
= divs
[j
];
49 if (expandableDiv
.className
.indexOf("expandable") == -1)
52 if (expandableDiv
.offsetHeight
<= CLIP_HEIGHT
)
55 // We found a div wrapping a cell content whose height exceeds
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.
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";
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
;
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 dynamically and mouse pointer goes out of an element.
86 (getCellInfo(this.id
).expanded
? "arrows_light.gif"
87 : "ellipses_light.gif");
89 expander
.onmouseover = function() {
91 (getCellInfo(this.id
).expanded
? "arrows_dark.gif"
92 : "ellipses_dark.gif");
94 expander
.onmouseout = function() {
96 (getCellInfo(this.id
).expanded
? "arrows_light.gif"
97 : "ellipses_light.gif");
100 expandableDiv
.innerHTML
= "";
101 expandableDiv
.appendChild(data
);
102 expandableDiv
.appendChild(expander
);
103 expandableDiv
.style
.height
= CLIP_HEIGHT
+ "px";
104 expandableDiv
.id
= "cell"+ idxStr
;
106 // Keep original cell height and its ecpanded/cpllapsed state.
107 if (!newGroupCreated
) {
108 CellsInfo
[groupCount
] = [];
109 newGroupCreated
= true;
111 CellsInfo
[groupCount
][cellCount
] = { 'height' : originalHeight
,
112 'expanded' : false };
115 groupCount
+= newGroupCreated
? 1 : 0;
119 function 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
131 if (elemOffset
< scrollTop
)
137 // Invoked when an expander is pressed; expand/collapse a cell.
138 function expandCollapse(id
) {
139 var cellInfo
= getCellInfo(id
);
140 var idx
= getCellIdx(id
);
142 // New height of a row.
144 // Smart page scrolling may be done after collapse.
147 if (cellInfo
.expanded
) {
148 // Cell is expanded - collapse the row height to CLIP_HEIGHT.
149 newHeight
= CLIP_HEIGHT
;
150 mayNeedScroll
= true;
153 // Cell is collapsed - expand the row height to the cells original height.
154 newHeight
= cellInfo
.height
;
155 mayNeedScroll
= false;
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
];
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;
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;
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
187 if (!isElemTopVisible(clickedExpandableDiv
)) {
188 window
.scrollBy(0, newHeight
- cellInfo
.height
);