xref: /llvm-project/clang/www/analyzer/scripts/expandcollapse.js (revision c5e54ddab31ca40a93e61f277d2bbd349fb84f3e)
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