| Index: tools/binary_size/template/D3SymbolTreeMap.js
|
| diff --git a/tools/binary_size/template/D3SymbolTreeMap.js b/tools/binary_size/template/D3SymbolTreeMap.js
|
| deleted file mode 100644
|
| index 2c3bab6e07f6a51f6a0d6f5932fc0207b06ae6a2..0000000000000000000000000000000000000000
|
| --- a/tools/binary_size/template/D3SymbolTreeMap.js
|
| +++ /dev/null
|
| @@ -1,911 +0,0 @@
|
| -// Copyright 2014 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| -
|
| -// TODO:
|
| -// 1. Visibility functions: base on boxPadding.t, not 15
|
| -// 2. Track a maxDisplayDepth that is user-settable:
|
| -// maxDepth == currentRoot.depth + maxDisplayDepth
|
| -function D3SymbolTreeMap(mapWidth, mapHeight, levelsToShow) {
|
| - this._mapContainer = undefined;
|
| - this._mapWidth = mapWidth;
|
| - this._mapHeight = mapHeight;
|
| - this.boxPadding = {'l': 5, 'r': 5, 't': 20, 'b': 5};
|
| - this.infobox = undefined;
|
| - this._maskContainer = undefined;
|
| - this._highlightContainer = undefined;
|
| - // Transition in this order:
|
| - // 1. Exiting items go away.
|
| - // 2. Updated items move.
|
| - // 3. New items enter.
|
| - this._exitDuration=500;
|
| - this._updateDuration=500;
|
| - this._enterDuration=500;
|
| - this._firstTransition=true;
|
| - this._layout = undefined;
|
| - this._currentRoot = undefined;
|
| - this._currentNodes = undefined;
|
| - this._treeData = undefined;
|
| - this._maxLevelsToShow = levelsToShow;
|
| - this._currentMaxDepth = this._maxLevelsToShow;
|
| -}
|
| -
|
| -/**
|
| - * Make a number pretty, with comma separators.
|
| - */
|
| -D3SymbolTreeMap._pretty = function(num) {
|
| - var asString = String(num);
|
| - var result = '';
|
| - var counter = 0;
|
| - for (var x = asString.length - 1; x >= 0; x--) {
|
| - counter++;
|
| - if (counter === 4) {
|
| - result = ',' + result;
|
| - counter = 1;
|
| - }
|
| - result = asString.charAt(x) + result;
|
| - }
|
| - return result;
|
| -}
|
| -
|
| -/**
|
| - * Express a number in terms of KiB, MiB, GiB, etc.
|
| - * Note that these are powers of 2, not of 10.
|
| - */
|
| -D3SymbolTreeMap._byteify = function(num) {
|
| - var suffix;
|
| - if (num >= 1024) {
|
| - if (num >= 1024 * 1024 * 1024) {
|
| - suffix = 'GiB';
|
| - num = num / (1024 * 1024 * 1024);
|
| - } else if (num >= 1024 * 1024) {
|
| - suffix = 'MiB';
|
| - num = num / (1024 * 1024);
|
| - } else if (num >= 1024) {
|
| - suffix = 'KiB'
|
| - num = num / 1024;
|
| - }
|
| - return num.toFixed(2) + ' ' + suffix;
|
| - }
|
| - return num + ' B';
|
| -}
|
| -
|
| -D3SymbolTreeMap._NM_SYMBOL_TYPE_DESCRIPTIONS = {
|
| - 'b': '.bss',
|
| - 'd': '.data and .data.*',
|
| - 'r': '.rodata',
|
| - 't': '.text',
|
| - 'v': 'Vtable entry',
|
| - '!': 'Generated Symbols (typeinfo, thunks, etc)',
|
| -};
|
| -D3SymbolTreeMap._NM_SYMBOL_TYPES = '';
|
| -for (var symbol_type in D3SymbolTreeMap._NM_SYMBOL_TYPE_DESCRIPTIONS) {
|
| - D3SymbolTreeMap._NM_SYMBOL_TYPES += symbol_type;
|
| -}
|
| -
|
| -/**
|
| - * Given a symbol type code, look up and return a human-readable description
|
| - * of that symbol type. If the symbol type does not match one of the known
|
| - * types, the unrecognized description (corresponding to symbol type '?') is
|
| - * returned instead of null or undefined.
|
| - */
|
| -D3SymbolTreeMap._getSymbolDescription = function(type) {
|
| - var result = D3SymbolTreeMap._NM_SYMBOL_TYPE_DESCRIPTIONS[type];
|
| - if (result === undefined) {
|
| - result = D3SymbolTreeMap._NM_SYMBOL_TYPE_DESCRIPTIONS['?'];
|
| - }
|
| - return result;
|
| -}
|
| -
|
| -D3SymbolTreeMap._colorArray = [
|
| - 'rgb(190,186,218)',
|
| - 'rgb(253,180,98)',
|
| - 'rgb(141,211,199)',
|
| - 'rgb(128,177,211)',
|
| - 'rgb(255,237,111)',
|
| - 'rgb(204,235,197)',
|
| -]
|
| -
|
| -D3SymbolTreeMap._initColorMap = function() {
|
| - var map = {};
|
| - var numColors = D3SymbolTreeMap._colorArray.length;
|
| - var count = 0;
|
| - for (var key in D3SymbolTreeMap._NM_SYMBOL_TYPE_DESCRIPTIONS) {
|
| - var index = count++ % numColors;
|
| - map[key] = d3.rgb(D3SymbolTreeMap._colorArray[index]);
|
| - }
|
| - D3SymbolTreeMap._colorMap = map;
|
| -}
|
| -D3SymbolTreeMap._initColorMap();
|
| -
|
| -D3SymbolTreeMap.getColorForType = function(type) {
|
| - var result = D3SymbolTreeMap._colorMap[type];
|
| - if (result === undefined) return d3.rgb('rgb(255,255,255)');
|
| - return result;
|
| -}
|
| -
|
| -D3SymbolTreeMap.prototype.init = function() {
|
| - this.infobox = this._createInfoBox();
|
| - this._mapContainer = d3.select('body').append('div')
|
| - .style('position', 'relative')
|
| - .style('width', this._mapWidth)
|
| - .style('height', this._mapHeight)
|
| - .style('padding', 0)
|
| - .style('margin', 0)
|
| - .style('box-shadow', '5px 5px 5px #888');
|
| - this._layout = this._createTreeMapLayout();
|
| - this._setData(tree_data); // TODO: Don't use global 'tree_data'
|
| -}
|
| -
|
| -/**
|
| - * Sets the data displayed by the treemap and layint out the map.
|
| - */
|
| -D3SymbolTreeMap.prototype._setData = function(data) {
|
| - this._treeData = data;
|
| - console.time('_crunchStats');
|
| - this._crunchStats(data);
|
| - console.timeEnd('_crunchStats');
|
| - this._currentRoot = this._treeData;
|
| - this._currentNodes = this._layout.nodes(this._currentRoot);
|
| - this._currentMaxDepth = this._maxLevelsToShow;
|
| - this._doLayout();
|
| -}
|
| -
|
| -/**
|
| - * Recursively traverses the entire tree starting from the specified node,
|
| - * computing statistics and recording metadata as it goes. Call this method
|
| - * only once per imported tree.
|
| - */
|
| -D3SymbolTreeMap.prototype._crunchStats = function(node) {
|
| - var stack = [];
|
| - stack.idCounter = 0;
|
| - this._crunchStatsHelper(stack, node);
|
| -}
|
| -
|
| -/**
|
| - * Invoke the specified visitor function on all data elements currently shown
|
| - * in the treemap including any and all of their children, starting at the
|
| - * currently-displayed root and descening recursively. The function will be
|
| - * passed the datum element representing each node. No traversal guarantees
|
| - * are made.
|
| - */
|
| -D3SymbolTreeMap.prototype.visitFromDisplayedRoot = function(visitor) {
|
| - this._visit(this._currentRoot, visitor);
|
| -}
|
| -
|
| -/**
|
| - * Helper function for visit functions.
|
| - */
|
| -D3SymbolTreeMap.prototype._visit = function(datum, visitor) {
|
| - visitor.call(this, datum);
|
| - if (datum.children) for (var i = 0; i < datum.children.length; i++) {
|
| - this._visit(datum.children[i], visitor);
|
| - }
|
| -}
|
| -
|
| -D3SymbolTreeMap.prototype._crunchStatsHelper = function(stack, node) {
|
| - // Only overwrite the node ID if it isn't already set.
|
| - // This allows stats to be crunched multiple times on subsets of data
|
| - // without breaking the data-to-ID bindings. New nodes get new IDs.
|
| - if (node.id === undefined) node.id = stack.idCounter++;
|
| - if (node.children === undefined) {
|
| - // Leaf node (symbol); accumulate stats.
|
| - for (var i = 0; i < stack.length; i++) {
|
| - var ancestor = stack[i];
|
| - if (!ancestor.symbol_stats) ancestor.symbol_stats = {};
|
| - if (ancestor.symbol_stats[node.t] === undefined) {
|
| - // New symbol type we haven't seen before, just record.
|
| - ancestor.symbol_stats[node.t] = {'count': 1,
|
| - 'size': node.value};
|
| - } else {
|
| - // Existing symbol type, increment.
|
| - ancestor.symbol_stats[node.t].count++;
|
| - ancestor.symbol_stats[node.t].size += node.value;
|
| - }
|
| - }
|
| - } else for (var i = 0; i < node.children.length; i++) {
|
| - stack.push(node);
|
| - this._crunchStatsHelper(stack, node.children[i]);
|
| - stack.pop();
|
| - }
|
| -}
|
| -
|
| -D3SymbolTreeMap.prototype._createTreeMapLayout = function() {
|
| - var result = d3.layout.treemap()
|
| - .padding([this.boxPadding.t, this.boxPadding.r,
|
| - this.boxPadding.b, this.boxPadding.l])
|
| - .size([this._mapWidth, this._mapHeight]);
|
| - return result;
|
| -}
|
| -
|
| -D3SymbolTreeMap.prototype.resize = function(width, height) {
|
| - this._mapWidth = width;
|
| - this._mapHeight = height;
|
| - this._mapContainer.style('width', width).style('height', height);
|
| - this._layout.size([this._mapWidth, this._mapHeight]);
|
| - this._currentNodes = this._layout.nodes(this._currentRoot);
|
| - this._doLayout();
|
| -}
|
| -
|
| -D3SymbolTreeMap.prototype._zoomDatum = function(datum) {
|
| - if (this._currentRoot === datum) return; // already here
|
| - this._hideHighlight(datum);
|
| - this._hideInfoBox(datum);
|
| - this._currentRoot = datum;
|
| - this._currentNodes = this._layout.nodes(this._currentRoot);
|
| - this._currentMaxDepth = this._currentRoot.depth + this._maxLevelsToShow;
|
| - console.log('zooming into datum ' + this._currentRoot.n);
|
| - this._doLayout();
|
| -}
|
| -
|
| -D3SymbolTreeMap.prototype.setMaxLevels = function(levelsToShow) {
|
| - this._maxLevelsToShow = levelsToShow;
|
| - this._currentNodes = this._layout.nodes(this._currentRoot);
|
| - this._currentMaxDepth = this._currentRoot.depth + this._maxLevelsToShow;
|
| - console.log('setting max levels to show: ' + this._maxLevelsToShow);
|
| - this._doLayout();
|
| -}
|
| -
|
| -/**
|
| - * Clone the specified tree, returning an independent copy of the data.
|
| - * Only the original attributes expected to exist prior to invoking
|
| - * _crunchStatsHelper are retained, with the exception of the 'id' attribute
|
| - * (which must be retained for proper transitions).
|
| - * If the optional filter parameter is provided, it will be called with 'this'
|
| - * set to this treemap instance and passed the 'datum' object as an argument.
|
| - * When specified, the copy will retain only the data for which the filter
|
| - * function returns true.
|
| - */
|
| -D3SymbolTreeMap.prototype._clone = function(datum, filter) {
|
| - var trackingStats = false;
|
| - if (this.__cloneState === undefined) {
|
| - console.time('_clone');
|
| - trackingStats = true;
|
| - this.__cloneState = {'accepted': 0, 'rejected': 0,
|
| - 'forced': 0, 'pruned': 0};
|
| - }
|
| -
|
| - // Must go depth-first. All parents of children that are accepted by the
|
| - // filter must be preserved!
|
| - var copy = {'n': datum.n, 'k': datum.k};
|
| - var childAccepted = false;
|
| - if (datum.children !== undefined) {
|
| - for (var i = 0; i < datum.children.length; i++) {
|
| - var copiedChild = this._clone(datum.children[i], filter);
|
| - if (copiedChild !== undefined) {
|
| - childAccepted = true; // parent must also be accepted.
|
| - if (copy.children === undefined) copy.children = [];
|
| - copy.children.push(copiedChild);
|
| - }
|
| - }
|
| - }
|
| -
|
| - // Ignore nodes that don't match the filter, when present.
|
| - var accept = false;
|
| - if (childAccepted) {
|
| - // Parent of an accepted child must also be accepted.
|
| - this.__cloneState.forced++;
|
| - accept = true;
|
| - } else if (filter !== undefined && filter.call(this, datum) !== true) {
|
| - this.__cloneState.rejected++;
|
| - } else if (datum.children === undefined) {
|
| - // Accept leaf nodes that passed the filter
|
| - this.__cloneState.accepted++;
|
| - accept = true;
|
| - } else {
|
| - // Non-leaf node. If no children are accepted, prune it.
|
| - this.__cloneState.pruned++;
|
| - }
|
| -
|
| - if (accept) {
|
| - if (datum.id !== undefined) copy.id = datum.id;
|
| - if (datum.lastPathElement !== undefined) {
|
| - copy.lastPathElement = datum.lastPathElement;
|
| - }
|
| - if (datum.t !== undefined) copy.t = datum.t;
|
| - if (datum.value !== undefined && datum.children === undefined) {
|
| - copy.value = datum.value;
|
| - }
|
| - } else {
|
| - // Discard the copy we were going to return
|
| - copy = undefined;
|
| - }
|
| -
|
| - if (trackingStats === true) {
|
| - // We are the fist call in the recursive chain.
|
| - console.timeEnd('_clone');
|
| - var totalAccepted = this.__cloneState.accepted +
|
| - this.__cloneState.forced;
|
| - console.log(
|
| - totalAccepted + ' nodes retained (' +
|
| - this.__cloneState.forced + ' forced by accepted children, ' +
|
| - this.__cloneState.accepted + ' accepted on their own merits), ' +
|
| - this.__cloneState.rejected + ' nodes (and their children) ' +
|
| - 'filtered out,' +
|
| - this.__cloneState.pruned + ' nodes pruned because because no ' +
|
| - 'children remained.');
|
| - delete this.__cloneState;
|
| - }
|
| - return copy;
|
| -}
|
| -
|
| -D3SymbolTreeMap.prototype.filter = function(filter) {
|
| - // Ensure we have a copy of the original root.
|
| - if (this._backupTree === undefined) this._backupTree = this._treeData;
|
| - this._mapContainer.selectAll('div').remove();
|
| - this._setData(this._clone(this._backupTree, filter));
|
| -}
|
| -
|
| -D3SymbolTreeMap.prototype._doLayout = function() {
|
| - console.time('_doLayout');
|
| - this._handleInodes();
|
| - this._handleLeaves();
|
| - this._firstTransition = false;
|
| - console.timeEnd('_doLayout');
|
| -}
|
| -
|
| -D3SymbolTreeMap.prototype._highlightElement = function(datum, selection) {
|
| - this._showHighlight(datum, selection);
|
| -}
|
| -
|
| -D3SymbolTreeMap.prototype._unhighlightElement = function(datum, selection) {
|
| - this._hideHighlight(datum, selection);
|
| -}
|
| -
|
| -D3SymbolTreeMap.prototype._handleInodes = function() {
|
| - console.time('_handleInodes');
|
| - var thisTreeMap = this;
|
| - var inodes = this._currentNodes.filter(function(datum){
|
| - return (datum.depth <= thisTreeMap._currentMaxDepth) &&
|
| - datum.children !== undefined;
|
| - });
|
| - var cellsEnter = this._mapContainer.selectAll('div.inode')
|
| - .data(inodes, function(datum) { return datum.id; })
|
| - .enter()
|
| - .append('div').attr('class', 'inode').attr('id', function(datum){
|
| - return 'node-' + datum.id;});
|
| -
|
| -
|
| - // Define enter/update/exit for inodes
|
| - cellsEnter
|
| - .append('div')
|
| - .attr('class', 'rect inode_rect_entering')
|
| - .style('z-index', function(datum) { return datum.id * 2; })
|
| - .style('position', 'absolute')
|
| - .style('left', function(datum) { return datum.x; })
|
| - .style('top', function(datum){ return datum.y; })
|
| - .style('width', function(datum){ return datum.dx; })
|
| - .style('height', function(datum){ return datum.dy; })
|
| - .style('opacity', '0')
|
| - .style('border', '1px solid black')
|
| - .style('background-image', function(datum) {
|
| - return thisTreeMap._makeSymbolBucketBackgroundImage.call(
|
| - thisTreeMap, datum);
|
| - })
|
| - .style('background-color', function(datum) {
|
| - if (datum.t === undefined) return 'rgb(220,220,220)';
|
| - return D3SymbolTreeMap.getColorForType(datum.t).toString();
|
| - })
|
| - .on('mouseover', function(datum){
|
| - thisTreeMap._highlightElement.call(
|
| - thisTreeMap, datum, d3.select(this));
|
| - thisTreeMap._showInfoBox.call(thisTreeMap, datum);
|
| - })
|
| - .on('mouseout', function(datum){
|
| - thisTreeMap._unhighlightElement.call(
|
| - thisTreeMap, datum, d3.select(this));
|
| - thisTreeMap._hideInfoBox.call(thisTreeMap, datum);
|
| - })
|
| - .on('mousemove', function(){
|
| - thisTreeMap._moveInfoBox.call(thisTreeMap, event);
|
| - })
|
| - .on('dblclick', function(datum){
|
| - if (datum !== thisTreeMap._currentRoot) {
|
| - // Zoom into the selection
|
| - thisTreeMap._zoomDatum(datum);
|
| - } else if (datum.parent) {
|
| - console.log('event.shiftKey=' + event.shiftKey);
|
| - if (event.shiftKey === true) {
|
| - // Back to root
|
| - thisTreeMap._zoomDatum(thisTreeMap._treeData);
|
| - } else {
|
| - // Zoom out of the selection
|
| - thisTreeMap._zoomDatum(datum.parent);
|
| - }
|
| - }
|
| - });
|
| - cellsEnter
|
| - .append('div')
|
| - .attr('class', 'label inode_label_entering')
|
| - .style('z-index', function(datum) { return (datum.id * 2) + 1; })
|
| - .style('position', 'absolute')
|
| - .style('left', function(datum){ return datum.x; })
|
| - .style('top', function(datum){ return datum.y; })
|
| - .style('width', function(datum) { return datum.dx; })
|
| - .style('height', function(datum) { return thisTreeMap.boxPadding.t; })
|
| - .style('opacity', '0')
|
| - .style('pointer-events', 'none')
|
| - .style('-webkit-user-select', 'none')
|
| - .style('overflow', 'hidden') // required for ellipsis
|
| - .style('white-space', 'nowrap') // required for ellipsis
|
| - .style('text-overflow', 'ellipsis')
|
| - .style('text-align', 'center')
|
| - .style('vertical-align', 'top')
|
| - .style('visibility', function(datum) {
|
| - return (datum.dx < 15 || datum.dy < 15) ? 'hidden' : 'visible';
|
| - })
|
| - .text(function(datum) {
|
| - var sizeish = ' [' + D3SymbolTreeMap._byteify(datum.value) + ']'
|
| - var text;
|
| - if (datum.k === 'b') { // bucket
|
| - if (datum === thisTreeMap._currentRoot) {
|
| - text = thisTreeMap.pathFor(datum) + ': '
|
| - + D3SymbolTreeMap._getSymbolDescription(datum.t)
|
| - } else {
|
| - text = D3SymbolTreeMap._getSymbolDescription(datum.t);
|
| - }
|
| - } else if (datum === thisTreeMap._currentRoot) {
|
| - // The top-most level should always show the complete path
|
| - text = thisTreeMap.pathFor(datum);
|
| - } else {
|
| - // Anything that isn't a bucket or a leaf (symbol) or the
|
| - // current root should just show its name.
|
| - text = datum.n;
|
| - }
|
| - return text + sizeish;
|
| - }
|
| - );
|
| -
|
| - // Complicated transition logic:
|
| - // For nodes that are entering, we want to fade them in in-place AFTER
|
| - // any adjusting nodes have resized and moved around. That way, new nodes
|
| - // seamlessly appear in the right spot after their containers have resized
|
| - // and moved around.
|
| - // To do this we do some trickery:
|
| - // 1. Define a '_entering' class on the entering elements
|
| - // 2. Use this to select only the entering elements and apply the opacity
|
| - // transition.
|
| - // 3. Use the same transition to drop the '_entering' suffix, so that they
|
| - // will correctly update in later zoom/resize/whatever operations.
|
| - // 4. The update transition is achieved by selecting the elements without
|
| - // the '_entering_' suffix and applying movement and resizing transition
|
| - // effects.
|
| - this._mapContainer.selectAll('div.inode_rect_entering').transition()
|
| - .duration(thisTreeMap._enterDuration).delay(
|
| - this._firstTransition ? 0 : thisTreeMap._exitDuration +
|
| - thisTreeMap._updateDuration)
|
| - .attr('class', 'rect inode_rect')
|
| - .style('opacity', '1')
|
| - this._mapContainer.selectAll('div.inode_label_entering').transition()
|
| - .duration(thisTreeMap._enterDuration).delay(
|
| - this._firstTransition ? 0 : thisTreeMap._exitDuration +
|
| - thisTreeMap._updateDuration)
|
| - .attr('class', 'label inode_label')
|
| - .style('opacity', '1')
|
| - this._mapContainer.selectAll('div.inode_rect').transition()
|
| - .duration(thisTreeMap._updateDuration).delay(thisTreeMap._exitDuration)
|
| - .style('opacity', '1')
|
| - .style('background-image', function(datum) {
|
| - return thisTreeMap._makeSymbolBucketBackgroundImage.call(
|
| - thisTreeMap, datum);
|
| - })
|
| - .style('left', function(datum) { return datum.x; })
|
| - .style('top', function(datum){ return datum.y; })
|
| - .style('width', function(datum){ return datum.dx; })
|
| - .style('height', function(datum){ return datum.dy; });
|
| - this._mapContainer.selectAll('div.inode_label').transition()
|
| - .duration(thisTreeMap._updateDuration).delay(thisTreeMap._exitDuration)
|
| - .style('opacity', '1')
|
| - .style('visibility', function(datum) {
|
| - return (datum.dx < 15 || datum.dy < 15) ? 'hidden' : 'visible';
|
| - })
|
| - .style('left', function(datum){ return datum.x; })
|
| - .style('top', function(datum){ return datum.y; })
|
| - .style('width', function(datum) { return datum.dx; })
|
| - .style('height', function(datum) { return thisTreeMap.boxPadding.t; })
|
| - .text(function(datum) {
|
| - var sizeish = ' [' + D3SymbolTreeMap._byteify(datum.value) + ']'
|
| - var text;
|
| - if (datum.k === 'b') {
|
| - if (datum === thisTreeMap._currentRoot) {
|
| - text = thisTreeMap.pathFor(datum) + ': ' +
|
| - D3SymbolTreeMap._getSymbolDescription(datum.t)
|
| - } else {
|
| - text = D3SymbolTreeMap._getSymbolDescription(datum.t);
|
| - }
|
| - } else if (datum === thisTreeMap._currentRoot) {
|
| - // The top-most level should always show the complete path
|
| - text = thisTreeMap.pathFor(datum);
|
| - } else {
|
| - // Anything that isn't a bucket or a leaf (symbol) or the
|
| - // current root should just show its name.
|
| - text = datum.n;
|
| - }
|
| - return text + sizeish;
|
| - });
|
| - var exit = this._mapContainer.selectAll('div.inode')
|
| - .data(inodes, function(datum) { return 'inode-' + datum.id; })
|
| - .exit();
|
| - exit.selectAll('div.inode_rect').transition().duration(
|
| - thisTreeMap._exitDuration).style('opacity', 0);
|
| - exit.selectAll('div.inode_label').transition().duration(
|
| - thisTreeMap._exitDuration).style('opacity', 0);
|
| - exit.transition().delay(thisTreeMap._exitDuration + 1).remove();
|
| -
|
| - console.log(inodes.length + ' inodes layed out.');
|
| - console.timeEnd('_handleInodes');
|
| -}
|
| -
|
| -D3SymbolTreeMap.prototype._handleLeaves = function() {
|
| - console.time('_handleLeaves');
|
| - var color_fn = d3.scale.category10();
|
| - var thisTreeMap = this;
|
| - var leaves = this._currentNodes.filter(function(datum){
|
| - return (datum.depth <= thisTreeMap._currentMaxDepth) &&
|
| - datum.children === undefined; });
|
| - var cellsEnter = this._mapContainer.selectAll('div.leaf')
|
| - .data(leaves, function(datum) { return datum.id; })
|
| - .enter()
|
| - .append('div').attr('class', 'leaf').attr('id', function(datum){
|
| - return 'node-' + datum.id;
|
| - });
|
| -
|
| - // Define enter/update/exit for leaves
|
| - cellsEnter
|
| - .append('div')
|
| - .attr('class', 'rect leaf_rect_entering')
|
| - .style('z-index', function(datum) { return datum.id * 2; })
|
| - .style('position', 'absolute')
|
| - .style('left', function(datum){ return datum.x; })
|
| - .style('top', function(datum){ return datum.y; })
|
| - .style('width', function(datum){ return datum.dx; })
|
| - .style('height', function(datum){ return datum.dy; })
|
| - .style('opacity', '0')
|
| - .style('background-color', function(datum) {
|
| - if (datum.t === undefined) return 'rgb(220,220,220)';
|
| - return D3SymbolTreeMap.getColorForType(datum.t)
|
| - .darker(0.3).toString();
|
| - })
|
| - .style('border', '1px solid black')
|
| - .on('mouseover', function(datum){
|
| - thisTreeMap._highlightElement.call(
|
| - thisTreeMap, datum, d3.select(this));
|
| - thisTreeMap._showInfoBox.call(thisTreeMap, datum);
|
| - })
|
| - .on('mouseout', function(datum){
|
| - thisTreeMap._unhighlightElement.call(
|
| - thisTreeMap, datum, d3.select(this));
|
| - thisTreeMap._hideInfoBox.call(thisTreeMap, datum);
|
| - })
|
| - .on('mousemove', function(){ thisTreeMap._moveInfoBox.call(
|
| - thisTreeMap, event);
|
| - });
|
| - cellsEnter
|
| - .append('div')
|
| - .attr('class', 'label leaf_label_entering')
|
| - .style('z-index', function(datum) { return (datum.id * 2) + 1; })
|
| - .style('position', 'absolute')
|
| - .style('left', function(datum){ return datum.x; })
|
| - .style('top', function(datum){ return datum.y; })
|
| - .style('width', function(datum) { return datum.dx; })
|
| - .style('height', function(datum) { return datum.dy; })
|
| - .style('opacity', '0')
|
| - .style('pointer-events', 'none')
|
| - .style('-webkit-user-select', 'none')
|
| - .style('overflow', 'hidden') // required for ellipsis
|
| - .style('white-space', 'nowrap') // required for ellipsis
|
| - .style('text-overflow', 'ellipsis')
|
| - .style('text-align', 'center')
|
| - .style('vertical-align', 'middle')
|
| - .style('visibility', function(datum) {
|
| - return (datum.dx < 15 || datum.dy < 15) ? 'hidden' : 'visible';
|
| - })
|
| - .text(function(datum) { return datum.n; });
|
| -
|
| - // Complicated transition logic: See note in _handleInodes()
|
| - this._mapContainer.selectAll('div.leaf_rect_entering').transition()
|
| - .duration(thisTreeMap._enterDuration).delay(
|
| - this._firstTransition ? 0 : thisTreeMap._exitDuration +
|
| - thisTreeMap._updateDuration)
|
| - .attr('class', 'rect leaf_rect')
|
| - .style('opacity', '1')
|
| - this._mapContainer.selectAll('div.leaf_label_entering').transition()
|
| - .duration(thisTreeMap._enterDuration).delay(
|
| - this._firstTransition ? 0 : thisTreeMap._exitDuration +
|
| - thisTreeMap._updateDuration)
|
| - .attr('class', 'label leaf_label')
|
| - .style('opacity', '1')
|
| - this._mapContainer.selectAll('div.leaf_rect').transition()
|
| - .duration(thisTreeMap._updateDuration).delay(thisTreeMap._exitDuration)
|
| - .style('opacity', '1')
|
| - .style('left', function(datum){ return datum.x; })
|
| - .style('top', function(datum){ return datum.y; })
|
| - .style('width', function(datum){ return datum.dx; })
|
| - .style('height', function(datum){ return datum.dy; });
|
| - this._mapContainer.selectAll('div.leaf_label').transition()
|
| - .duration(thisTreeMap._updateDuration).delay(thisTreeMap._exitDuration)
|
| - .style('opacity', '1')
|
| - .style('visibility', function(datum) {
|
| - return (datum.dx < 15 || datum.dy < 15) ? 'hidden' : 'visible';
|
| - })
|
| - .style('left', function(datum){ return datum.x; })
|
| - .style('top', function(datum){ return datum.y; })
|
| - .style('width', function(datum) { return datum.dx; })
|
| - .style('height', function(datum) { return datum.dy; });
|
| - var exit = this._mapContainer.selectAll('div.leaf')
|
| - .data(leaves, function(datum) { return 'leaf-' + datum.id; })
|
| - .exit();
|
| - exit.selectAll('div.leaf_rect').transition()
|
| - .duration(thisTreeMap._exitDuration)
|
| - .style('opacity', 0);
|
| - exit.selectAll('div.leaf_label').transition()
|
| - .duration(thisTreeMap._exitDuration)
|
| - .style('opacity', 0);
|
| - exit.transition().delay(thisTreeMap._exitDuration + 1).remove();
|
| -
|
| - console.log(leaves.length + ' leaves layed out.');
|
| - console.timeEnd('_handleLeaves');
|
| -}
|
| -
|
| -D3SymbolTreeMap.prototype._makeSymbolBucketBackgroundImage = function(datum) {
|
| - if (!(datum.t === undefined && datum.depth == this._currentMaxDepth)) {
|
| - return 'none';
|
| - }
|
| - var text = '';
|
| - var lastStop = 0;
|
| - for (var x = 0; x < D3SymbolTreeMap._NM_SYMBOL_TYPES.length; x++) {
|
| - symbol_type = D3SymbolTreeMap._NM_SYMBOL_TYPES.charAt(x);
|
| - var stats = datum.symbol_stats[symbol_type];
|
| - if (stats !== undefined) {
|
| - if (text.length !== 0) {
|
| - text += ', ';
|
| - }
|
| - var percent = 100 * (stats.size / datum.value);
|
| - var nowStop = lastStop + percent;
|
| - var tempcolor = D3SymbolTreeMap.getColorForType(symbol_type);
|
| - var color = d3.rgb(tempcolor).toString();
|
| - text += color + ' ' + lastStop + '%, ' + color + ' ' +
|
| - nowStop + '%';
|
| - lastStop = nowStop;
|
| - }
|
| - }
|
| - return 'linear-gradient(' + (datum.dx > datum.dy ? 'to right' :
|
| - 'to bottom') + ', ' + text + ')';
|
| -}
|
| -
|
| -D3SymbolTreeMap.prototype.pathFor = function(datum) {
|
| - if (datum.__path) return datum.__path;
|
| - parts=[];
|
| - node = datum;
|
| - while (node) {
|
| - if (node.k === 'p') { // path node
|
| - if(node.n !== '/') parts.unshift(node.n);
|
| - }
|
| - node = node.parent;
|
| - }
|
| - datum.__path = '/' + parts.join('/');
|
| - return datum.__path;
|
| -}
|
| -
|
| -D3SymbolTreeMap.prototype._createHighlight = function(datum, selection) {
|
| - var x = parseInt(selection.style('left'));
|
| - var y = parseInt(selection.style('top'));
|
| - var w = parseInt(selection.style('width'));
|
| - var h = parseInt(selection.style('height'));
|
| - datum.highlight = this._mapContainer.append('div')
|
| - .attr('id', 'h-' + datum.id)
|
| - .attr('class', 'highlight')
|
| - .style('pointer-events', 'none')
|
| - .style('-webkit-user-select', 'none')
|
| - .style('z-index', '999999')
|
| - .style('position', 'absolute')
|
| - .style('top', y-2)
|
| - .style('left', x-2)
|
| - .style('width', w+4)
|
| - .style('height', h+4)
|
| - .style('margin', 0)
|
| - .style('padding', 0)
|
| - .style('border', '4px outset rgba(250,40,200,0.9)')
|
| - .style('box-sizing', 'border-box')
|
| - .style('opacity', 0.0);
|
| -}
|
| -
|
| -D3SymbolTreeMap.prototype._showHighlight = function(datum, selection) {
|
| - if (datum === this._currentRoot) return;
|
| - if (datum.highlight === undefined) {
|
| - this._createHighlight(datum, selection);
|
| - }
|
| - datum.highlight.transition().duration(200).style('opacity', 1.0);
|
| -}
|
| -
|
| -D3SymbolTreeMap.prototype._hideHighlight = function(datum, selection) {
|
| - if (datum.highlight === undefined) return;
|
| - datum.highlight.transition().duration(750)
|
| - .style('opacity', 0)
|
| - .each('end', function(){
|
| - if (datum.highlight) datum.highlight.remove();
|
| - delete datum.highlight;
|
| - });
|
| -}
|
| -
|
| -D3SymbolTreeMap.prototype._createInfoBox = function() {
|
| - return d3.select('body')
|
| - .append('div')
|
| - .attr('id', 'infobox')
|
| - .style('z-index', '2147483647') // (2^31) - 1: Hopefully safe :)
|
| - .style('position', 'absolute')
|
| - .style('visibility', 'hidden')
|
| - .style('background-color', 'rgba(255,255,255, 0.9)')
|
| - .style('border', '1px solid black')
|
| - .style('padding', '10px')
|
| - .style('-webkit-user-select', 'none')
|
| - .style('box-shadow', '3px 3px rgba(70,70,70,0.5)')
|
| - .style('border-radius', '10px')
|
| - .style('white-space', 'nowrap');
|
| -}
|
| -
|
| -D3SymbolTreeMap.prototype._showInfoBox = function(datum) {
|
| - this.infobox.text('');
|
| - var numSymbols = 0;
|
| - var sizeish = D3SymbolTreeMap._pretty(datum.value) + ' bytes (' +
|
| - D3SymbolTreeMap._byteify(datum.value) + ')';
|
| - if (datum.k === 'p' || datum.k === 'b') { // path or bucket
|
| - if (datum.symbol_stats) { // can be empty if filters are applied
|
| - for (var x = 0; x < D3SymbolTreeMap._NM_SYMBOL_TYPES.length; x++) {
|
| - symbol_type = D3SymbolTreeMap._NM_SYMBOL_TYPES.charAt(x);
|
| - var stats = datum.symbol_stats[symbol_type];
|
| - if (stats !== undefined) numSymbols += stats.count;
|
| - }
|
| - }
|
| - } else if (datum.k === 's') { // symbol
|
| - numSymbols = 1;
|
| - }
|
| -
|
| - if (datum.k === 'p' && !datum.lastPathElement) {
|
| - this.infobox.append('div').text('Directory: ' + this.pathFor(datum))
|
| - this.infobox.append('div').text('Size: ' + sizeish);
|
| - } else {
|
| - if (datum.k === 'p') { // path
|
| - this.infobox.append('div').text('File: ' + this.pathFor(datum))
|
| - this.infobox.append('div').text('Size: ' + sizeish);
|
| - } else if (datum.k === 'b') { // bucket
|
| - this.infobox.append('div').text('Symbol Bucket: ' +
|
| - D3SymbolTreeMap._getSymbolDescription(datum.t));
|
| - this.infobox.append('div').text('Count: ' + numSymbols);
|
| - this.infobox.append('div').text('Size: ' + sizeish);
|
| - this.infobox.append('div').text('Location: ' + this.pathFor(datum))
|
| - } else if (datum.k === 's') { // symbol
|
| - this.infobox.append('div').text('Symbol: ' + datum.n);
|
| - this.infobox.append('div').text('Type: ' +
|
| - D3SymbolTreeMap._getSymbolDescription(datum.t));
|
| - this.infobox.append('div').text('Size: ' + sizeish);
|
| - this.infobox.append('div').text('Location: ' + this.pathFor(datum))
|
| - }
|
| - }
|
| - if (datum.k === 'p') {
|
| - this.infobox.append('div')
|
| - .text('Number of symbols: ' + D3SymbolTreeMap._pretty(numSymbols));
|
| - if (datum.symbol_stats) { // can be empty if filters are applied
|
| - var table = this.infobox.append('table')
|
| - .attr('border', 1).append('tbody');
|
| - var header = table.append('tr');
|
| - header.append('th').text('Type');
|
| - header.append('th').text('Count');
|
| - header.append('th')
|
| - .style('white-space', 'nowrap')
|
| - .text('Total Size (Bytes)');
|
| - for (var x = 0; x < D3SymbolTreeMap._NM_SYMBOL_TYPES.length; x++) {
|
| - symbol_type = D3SymbolTreeMap._NM_SYMBOL_TYPES.charAt(x);
|
| - var stats = datum.symbol_stats[symbol_type];
|
| - if (stats !== undefined) {
|
| - var tr = table.append('tr');
|
| - tr.append('td')
|
| - .style('white-space', 'nowrap')
|
| - .text(D3SymbolTreeMap._getSymbolDescription(
|
| - symbol_type));
|
| - tr.append('td').text(D3SymbolTreeMap._pretty(stats.count));
|
| - tr.append('td').text(D3SymbolTreeMap._pretty(stats.size));
|
| - }
|
| - }
|
| - }
|
| - }
|
| - this.infobox.style('visibility', 'visible');
|
| -}
|
| -
|
| -D3SymbolTreeMap.prototype._hideInfoBox = function(datum) {
|
| - this.infobox.style('visibility', 'hidden');
|
| -}
|
| -
|
| -D3SymbolTreeMap.prototype._moveInfoBox = function(event) {
|
| - var element = document.getElementById('infobox');
|
| - var w = element.offsetWidth;
|
| - var h = element.offsetHeight;
|
| - var offsetLeft = 10;
|
| - var offsetTop = 10;
|
| -
|
| - var rightLimit = window.innerWidth;
|
| - var rightEdge = event.pageX + offsetLeft + w;
|
| - if (rightEdge > rightLimit) {
|
| - // Too close to screen edge, reflect around the cursor
|
| - offsetLeft = -1 * (w + offsetLeft);
|
| - }
|
| -
|
| - var bottomLimit = window.innerHeight;
|
| - var bottomEdge = event.pageY + offsetTop + h;
|
| - if (bottomEdge > bottomLimit) {
|
| - // Too close ot screen edge, reflect around the cursor
|
| - offsetTop = -1 * (h + offsetTop);
|
| - }
|
| -
|
| - this.infobox.style('top', (event.pageY + offsetTop) + 'px')
|
| - .style('left', (event.pageX + offsetLeft) + 'px');
|
| -}
|
| -
|
| -D3SymbolTreeMap.prototype.biggestSymbols = function(maxRecords) {
|
| - var result = undefined;
|
| - var smallest = undefined;
|
| - var sortFunction = function(a,b) {
|
| - var result = b.value - a.value;
|
| - if (result !== 0) return result; // sort by size
|
| - var pathA = treemap.pathFor(a); // sort by path
|
| - var pathB = treemap.pathFor(b);
|
| - if (pathA > pathB) return 1;
|
| - if (pathB > pathA) return -1;
|
| - return a.n - b.n; // sort by symbol name
|
| - };
|
| - this.visitFromDisplayedRoot(function(datum) {
|
| - if (datum.children) return; // ignore non-leaves
|
| - if (!result) { // first element
|
| - result = [datum];
|
| - smallest = datum.value;
|
| - return;
|
| - }
|
| - if (result.length < maxRecords) { // filling the array
|
| - result.push(datum);
|
| - return;
|
| - }
|
| - if (datum.value > smallest) { // array is already full
|
| - result.push(datum);
|
| - result.sort(sortFunction);
|
| - result.pop(); // get rid of smallest element
|
| - smallest = result[maxRecords - 1].value; // new threshold for entry
|
| - }
|
| - });
|
| - result.sort(sortFunction);
|
| - return result;
|
| -}
|
| -
|
| -D3SymbolTreeMap.prototype.biggestPaths = function(maxRecords) {
|
| - var result = undefined;
|
| - var smallest = undefined;
|
| - var sortFunction = function(a,b) {
|
| - var result = b.value - a.value;
|
| - if (result !== 0) return result; // sort by size
|
| - var pathA = treemap.pathFor(a); // sort by path
|
| - var pathB = treemap.pathFor(b);
|
| - if (pathA > pathB) return 1;
|
| - if (pathB > pathA) return -1;
|
| - console.log('warning, multiple entries for the same path: ' + pathA);
|
| - return 0; // should be impossible
|
| - };
|
| - this.visitFromDisplayedRoot(function(datum) {
|
| - if (!datum.lastPathElement) return; // ignore non-files
|
| - if (!result) { // first element
|
| - result = [datum];
|
| - smallest = datum.value;
|
| - return;
|
| - }
|
| - if (result.length < maxRecords) { // filling the array
|
| - result.push(datum);
|
| - return;
|
| - }
|
| - if (datum.value > smallest) { // array is already full
|
| - result.push(datum);
|
| - result.sort(sortFunction);
|
| - result.pop(); // get rid of smallest element
|
| - smallest = result[maxRecords - 1].value; // new threshold for entry
|
| - }
|
| - });
|
| - result.sort(sortFunction);
|
| - return result;
|
| -}
|
|
|