Home | History | Annotate | Download | only in static
      1 // Copyright 2013 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  * This is a view class showing tree-menu.
      7  * @param {Object} profiler Must have addListener method.
      8  * @construct
      9  */
     10 var MenuView = function(profiler) {
     11   this.profiler_ = profiler;
     12   this.placeholder_ = '#category-menu';
     13 
     14   // Update graph view and menu view when profiler model changed.
     15   profiler.addListener('changed', this.redraw_.bind(this));
     16   profiler.addListener('changed:selected', this.selectNode_.bind(this));
     17 };
     18 
     19 /**
     20  * Highlight the node being selected.
     21  * @param {string|null} id Model id.
     22  * @param {Object} pos Clicked position. Not used
     23  * @private
     24  */
     25 MenuView.prototype.selectNode_ = function(id) {
     26   var $tree = this.$tree_;
     27 
     28   if (id == null) {
     29     $tree.tree('selectNode', null);
     30     return;
     31   }
     32 
     33   var node = $tree.tree('getNodeById', id);
     34   $tree.tree('selectNode', node);
     35 };
     36 
     37 /**
     38  * Update menu view when model updated.
     39  * @param {Array.<Object>} models
     40  * @private
     41  */
     42 MenuView.prototype.redraw_ = function(models) {
     43   function convert(origin, target) {
     44     target.label = origin.name;
     45     target.id = origin.id;
     46 
     47     if ('children' in origin) {
     48       target.children = [];
     49       origin.children.forEach(function(originChild) {
     50         var targetChild = {};
     51         target.children.push(targetChild);
     52         convert(originChild, targetChild);
     53       });
     54     }
     55   }
     56 
     57   function merge(origin, target) {
     58     if (!('children' in origin))
     59       return;
     60     if (!('children' in target)) {
     61       target.children = origin.children;
     62       return;
     63     }
     64 
     65     origin.children.forEach(function(child) {
     66       // Find child with the same label in target tree.
     67       var index = target.children.reduce(function(previous, current, index) {
     68         if (child.label === current.label)
     69         return index;
     70         return previous;
     71       }, -1);
     72       if (index === -1)
     73         target.children.push(child);
     74       else
     75         merge(child, target.children[index]);
     76     });
     77   }
     78 
     79   var self = this;
     80 
     81   // Merge trees in all snapshots.
     82   var union = null;
     83   models.forEach(function(model) {
     84     var data = {};
     85     convert(model, data);
     86     if (!union)
     87       union = data;
     88     else
     89       merge(data, union);
     90   });
     91 
     92   // Draw breakdown menu.
     93   var data = [union];
     94   if (!this.$tree_) {
     95     this.$tree_ = $(this.placeholder_).tree({
     96       data: data,
     97       autoOpen: true,
     98       onCreateLi: function(node, $li) {
     99         // TODO(junjianx): Add checkbox to decide the breakdown visibility.
    100       }
    101     });
    102 
    103     // Delegate events
    104     this.$tree_.bind('tree.click', function(event) {
    105       event.preventDefault();
    106       self.profiler_.setSelected(event.node.id);
    107     });
    108     this.$tree_.bind('tree.close', function(event) {
    109       event.preventDefault();
    110       self.profiler_.unsetSub(event.node.id);
    111       self.profiler_.setSelected(event.node.id);
    112     });
    113   } else {
    114     this.$tree_.tree('loadData', data);
    115   }
    116 };
    117