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 */ 10var 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 */ 25MenuView.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 */ 42MenuView.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