Home | History | Annotate | Download | only in net_internals
      1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 /**
      6  * Inherit the prototype methods from one constructor into another.
      7  */
      8 function inherits(childCtor, parentCtor) {
      9   function tempCtor() {};
     10   tempCtor.prototype = parentCtor.prototype;
     11   childCtor.superClass_ = parentCtor.prototype;
     12   childCtor.prototype = new tempCtor();
     13   childCtor.prototype.constructor = childCtor;
     14 };
     15 
     16 /**
     17  * Sets the width (in pixels) on a DOM node.
     18  */
     19 function setNodeWidth(node, widthPx) {
     20   node.style.width = widthPx.toFixed(0) + 'px';
     21 }
     22 
     23 /**
     24  * Sets the height (in pixels) on a DOM node.
     25  */
     26 function setNodeHeight(node, heightPx) {
     27   node.style.height = heightPx.toFixed(0) + 'px';
     28 }
     29 
     30 /**
     31  * Sets the position and size of a DOM node (in pixels).
     32  */
     33 function setNodePosition(node, leftPx, topPx, widthPx, heightPx) {
     34   node.style.left = leftPx.toFixed(0) + 'px';
     35   node.style.top = topPx.toFixed(0) + 'px';
     36   setNodeWidth(node, widthPx);
     37   setNodeHeight(node, heightPx);
     38 }
     39 
     40 /**
     41  * Sets the visibility for a DOM node.
     42  */
     43 function setNodeDisplay(node, isVisible) {
     44   node.style.display = isVisible ? '' : 'none';
     45 }
     46 
     47 /**
     48  * Adds a node to |parentNode|, of type |tagName|.
     49  */
     50 function addNode(parentNode, tagName) {
     51   var elem = parentNode.ownerDocument.createElement(tagName);
     52   parentNode.appendChild(elem);
     53   return elem;
     54 }
     55 
     56 /**
     57  * Adds |text| to node |parentNode|.
     58  */
     59 function addTextNode(parentNode, text) {
     60   var textNode = parentNode.ownerDocument.createTextNode(text);
     61   parentNode.appendChild(textNode);
     62   return textNode;
     63 }
     64 
     65 /**
     66  * Adds a node to |parentNode|, of type |tagName|.  Then adds
     67  * |text| to the new node.
     68  */
     69 function addNodeWithText(parentNode, tagName, text) {
     70   var elem = parentNode.ownerDocument.createElement(tagName);
     71   parentNode.appendChild(elem);
     72   addTextNode(elem, text);
     73   return elem;
     74 }
     75 
     76 /**
     77  * Adds or removes a CSS class to |node|.
     78  */
     79 function changeClassName(node, classNameToAddOrRemove, isAdd) {
     80   // Multiple classes can be separated by spaces.
     81   var currentNames = node.className.split(' ');
     82 
     83   if (isAdd) {
     84     if (!(classNameToAddOrRemove in currentNames)) {
     85       currentNames.push(classNameToAddOrRemove);
     86     }
     87   } else {
     88     for (var i = 0; i < currentNames.length; ++i) {
     89       if (currentNames[i] == classNameToAddOrRemove) {
     90         currentNames.splice(i, 1);
     91         break;
     92       }
     93     }
     94   }
     95 
     96   node.className = currentNames.join(' ');
     97 }
     98 
     99 function getKeyWithValue(map, value) {
    100   for (key in map) {
    101     if (map[key] == value)
    102       return key;
    103   }
    104   return '?';
    105 }
    106 
    107 /**
    108  * Looks up |key| in |map|, and returns the resulting entry, if  there is one.
    109  * Otherwise, returns |key|.  Intended primarily for use with incomplete
    110  * tables, and for reasonable behavior with system enumerations that may be
    111  * extended in the future.
    112  */
    113 function tryGetValueWithKey(map, key) {
    114   if (key in map)
    115     return map[key];
    116   return key;
    117 }
    118 
    119 /**
    120  * Builds a string by repeating |str| |count| times.
    121  */
    122 function makeRepeatedString(str, count) {
    123   var out = [];
    124   for (var i = 0; i < count; ++i)
    125     out.push(str);
    126   return out.join('');
    127 }
    128 
    129 /**
    130  * TablePrinter is a helper to format a table as ascii art or an HTML table.
    131  *
    132  * Usage: call addRow() and addCell() repeatedly to specify the data.
    133  *
    134  * addHeaderCell() can optionally be called to specify header cells for a
    135  * single header row.  The header row appears at the top of an HTML formatted
    136  * table, and uses thead and th tags.  In ascii tables, the header is separated
    137  * from the table body by a partial row of dashes.
    138  *
    139  * setTitle() can optionally be used to set a title that is displayed before
    140  * the header row.  In HTML tables, it uses the title class and in ascii tables
    141  * it's between two rows of dashes.
    142  *
    143  * Once all the fields have been input, call toText() to format it as text or
    144  * toHTML() to format it as HTML.
    145  */
    146 function TablePrinter() {
    147   this.rows_ = [];
    148   this.hasHeaderRow_ = false;
    149   this.title_ = null;
    150 }
    151 
    152 /**
    153  * Links are only used in HTML tables.
    154  */
    155 function TablePrinterCell(value) {
    156   this.text = '' + value;
    157   this.link = null;
    158   this.alignRight = false;
    159   this.allowOverflow = false;
    160 }
    161 
    162 /**
    163  * Starts a new row.
    164  */
    165 TablePrinter.prototype.addRow = function() {
    166   this.rows_.push([]);
    167 };
    168 
    169 /**
    170  * Adds a column to the current row, setting its value to cellText.
    171  *
    172  * @returns {!TablePrinterCell} the cell that was added.
    173  */
    174 TablePrinter.prototype.addCell = function(cellText) {
    175   var r = this.rows_[this.rows_.length - 1];
    176   var cell = new TablePrinterCell(cellText);
    177   r.push(cell);
    178   return cell;
    179 };
    180 
    181 TablePrinter.prototype.setTitle = function(title) {
    182   this.title_ = title;
    183 };
    184 
    185 /**
    186  * Adds a header row, if not already present, and adds a new column to it,
    187  * setting its contents to |headerText|.
    188  *
    189  * @returns {!TablePrinterCell} the cell that was added.
    190  */
    191 TablePrinter.prototype.addHeaderCell = function(headerText) {
    192   // Insert empty new row at start of |rows_| if currently no header row.
    193   if (!this.hasHeaderRow_) {
    194     this.rows_.splice(0, 0, []);
    195     this.hasHeaderRow_ = true;
    196   }
    197   var cell = new TablePrinterCell(headerText);
    198   this.rows_[0].push(cell);
    199   return cell;
    200 };
    201 
    202 /**
    203  * Returns the maximum number of columns this table contains.
    204  */
    205 TablePrinter.prototype.getNumColumns = function() {
    206   var numColumns = 0;
    207   for (var i = 0; i < this.rows_.length; ++i) {
    208     numColumns = Math.max(numColumns, this.rows_[i].length);
    209   }
    210   return numColumns;
    211 }
    212 
    213 /**
    214  * Returns the cell at position (rowIndex, columnIndex), or null if there is
    215  * no such cell.
    216  */
    217 TablePrinter.prototype.getCell_ = function(rowIndex, columnIndex) {
    218   if (rowIndex >= this.rows_.length)
    219     return null;
    220   var row = this.rows_[rowIndex];
    221   if (columnIndex >= row.length)
    222     return null;
    223   return row[columnIndex];
    224 };
    225 
    226 /**
    227  * Returns a formatted text representation of the table data.
    228  * |spacing| indicates number of extra spaces, if any, to add between
    229  * columns.
    230  */
    231 TablePrinter.prototype.toText = function(spacing) {
    232   var numColumns = this.getNumColumns();
    233 
    234   // Figure out the maximum width of each column.
    235   var columnWidths = [];
    236   columnWidths.length = numColumns;
    237   for (var i = 0; i < numColumns; ++i)
    238     columnWidths[i] = 0;
    239 
    240   // If header row is present, temporarily add a spacer row to |rows_|.
    241   if (this.hasHeaderRow_) {
    242     var headerSpacerRow = [];
    243     for (var c = 0; c < numColumns; ++c) {
    244       var cell = this.getCell_(0, c);
    245       if (!cell)
    246         continue;
    247       var spacerStr = makeRepeatedString('-', cell.text.length);
    248       headerSpacerRow.push(new TablePrinterCell(spacerStr));
    249     }
    250     this.rows_.splice(1, 0, headerSpacerRow);
    251   }
    252 
    253   var numRows = this.rows_.length;
    254   for (var c = 0; c < numColumns; ++c) {
    255     for (var r = 0; r < numRows; ++r) {
    256       var cell = this.getCell_(r, c);
    257       if (cell && !cell.allowOverflow) {
    258         columnWidths[c] = Math.max(columnWidths[c], cell.text.length);
    259       }
    260     }
    261   }
    262 
    263   var out = [];
    264 
    265   // Print title, if present.
    266   if (this.title_) {
    267     var titleSpacerStr = makeRepeatedString('-', this.title_.length);
    268     out.push(titleSpacerStr);
    269     out.push('\n');
    270     out.push(this.title_);
    271     out.push('\n');
    272     out.push(titleSpacerStr);
    273     out.push('\n');
    274   }
    275 
    276   // Print each row.
    277   var spacingStr = makeRepeatedString(' ', spacing);
    278   for (var r = 0; r < numRows; ++r) {
    279     for (var c = 0; c < numColumns; ++c) {
    280       var cell = this.getCell_(r, c);
    281       if (cell) {
    282         // Pad the cell with spaces to make it fit the maximum column width.
    283         var padding = columnWidths[c] - cell.text.length;
    284         var paddingStr = makeRepeatedString(' ', padding);
    285 
    286         if (cell.alignRight) {
    287           out.push(paddingStr);
    288           out.push(cell.text);
    289         } else {
    290           out.push(cell.text);
    291           out.push(paddingStr);
    292         }
    293         out.push(spacingStr);
    294       }
    295     }
    296     out.push('\n');
    297   }
    298 
    299   // Remove spacer row under the header row, if one was added.
    300   if (this.hasHeaderRow_)
    301     this.rows_.splice(1, 1);
    302 
    303   return out.join('');
    304 };
    305 
    306 /**
    307  * Adds a new HTML table to the node |parent| using the specified style.
    308  */
    309 TablePrinter.prototype.toHTML = function(parent, style) {
    310   var numRows = this.rows_.length;
    311   var numColumns = this.getNumColumns();
    312 
    313   var table = addNode(parent, 'table');
    314   table.setAttribute('class', style);
    315 
    316   var thead = addNode(table, 'thead');
    317   var tbody = addNode(table, 'tbody');
    318 
    319   // Add title, if needed.
    320   if (this.title_) {
    321     var tableTitleRow = addNode(thead, 'tr');
    322     var tableTitle = addNodeWithText(tableTitleRow, 'th', this.title_);
    323     tableTitle.colSpan = numColumns;
    324     changeClassName(tableTitle, 'title', true);
    325   }
    326 
    327   // Fill table body, adding header row first, if needed.
    328   for (var r = 0; r < numRows; ++r) {
    329     var cellType;
    330     var row;
    331     if (r == 0 && this.hasHeaderRow_) {
    332       row = addNode(thead, 'tr');
    333       cellType = 'th';
    334     } else {
    335       row = addNode(tbody, 'tr');
    336       cellType = 'td';
    337     }
    338     for (var c = 0; c < numColumns; ++c) {
    339       var cell = this.getCell_(r, c);
    340       if (cell) {
    341         var tableCell = addNode(row, cellType, cell.text);
    342         if (cell.alignRight)
    343           tableCell.alignRight = true;
    344         // If allowing overflow on the rightmost cell of a row,
    345         // make the cell span the rest of the columns.  Otherwise,
    346         // ignore the flag.
    347         if (cell.allowOverflow && !this.getCell_(r, c + 1))
    348           tableCell.colSpan = numColumns - c;
    349         if (cell.link) {
    350           var linkNode = addNodeWithText(tableCell, 'a', cell.text);
    351           linkNode.href = cell.link;
    352         } else {
    353           addTextNode(tableCell, cell.text);
    354         }
    355       }
    356     }
    357   }
    358   return table;
    359 };
    360 
    361