summaryrefslogtreecommitdiffstats
path: root/tools/deep_memory_profiler
diff options
context:
space:
mode:
authorjunjianx@chromium.org <junjianx@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-08-26 02:20:56 +0000
committerjunjianx@chromium.org <junjianx@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-08-26 02:20:56 +0000
commit623d620fb864a3e983902c5ccdad4c547b680e2b (patch)
tree915c1959754fa450e58dd452b7a283b8955857f1 /tools/deep_memory_profiler
parent55af928a7fedbb15853a0226592547526832e2e5 (diff)
downloadchromium_src-623d620fb864a3e983902c5ccdad4c547b680e2b.zip
chromium_src-623d620fb864a3e983902c5ccdad4c547b680e2b.tar.gz
chromium_src-623d620fb864a3e983902c5ccdad4c547b680e2b.tar.bz2
Add tree menu for dmprof visualizer. Use jqTree as third-party plugin
BUG=259206 NOTRY=True Review URL: https://chromiumcodereview.appspot.com/23075010 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@219490 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'tools/deep_memory_profiler')
-rw-r--r--tools/deep_memory_profiler/visualizer/graph-view.js126
-rw-r--r--tools/deep_memory_profiler/visualizer/index.css30
-rw-r--r--tools/deep_memory_profiler/visualizer/index.html (renamed from tools/deep_memory_profiler/visualizer/main.html)17
-rw-r--r--tools/deep_memory_profiler/visualizer/index.js17
-rw-r--r--tools/deep_memory_profiler/visualizer/main.css9
-rw-r--r--tools/deep_memory_profiler/visualizer/main.js101
-rw-r--r--tools/deep_memory_profiler/visualizer/menu-view.js75
-rw-r--r--tools/deep_memory_profiler/visualizer/profiler-model.js170
-rw-r--r--tools/deep_memory_profiler/visualizer/profiler.js110
-rw-r--r--tools/deep_memory_profiler/visualizer/third_party/jqTree/LICENSE202
-rw-r--r--tools/deep_memory_profiler/visualizer/third_party/jqTree/OWNERS2
-rw-r--r--tools/deep_memory_profiler/visualizer/third_party/jqTree/README.chromium29
-rw-r--r--tools/deep_memory_profiler/visualizer/third_party/jqTree/README.md21
-rw-r--r--tools/deep_memory_profiler/visualizer/third_party/jqTree/jqtree.css139
-rw-r--r--tools/deep_memory_profiler/visualizer/third_party/jqTree/tree.jquery.js2609
-rw-r--r--tools/deep_memory_profiler/visualizer/utility.js57
16 files changed, 3476 insertions, 238 deletions
diff --git a/tools/deep_memory_profiler/visualizer/graph-view.js b/tools/deep_memory_profiler/visualizer/graph-view.js
new file mode 100644
index 0000000..b9b62bc
--- /dev/null
+++ b/tools/deep_memory_profiler/visualizer/graph-view.js
@@ -0,0 +1,126 @@
+// Copyright 2013 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.
+
+/**
+ * This is a view class showing flot graph.
+ * @param {Object} model Must have addListener method.
+ * @construct
+ */
+var GraphView = function(model) {
+ this.model_ = model;
+ // Update graph view and menu view when model changed.
+ model.addListener('changed', this.redraw.bind(this));
+};
+
+/**
+ * Generate lines for flot plotting.
+ * @param {Array.<Object>} categories
+ * @return {Array.<Object>}
+ */
+GraphView.prototype.generateLines_ = function(categories) {
+ function getLeaves(node, breakdowns) {
+ if ('breakdowns' in node) {
+ node.breakdowns.forEach(function(breakdown) {
+ getLeaves(breakdown, breakdowns);
+ });
+ } else {
+ breakdowns.push(node);
+ }
+ }
+
+ var lines = {};
+ var snapshotNum = categories.length;
+ // Initialize lines with all zero.
+ categories.forEach(function(category) {
+ var breakdowns = [];
+ getLeaves(category, breakdowns);
+ breakdowns.forEach(function(breakdown) {
+ var name = breakdown.name;
+ if (lines[name])
+ return;
+ lines[name] = [];
+ for (var i = 0; i < snapshotNum; ++i)
+ lines[name].push([i, 0]);
+ });
+ });
+
+ // Assignment lines with values of categories.
+ categories.forEach(function(category, index) {
+ var breakdowns = [];
+ getLeaves(category, breakdowns);
+ breakdowns.forEach(function(breakdown) {
+ var name = breakdown.name;
+ var memory = breakdown.memory;
+ lines[name][index] = [index, memory];
+ });
+ });
+
+ return Object.keys(lines).map(function(name) {
+ return {
+ label: name,
+ data: lines[name]
+ };
+ });
+};
+
+/**
+ * Update garph view when model updated.
+ * TODO(junjianx): use redraw function to improve perfomance.
+ * @param {Array.<Object>} categories
+ */
+GraphView.prototype.redraw = function(categories) {
+ var placeholder = '#graph-div';
+ var lines = this.generateLines_(categories);
+ var graph = $.plot(placeholder, lines, {
+ series: {
+ stack: true,
+ lines: { show: true, fill: true }
+ },
+ grid: {
+ hoverable: true,
+ clickable: true
+ }
+ });
+
+ // Bind click event so that user can select breakdown by clicking stack
+ // area. It firstly checks x range which clicked point is in, and all lines
+ // share same x values, so it is checked only once at first. Secondly, it
+ // checked y range by accumulated y values because this is a stack graph.
+ $(placeholder).bind('plotclick', function(event, pos, item) {
+ // If only <=1 line exists or axis area clicked, return.
+ var right = binarySearch.call(lines[0].data.map(function(point) {
+ return point[0];
+ }), pos.x);
+ if (lines.length <= 1 || right === lines.length || right === 0)
+ return;
+
+ // Calculate interpolate y value of every line.
+ for (var i = 0; i < lines.length; ++i) {
+ var line = lines[i].data;
+ // [left, right] is the range including clicked point.
+ var left = right - 1;
+ var leftPoint = {
+ x: line[left][0],
+ y: (leftPoint ? leftPoint.y : 0) + line[left][1]
+ };
+ var rightPoint = {
+ x: line[right][0],
+ y: (rightPoint ? rightPoint.y : 0) + line[right][1]
+ };
+
+ // Calculate slope of the linear equation.
+ var slope = (rightPoint.y - leftPoint.y) / (rightPoint.x - leftPoint.x);
+ var interpolateY = slope * (pos.x - rightPoint.x) + rightPoint.y;
+ if (interpolateY >= pos.y)
+ break;
+ }
+
+ // If pos.y is higher than all lines, return.
+ if (i === lines.length)
+ return;
+
+ // TODO(junjianx): temporary log for checking selected object.
+ console.log('line ' + i + ' is selected.');
+ });
+};
diff --git a/tools/deep_memory_profiler/visualizer/index.css b/tools/deep_memory_profiler/visualizer/index.css
new file mode 100644
index 0000000..4bbc166
--- /dev/null
+++ b/tools/deep_memory_profiler/visualizer/index.css
@@ -0,0 +1,30 @@
+/* Copyright 2013 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. */
+
+#graph-div {
+ width: 1024px;
+ height: 600px;
+ margin-top: 30px;
+ margin-left: 50px;
+ float: left;
+}
+
+#info-div {
+ width: 240px;
+ height: 600px;
+ margin-top: 35px;
+ margin-left: 50px;
+ float: left;
+ box-shadow: 0 4px 16px rgba(0,0,0,0.2);
+ outline: 1px solid rgba(0,0,0,0.2);
+ overflow: auto;
+}
+
+#breakdown-menu {
+ padding-left: 15px;
+}
+
+#subs-dropdown {
+ padding-left: 15px;
+} \ No newline at end of file
diff --git a/tools/deep_memory_profiler/visualizer/main.html b/tools/deep_memory_profiler/visualizer/index.html
index 75df003..b930bb4 100644
--- a/tools/deep_memory_profiler/visualizer/main.html
+++ b/tools/deep_memory_profiler/visualizer/index.html
@@ -5,15 +5,24 @@ Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<meta charset="utf-8">
-<link rel="stylesheet" href="main.css">
+<link rel="stylesheet" href="index.css">
+<link rel="stylesheet" href="third_party/jqTree/jqtree.css">
+
<script src="../../../third_party/flot/jquery.min.js"></script>
<script src="../../../third_party/flot/jquery.flot.min.js"></script>
<script src="../../../third_party/flot/jquery.flot.stack.min.js"></script>
+<script src="third_party/jqTree/tree.jquery.js"></script>
<script src="utility.js"></script>
-<script src="profiler.js"></script>
-<script src="main.js"></script>
+<script src="profiler-model.js"></script>
+<script src="graph-view.js"></script>
+<script src="menu-view.js"></script>
+<script src="index.js"></script>
<body>
<h2>Deep Memory Profiler Visulaizer</h2>
- <div id="plot" class="plot-container"></div>
+ <div id="graph-div"></div>
+ <div id="info-div">
+ <div id="subs-dropdown"></div>
+ <div id="breakdown-menu"></div>
+ </div>
</body>
diff --git a/tools/deep_memory_profiler/visualizer/index.js b/tools/deep_memory_profiler/visualizer/index.js
new file mode 100644
index 0000000..431bfde
--- /dev/null
+++ b/tools/deep_memory_profiler/visualizer/index.js
@@ -0,0 +1,17 @@
+// Copyright 2013 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.
+
+$(function() {
+ // Read original data and plot.
+ $.getJSON('data/result.json', function(jsonData) {
+ // Create model.
+ var profiler = new Profiler(jsonData);
+ // Create views subscribing model events.
+ var graphView = new GraphView(profiler);
+ var menuView = new MenuView(profiler);
+
+ // initialize categories according to roots information.
+ profiler.initializeCategories();
+ });
+});
diff --git a/tools/deep_memory_profiler/visualizer/main.css b/tools/deep_memory_profiler/visualizer/main.css
deleted file mode 100644
index 5d9ed53..0000000
--- a/tools/deep_memory_profiler/visualizer/main.css
+++ /dev/null
@@ -1,9 +0,0 @@
-/* Copyright 2013 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. */
-
-.plot-container {
- width: 1240px;
- height: 720px;
- margin: 30px auto 30px auto;
-} \ No newline at end of file
diff --git a/tools/deep_memory_profiler/visualizer/main.js b/tools/deep_memory_profiler/visualizer/main.js
deleted file mode 100644
index 1d96213..0000000
--- a/tools/deep_memory_profiler/visualizer/main.js
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright 2013 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.
-
-/**
- * Generate lines for flot plotting.
- * @param {Array.<Object>} categories
- * @return {Array.<Object>}
- */
-var generateLines = function(categories) {
- var lines = {};
- var snapshotNum = categories.length;
-
- // Initialize lines with all zero.
- categories.forEach(function(categories) {
- Object.keys(categories).forEach(function(breakdownName) {
- if (lines[breakdownName])
- return;
- lines[breakdownName] = [];
- for (var i = 0; i < snapshotNum; ++i)
- lines[breakdownName].push([i, 0]);
- });
- });
-
- // Assignment lines with values of categories.
- categories.forEach(function(categories, index) {
- Object.keys(categories).forEach(function(breakdownName) {
- lines[breakdownName][index] = [index, categories[breakdownName]];
- });
- });
-
- return Object.keys(lines).map(function(breakdownName) {
- return {
- label: breakdownName,
- data: lines[breakdownName]
- };
- });
-};
-
-$(function() {
- // Read original data and plot.
- $.getJSON('data/result.json', function(jsonData) {
- var profiler = new Profiler(jsonData);
- var categories = profiler.getCategories();
- var lines = generateLines(categories);
- var placeholder = '#plot';
-
- // Bind click event so that user can select breakdown by clicking stack
- // area. It firstly checks x range which clicked point is in, and all lines
- // share same x values, so it is checked only once at first. Secondly, it
- // checked y range by accumulated y values because this is a stack graph.
- $(placeholder).bind('plotclick', function(event, pos, item) {
- // If only <=1 line exists or axis area clicked, return.
- var right = binarySearch.call(lines[0].data.map(function(point) {
- return point[0];
- }), pos.x);
- if (lines.length <= 1 || right === lines.length || right === 0)
- return;
-
- // Calculate interpolate y value of every line.
- for (var i = 0; i < lines.length; ++i) {
- var line = lines[i].data;
- // [left, right] is the range including clicked point.
- var left = right - 1;
- var leftPoint = {
- x: line[left][0],
- y: (leftPoint ? leftPoint.y : 0) + line[left][1]
- };
- var rightPoint = {
- x: line[right][0],
- y: (rightPoint ? rightPoint.y : 0) + line[right][1]
- };
-
- // Calculate slope of the linear equation.
- var slope = (rightPoint.y - leftPoint.y) / (rightPoint.x - leftPoint.x);
- var interpolateY = slope * (pos.x - rightPoint.x) + rightPoint.y;
- if (interpolateY >= pos.y)
- break;
- }
-
- // If pos.y is higher than all lines, return.
- if (i === lines.length)
- return;
-
- // TODO(junjianx): temporary log for checking selected object.
- console.log('line ' + i + ' is selected.');
- });
-
- // Plot stack graph.
- $.plot(placeholder, lines, {
- series: {
- stack: true,
- lines: { show: true, fill: true }
- },
- grid: {
- hoverable: true,
- clickable: true
- }
- });
- });
-});
diff --git a/tools/deep_memory_profiler/visualizer/menu-view.js b/tools/deep_memory_profiler/visualizer/menu-view.js
new file mode 100644
index 0000000..1939f50
--- /dev/null
+++ b/tools/deep_memory_profiler/visualizer/menu-view.js
@@ -0,0 +1,75 @@
+// Copyright 2013 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.
+
+/**
+ * This is a view class showing tree-menu.
+ * @param {Object} model Must have addListener method.
+ * @construct
+ */
+var MenuView = function(model) {
+ this.model_ = model;
+ // Update graph view and menu view when model changed.
+ model.addListener('changed', this.redraw.bind(this));
+};
+
+/**
+ * Update menu view when model updated.
+ * @param {Array.<Object>} categories
+ */
+MenuView.prototype.redraw = function(categories) {
+ function generateTree(origin, target) {
+ target.label = origin.name;
+
+ if ('breakdowns' in origin) {
+ target.children = [];
+ origin.breakdowns.forEach(function(breakdown) {
+ var child = {};
+ target.children.push(child);
+ generateTree(breakdown, child);
+ });
+ }
+ }
+
+ function mergeTree(left, right) {
+ if (!('children' in right) && 'children' in left)
+ return;
+ if ('children' in right && !('children' in left))
+ left.children = right.children;
+ if ('children' in right && 'children' in left) {
+ right.children.forEach(function(child) {
+ // Find child with the same label in right tree.
+ var index = left.children.reduce(function(previous, current, index) {
+ if (child.label === current.label)
+ return index;
+ return previous;
+ }, -1);
+ if (index === -1)
+ left.children.push(child);
+ else
+ mergeTree(child, left.children[index]);
+ });
+ }
+ }
+
+ // Merge trees in all snapshots.
+ var union = null;
+ categories.forEach(function(category) {
+ var tree = {};
+ generateTree(category, tree);
+ if (!union)
+ union = tree;
+ else
+ mergeTree(union, tree);
+ });
+
+ var placeholder = '#breakdown-menu';
+ // Draw breakdown menu.
+ $(placeholder).tree({
+ data: [union],
+ autoOpen: true,
+ onCreateLi: function(node, $li) {
+ // TODO(junjianx): Add checkbox to decide the breakdown visibility.
+ }
+ });
+};
diff --git a/tools/deep_memory_profiler/visualizer/profiler-model.js b/tools/deep_memory_profiler/visualizer/profiler-model.js
new file mode 100644
index 0000000..878d0bd
--- /dev/null
+++ b/tools/deep_memory_profiler/visualizer/profiler-model.js
@@ -0,0 +1,170 @@
+// Copyright 2013 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.
+
+/**
+ * This class provides data access interface for dump file profiler.
+ * @constructor
+ */
+var Profiler = function(jsonData) {
+ this.jsonData_ = jsonData;
+ // Initialize template and calculate categories with roots information.
+ // TODO(junjianx): Make file path an argument.
+ this.template_ = jsonData.templates['l2'];
+
+ // Trigger event.
+ this.callbacks_ = {};
+};
+
+/**
+ * Mimic Eventemitter in node. Add new listener for event.
+ * @param {string} event
+ * @param {Function} callback
+ */
+Profiler.prototype.addListener = function(event, callback) {
+ if (!this.callbacks_[event])
+ this.callbacks_[event] = $.Callbacks();
+ this.callbacks_[event].add(callback);
+};
+
+/**
+ * This function will emit the event.
+ * @param {string} event
+ */
+Profiler.prototype.emit = function(event) {
+ // Listeners should be able to receive arbitrary number of parameters.
+ var eventArguments = Array.prototype.slice.call(arguments, 1);
+
+ if (this.callbacks_[event])
+ this.callbacks_[event].fire.apply(this, eventArguments);
+};
+
+/**
+ * Remove listener from event.
+ * @param {string} event
+ * @param {Function} callback
+ */
+Profiler.prototype.removeListener = function(event, callback) {
+ if (this.callbacks_[event])
+ this.callbacks_[event].remove(callback);
+};
+
+/**
+ * Calcualte initial categories according default template.
+ */
+Profiler.prototype.initializeCategories = function() {
+ this.categories_ = this.calculateCategories_();
+ this.emit('changed', this.categories_);
+};
+
+Profiler.prototype.accumulate_ = function(
+ template, snapshot, worldUnits, localUnits, nodePath, nodeName) {
+ var self = this;
+ var totalMemory = 0;
+ var worldName = template[0];
+ var rootBreakdownName = template[1];
+ var breakdowns = snapshot.worlds[worldName].breakdown[rootBreakdownName];
+ // Make deep copy of localUnits.
+ var remainderUnits = localUnits.slice(0);
+ var categories = {
+ name: nodeName || worldName + '-' + rootBreakdownName,
+ nodePath: nodePath.slice(0),
+ breakdowns: []
+ };
+
+ Object.keys(breakdowns).forEach(function(breakdownName) {
+ var breakdown = breakdowns[breakdownName];
+ if (breakdown['hidden'] === true)
+ return;
+
+ // Accumulate breakdowns.
+ var matchedUnits = intersection(breakdown.units, localUnits);
+ var memory = matchedUnits.reduce(function(previous, current) {
+ return previous + worldUnits[worldName][current];
+ }, 0);
+ totalMemory += memory;
+ remainderUnits = difference(remainderUnits, matchedUnits);
+
+ // Handle subs options if exists.
+ if (!(breakdownName in template[2])) {
+ categories.breakdowns.push({
+ name: breakdownName,
+ memory: memory
+ });
+
+ if ('subs' in breakdown && breakdown.subs.length) {
+ var length = categories.breakdowns.length;
+ categories.breakdowns[length-1].subs = breakdown.subs;
+ }
+ } else {
+ var subTemplate = template[2][breakdownName];
+ var subWorldName = subTemplate[0];
+ var subRootBreakdownName = subTemplate[1];
+ var subNodePath = nodePath.slice(0).concat([breakdownName, 2]);
+ var result = null;
+
+ // If subs is in the same world, units should be filtered.
+ if (subWorldName === worldName) {
+ result = self.accumulate_(subTemplate, snapshot, worldUnits,
+ matchedUnits, subNodePath, breakdownName);
+ categories.breakdowns.push(result.categories);
+ if (!result.remainderUnits.length)
+ return;
+
+ var remainMemory =
+ result.remainderUnits.reduce(function(previous, current) {
+ return previous + worldUnits[subWorldName][current];
+ }, 0);
+
+ categories.breakdowns.push({
+ name: breakdownName + '-remaining',
+ memory: remainMemory
+ });
+ } else {
+ var subLocalUnits = Object.keys(worldUnits[subWorldName]);
+ subLocalUnits = subLocalUnits.map(function(unitName) {
+ return parseInt(unitName, 10);
+ });
+
+ result = self.accumulate_(subTemplate, snapshot, worldUnits,
+ subLocalUnits, subNodePath, breakdownName);
+ categories.breakdowns.push(result.categories);
+
+ if (memory > result.totalMemory) {
+ categories.breakdowns.push({
+ name: breakdownName + '-remaining',
+ memory: memory - result.totalMemory
+ });
+ }
+ }
+ }
+ });
+
+ return {
+ categories: categories,
+ totalMemory: totalMemory,
+ remainderUnits: remainderUnits
+ };
+};
+
+Profiler.prototype.calculateCategories_ = function() {
+ var self = this;
+
+ return self.jsonData_.snapshots.map(function(snapshot) {
+ var worldUnits = {};
+ for (var worldName in snapshot.worlds) {
+ worldUnits[worldName] = {};
+ var units = snapshot.worlds[worldName].units;
+ for (var unitName in units)
+ worldUnits[worldName][unitName] = units[unitName][0];
+ }
+ var localUnits = Object.keys(worldUnits[self.template_[0]]);
+ localUnits = localUnits.map(function(unitName) {
+ return parseInt(unitName, 10);
+ });
+
+ var result =
+ self.accumulate_(self.template_, snapshot, worldUnits, localUnits, [2]);
+ return result.categories;
+ });
+}; \ No newline at end of file
diff --git a/tools/deep_memory_profiler/visualizer/profiler.js b/tools/deep_memory_profiler/visualizer/profiler.js
deleted file mode 100644
index 672c8f0..0000000
--- a/tools/deep_memory_profiler/visualizer/profiler.js
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright 2013 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.
-
-/**
- * This class provides data access interface for dump file profiler
- * @constructor
- */
-var Profiler = function(jsonData) {
- this._jsonData = jsonData;
-};
-
-/**
- * Get units of a snapshot in a world.
- * Exception will be thrown when no world of given name exists.
- * @param {string} worldName
- * @param {number} snapshotIndex
- * @return {Object.<string, number>}
- * @private
- */
-Profiler.prototype.getUnits_ = function(worldName, snapshotIndex) {
- var snapshot = this._jsonData.snapshots[snapshotIndex];
- if (!snapshot.worlds[worldName])
- throw 'no world ' + worldName + ' in snapshot ' + index;
-
- // Return units.
- var world = snapshot.worlds[worldName];
- var units = {};
- for (var unitName in world.units)
- units[unitName] = world.units[unitName][0];
- return units;
-};
-
-/**
- * Get first-level breakdowns of a snapshot in a world.
- * Exception will be thrown when no world of given name exists.
- * @param {string} worldName
- * @param {number} snapshotIndex
- * @return {Object.<string, Object>}
- * @private
- */
-Profiler.prototype.getBreakdowns_ = function(worldName, snapshotIndex) {
- var snapshot = this._jsonData.snapshots[snapshotIndex];
- if (!snapshot.worlds[worldName])
- throw 'no world ' + worldName + ' in snapshot ' + index;
-
- // Return breakdowns.
- // TODO(junjianx): handle breakdown with arbitrary-level structure.
- return snapshot.worlds[worldName].breakdown;
-};
-
-/**
- * Get categories from fixed hard-coded worlds and breakdowns temporarily.
- * TODO(junjianx): remove the hard-code and support general cases.
- * @return {Array.<Object>}
- */
-Profiler.prototype.getCategories = function() {
- var categories = [];
- var snapshotNum = this._jsonData.snapshots.length;
-
- for (var snapshotIndex = 0; snapshotIndex < snapshotNum; ++snapshotIndex) {
- // Initial categories object for one snapshot.
- categories.push({});
-
- // Handle breakdowns in malloc world.
- var mallocBreakdown = this.getBreakdowns_('malloc', snapshotIndex);
- var mallocUnits = this.getUnits_('malloc', snapshotIndex);
- if (!mallocBreakdown['component'])
- throw 'no breakdown ' + 'component' + ' in snapshot ' + snapshotIndex;
-
- var componentBreakdown = mallocBreakdown['component'];
- var componentMemory = 0;
- Object.keys(componentBreakdown).forEach(function(breakdownName) {
- var breakdown = componentBreakdown[breakdownName];
- var memory = breakdown.units.reduce(function(previous, current) {
- return previous + mallocUnits[current];
- }, 0);
- componentMemory += memory;
-
- if (componentBreakdown['hidden'] === true)
- return;
- else
- categories[snapshotIndex][breakdownName] = memory;
- });
-
- // Handle breakdowns in vm world.
- var vmBreakdown = this.getBreakdowns_('vm', snapshotIndex);
- var vmUnits = this.getUnits_('vm', snapshotIndex);
- if (!vmBreakdown['map'])
- throw 'no breakdown ' + 'map' + ' in snapshot ' + snapshotIndex;
-
- var mapBreakdown = vmBreakdown['map'];
-
- Object.keys(mapBreakdown).forEach(function(breakdownName) {
- var breakdown = mapBreakdown[breakdownName];
- var memory = breakdown.units.reduce(function(previous, current) {
- return previous + vmUnits[current];
- }, 0);
-
- if (vmBreakdown['hidden'] === true)
- return;
- else if (breakdownName === 'mmap-tcmalloc')
- categories[snapshotIndex]['tc-unused'] = memory - componentMemory;
- else
- categories[snapshotIndex][breakdownName] = memory;
- });
- }
-
- return categories;
-};
diff --git a/tools/deep_memory_profiler/visualizer/third_party/jqTree/LICENSE b/tools/deep_memory_profiler/visualizer/third_party/jqTree/LICENSE
new file mode 100644
index 0000000..d09301f
--- /dev/null
+++ b/tools/deep_memory_profiler/visualizer/third_party/jqTree/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2011 Marco Braak
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/tools/deep_memory_profiler/visualizer/third_party/jqTree/OWNERS b/tools/deep_memory_profiler/visualizer/third_party/jqTree/OWNERS
new file mode 100644
index 0000000..a207fbd
--- /dev/null
+++ b/tools/deep_memory_profiler/visualizer/third_party/jqTree/OWNERS
@@ -0,0 +1,2 @@
+junjianx@chromium.org
+dmikurube@chromium.org
diff --git a/tools/deep_memory_profiler/visualizer/third_party/jqTree/README.chromium b/tools/deep_memory_profiler/visualizer/third_party/jqTree/README.chromium
new file mode 100644
index 0000000..ae316fd
--- /dev/null
+++ b/tools/deep_memory_profiler/visualizer/third_party/jqTree/README.chromium
@@ -0,0 +1,29 @@
+Name: jqTree
+URL: http://mbraak.github.io/jqTree/
+Version: 0.17
+License: Apache 2.0
+License File: LICENSE
+Security Critical: no
+
+Description:
+This is a jquery plugin to draw a tree from a JSON data. This library is
+required only by src/tools/deep_memory_profiler, and it doesn't go into the
+binary.
+
+Local Modifications:
+Removed the following files:
+* bower.json
+* build
+* docs_src/
+* examples/
+* extra/
+* intodex.html
+* jqtree-circle.png
+* jqtree.jquery.jsonon
+* phantomjs/
+* screenshot.png
+* sitemap.txt
+* src/
+* test/
+* tree_logo.png
+* .travis.yml
diff --git a/tools/deep_memory_profiler/visualizer/third_party/jqTree/README.md b/tools/deep_memory_profiler/visualizer/third_party/jqTree/README.md
new file mode 100644
index 0000000..d9ee14d
--- /dev/null
+++ b/tools/deep_memory_profiler/visualizer/third_party/jqTree/README.md
@@ -0,0 +1,21 @@
+[![Travis Status](https://secure.travis-ci.org/mbraak/jqTree.png)](http://travis-ci.org/mbraak/jqTree)
+
+#jqTree
+
+JqTree is a tree widget. Read more in the [documentation](http://mbraak.github.io/jqTree/).
+
+##Features
+
+* Create a tree from JSON data
+* Drag and drop
+* Works on ie7+, firefox 3.6+, chrome and safari
+* Written in Coffeescript
+
+The project is hosted on [github](https://github.com/mbraak/jqTree), has a [test suite](http://mbraak.github.io/jqTree/test/test.html).
+
+See index.html for the full documentation. The documentation is also on [github](http://mbraak.github.io/jqTree/)
+
+##Thanks
+
+The code for the mouse widget is heavily inspired by the mouse widget from jquery ui.
+Tree designed by Hernan D. Schlosman from The Noun Project.
diff --git a/tools/deep_memory_profiler/visualizer/third_party/jqTree/jqtree.css b/tools/deep_memory_profiler/visualizer/third_party/jqTree/jqtree.css
new file mode 100644
index 0000000..5f040ce
--- /dev/null
+++ b/tools/deep_memory_profiler/visualizer/third_party/jqTree/jqtree.css
@@ -0,0 +1,139 @@
+ul.jqtree-tree {
+ margin-left: 12px;
+}
+
+ul.jqtree-tree,
+ul.jqtree-tree ul.jqtree_common {
+ list-style: none outside;
+ margin-bottom: 0;
+ padding: 0;
+}
+
+ul.jqtree-tree ul.jqtree_common {
+ display: block;
+ margin-left: 12px;
+ margin-right: 0;
+}
+ul.jqtree-tree li.jqtree-closed > ul.jqtree_common {
+ display: none;
+}
+
+ul.jqtree-tree li.jqtree_common {
+ clear: both;
+ list-style-type: none;
+}
+ul.jqtree-tree .jqtree-toggler {
+ display: block;
+ position: absolute;
+ left: -1.5em;
+ top: 30%;
+ *top: 0; /* fix for ie7 */
+ font-size: 12px;
+ line-height: 12px;
+ font-family: arial; /* fix for ie9 */
+ border-bottom: none;
+ color: #333;
+}
+
+ul.jqtree-tree .jqtree-toggler:hover {
+ color: #000;
+}
+
+ul.jqtree-tree .jqtree-element {
+ cursor: pointer;
+}
+
+ul.jqtree-tree .jqtree-title {
+ color: #1C4257;
+ vertical-align: middle;
+}
+
+ul.jqtree-tree li.jqtree-folder {
+ margin-bottom: 4px;
+}
+
+ul.jqtree-tree li.jqtree-folder.jqtree-closed {
+ margin-bottom: 1px;
+}
+
+ul.jqtree-tree li.jqtree-folder .jqtree-title {
+ margin-left: 0;
+}
+
+ul.jqtree-tree .jqtree-toggler.jqtree-closed {
+ background-position: 0 0;
+}
+
+span.jqtree-dragging {
+ color: #fff;
+ background: #000;
+ opacity: 0.6;
+ cursor: pointer;
+ padding: 2px 8px;
+}
+
+ul.jqtree-tree li.jqtree-ghost {
+ position: relative;
+ z-index: 10;
+ margin-right: 10px;
+}
+
+ul.jqtree-tree li.jqtree-ghost span {
+ display: block;
+}
+
+ul.jqtree-tree li.jqtree-ghost span.jqtree-circle {
+ background-image: url(jqtree-circle.png);
+ background-repeat: no-repeat;
+ height: 8px;
+ width: 8px;
+ position: absolute;
+ top: -4px;
+ left: 2px;
+}
+
+ul.jqtree-tree li.jqtree-ghost span.jqtree-line {
+ background-color: #0000ff;
+ height: 2px;
+ padding: 0;
+ position: absolute;
+ top: -1px;
+ left: 10px;
+ width: 100%;
+}
+
+ul.jqtree-tree li.jqtree-ghost.jqtree-inside {
+ margin-left: 48px;
+}
+
+ul.jqtree-tree span.jqtree-border {
+ position: absolute;
+ display: block;
+ left: -2px;
+ top: 0;
+ border: solid 2px #0000ff;
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+ margin: 0;
+}
+
+ul.jqtree-tree .jqtree-element {
+ width: 100%; /* todo: why is this in here? */
+ *width: auto; /* ie7 fix; issue 41 */
+ position: relative;
+}
+
+ul.jqtree-tree li.jqtree-selected > .jqtree-element,
+ul.jqtree-tree li.jqtree-selected > .jqtree-element:hover {
+ background-color: #97BDD6;
+ background: -webkit-gradient(linear, left top, left bottom, from(#BEE0F5), to(#89AFCA));
+ background: -moz-linear-gradient(top, #BEE0F5, #89AFCA);
+ background: -ms-linear-gradient(top, #BEE0F5, #89AFCA);
+ background: -o-linear-gradient(top, #BEE0F5, #89AFCA);
+ text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7);
+}
+
+ul.jqtree-tree .jqtree-moving > .jqtree-element .jqtree-title {
+ outline: dashed 1px #0000ff;
+} \ No newline at end of file
diff --git a/tools/deep_memory_profiler/visualizer/third_party/jqTree/tree.jquery.js b/tools/deep_memory_profiler/visualizer/third_party/jqTree/tree.jquery.js
new file mode 100644
index 0000000..2ebff8b
--- /dev/null
+++ b/tools/deep_memory_profiler/visualizer/third_party/jqTree/tree.jquery.js
@@ -0,0 +1,2609 @@
+// Generated by CoffeeScript 1.6.3
+/*
+Copyright 2013 Marco Braak
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+
+(function() {
+ var $, BorderDropHint, DragAndDropHandler, DragElement, FolderElement, GhostDropHint, JqTreeWidget, KeyHandler, MouseWidget, Node, NodeElement, Position, SaveStateHandler, ScrollHandler, SelectNodeHandler, SimpleWidget, html_escape, indexOf, json_escapable, json_meta, json_quote, json_str, _indexOf, _ref, _ref1, _ref2,
+ __slice = [].slice,
+ __hasProp = {}.hasOwnProperty,
+ __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
+
+ $ = this.jQuery;
+
+ SimpleWidget = (function() {
+ SimpleWidget.prototype.defaults = {};
+
+ function SimpleWidget(el, options) {
+ this.$el = $(el);
+ this.options = $.extend({}, this.defaults, options);
+ }
+
+ SimpleWidget.prototype.destroy = function() {
+ return this._deinit();
+ };
+
+ SimpleWidget.prototype._init = function() {
+ return null;
+ };
+
+ SimpleWidget.prototype._deinit = function() {
+ return null;
+ };
+
+ SimpleWidget.register = function(widget_class, widget_name) {
+ var callFunction, createWidget, destroyWidget, getDataKey;
+ getDataKey = function() {
+ return "simple_widget_" + widget_name;
+ };
+ createWidget = function($el, options) {
+ var data_key, el, widget, _i, _len;
+ data_key = getDataKey();
+ for (_i = 0, _len = $el.length; _i < _len; _i++) {
+ el = $el[_i];
+ widget = new widget_class(el, options);
+ if (!$.data(el, data_key)) {
+ $.data(el, data_key, widget);
+ }
+ widget._init();
+ }
+ return $el;
+ };
+ destroyWidget = function($el) {
+ var data_key, el, widget, _i, _len, _results;
+ data_key = getDataKey();
+ _results = [];
+ for (_i = 0, _len = $el.length; _i < _len; _i++) {
+ el = $el[_i];
+ widget = $.data(el, data_key);
+ if (widget && (widget instanceof SimpleWidget)) {
+ widget.destroy();
+ }
+ _results.push($.removeData(el, data_key));
+ }
+ return _results;
+ };
+ callFunction = function($el, function_name, args) {
+ var el, result, widget, widget_function, _i, _len;
+ result = null;
+ for (_i = 0, _len = $el.length; _i < _len; _i++) {
+ el = $el[_i];
+ widget = $.data(el, getDataKey());
+ if (widget && (widget instanceof SimpleWidget)) {
+ widget_function = widget[function_name];
+ if (widget_function && (typeof widget_function === 'function')) {
+ result = widget_function.apply(widget, args);
+ }
+ }
+ }
+ return result;
+ };
+ return $.fn[widget_name] = function() {
+ var $el, args, argument1, function_name, options;
+ argument1 = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
+ $el = this;
+ if (argument1 === void 0 || typeof argument1 === 'object') {
+ options = argument1;
+ return createWidget($el, options);
+ } else if (typeof argument1 === 'string' && argument1[0] !== '_') {
+ function_name = argument1;
+ if (function_name === 'destroy') {
+ return destroyWidget($el);
+ } else {
+ return callFunction($el, function_name, args);
+ }
+ }
+ };
+ };
+
+ return SimpleWidget;
+
+ })();
+
+ this.SimpleWidget = SimpleWidget;
+
+ /*
+ This widget does the same a the mouse widget in jqueryui.
+ */
+
+
+ MouseWidget = (function(_super) {
+ __extends(MouseWidget, _super);
+
+ function MouseWidget() {
+ _ref = MouseWidget.__super__.constructor.apply(this, arguments);
+ return _ref;
+ }
+
+ MouseWidget.is_mouse_handled = false;
+
+ MouseWidget.prototype._init = function() {
+ this.$el.bind('mousedown.mousewidget', $.proxy(this._mouseDown, this));
+ this.$el.bind('touchstart.mousewidget', $.proxy(this._touchStart, this));
+ this.is_mouse_started = false;
+ this.mouse_delay = 0;
+ this._mouse_delay_timer = null;
+ this._is_mouse_delay_met = true;
+ return this.mouse_down_info = null;
+ };
+
+ MouseWidget.prototype._deinit = function() {
+ var $document;
+ this.$el.unbind('mousedown.mousewidget');
+ this.$el.unbind('touchstart.mousewidget');
+ $document = $(document);
+ $document.unbind('mousemove.mousewidget');
+ return $document.unbind('mouseup.mousewidget');
+ };
+
+ MouseWidget.prototype._mouseDown = function(e) {
+ var result;
+ if (e.which !== 1) {
+ return;
+ }
+ result = this._handleMouseDown(e, this._getPositionInfo(e));
+ if (result) {
+ e.preventDefault();
+ }
+ return result;
+ };
+
+ MouseWidget.prototype._handleMouseDown = function(e, position_info) {
+ if (MouseWidget.is_mouse_handled) {
+ return;
+ }
+ if (this.is_mouse_started) {
+ this._handleMouseUp(position_info);
+ }
+ this.mouse_down_info = position_info;
+ if (!this._mouseCapture(position_info)) {
+ return;
+ }
+ this._handleStartMouse();
+ this.is_mouse_handled = true;
+ return true;
+ };
+
+ MouseWidget.prototype._handleStartMouse = function() {
+ var $document;
+ $document = $(document);
+ $document.bind('mousemove.mousewidget', $.proxy(this._mouseMove, this));
+ $document.bind('touchmove.mousewidget', $.proxy(this._touchMove, this));
+ $document.bind('mouseup.mousewidget', $.proxy(this._mouseUp, this));
+ $document.bind('touchend.mousewidget', $.proxy(this._touchEnd, this));
+ if (this.mouse_delay) {
+ return this._startMouseDelayTimer();
+ }
+ };
+
+ MouseWidget.prototype._startMouseDelayTimer = function() {
+ var _this = this;
+ if (this._mouse_delay_timer) {
+ clearTimeout(this._mouse_delay_timer);
+ }
+ this._mouse_delay_timer = setTimeout(function() {
+ return _this._is_mouse_delay_met = true;
+ }, this.mouse_delay);
+ return this._is_mouse_delay_met = false;
+ };
+
+ MouseWidget.prototype._mouseMove = function(e) {
+ return this._handleMouseMove(e, this._getPositionInfo(e));
+ };
+
+ MouseWidget.prototype._handleMouseMove = function(e, position_info) {
+ if (this.is_mouse_started) {
+ this._mouseDrag(position_info);
+ return e.preventDefault();
+ }
+ if (this.mouse_delay && !this._is_mouse_delay_met) {
+ return true;
+ }
+ this.is_mouse_started = this._mouseStart(this.mouse_down_info) !== false;
+ if (this.is_mouse_started) {
+ this._mouseDrag(position_info);
+ } else {
+ this._handleMouseUp(position_info);
+ }
+ return !this.is_mouse_started;
+ };
+
+ MouseWidget.prototype._getPositionInfo = function(e) {
+ return {
+ page_x: e.pageX,
+ page_y: e.pageY,
+ target: e.target,
+ original_event: e
+ };
+ };
+
+ MouseWidget.prototype._mouseUp = function(e) {
+ return this._handleMouseUp(this._getPositionInfo(e));
+ };
+
+ MouseWidget.prototype._handleMouseUp = function(position_info) {
+ var $document;
+ $document = $(document);
+ $document.unbind('mousemove.mousewidget');
+ $document.unbind('touchmove.mousewidget');
+ $document.unbind('mouseup.mousewidget');
+ $document.unbind('touchend.mousewidget');
+ if (this.is_mouse_started) {
+ this.is_mouse_started = false;
+ this._mouseStop(position_info);
+ }
+ };
+
+ MouseWidget.prototype._mouseCapture = function(position_info) {
+ return true;
+ };
+
+ MouseWidget.prototype._mouseStart = function(position_info) {
+ return null;
+ };
+
+ MouseWidget.prototype._mouseDrag = function(position_info) {
+ return null;
+ };
+
+ MouseWidget.prototype._mouseStop = function(position_info) {
+ return null;
+ };
+
+ MouseWidget.prototype.setMouseDelay = function(mouse_delay) {
+ return this.mouse_delay = mouse_delay;
+ };
+
+ MouseWidget.prototype._touchStart = function(e) {
+ var touch;
+ if (e.originalEvent.touches.length > 1) {
+ return;
+ }
+ touch = e.originalEvent.changedTouches[0];
+ return this._handleMouseDown(e, this._getPositionInfo(touch));
+ };
+
+ MouseWidget.prototype._touchMove = function(e) {
+ var touch;
+ if (e.originalEvent.touches.length > 1) {
+ return;
+ }
+ touch = e.originalEvent.changedTouches[0];
+ return this._handleMouseMove(e, this._getPositionInfo(touch));
+ };
+
+ MouseWidget.prototype._touchEnd = function(e) {
+ var touch;
+ if (e.originalEvent.touches.length > 1) {
+ return;
+ }
+ touch = e.originalEvent.changedTouches[0];
+ return this._handleMouseUp(this._getPositionInfo(touch));
+ };
+
+ return MouseWidget;
+
+ })(SimpleWidget);
+
+ this.Tree = {};
+
+ $ = this.jQuery;
+
+ Position = {
+ getName: function(position) {
+ return Position.strings[position - 1];
+ },
+ nameToIndex: function(name) {
+ var i, _i, _ref1;
+ for (i = _i = 1, _ref1 = Position.strings.length; 1 <= _ref1 ? _i <= _ref1 : _i >= _ref1; i = 1 <= _ref1 ? ++_i : --_i) {
+ if (Position.strings[i - 1] === name) {
+ return i;
+ }
+ }
+ return 0;
+ }
+ };
+
+ Position.BEFORE = 1;
+
+ Position.AFTER = 2;
+
+ Position.INSIDE = 3;
+
+ Position.NONE = 4;
+
+ Position.strings = ['before', 'after', 'inside', 'none'];
+
+ this.Tree.Position = Position;
+
+ Node = (function() {
+ function Node(o, is_root, node_class) {
+ if (is_root == null) {
+ is_root = false;
+ }
+ if (node_class == null) {
+ node_class = Node;
+ }
+ this.setData(o);
+ this.children = [];
+ this.parent = null;
+ if (is_root) {
+ this.id_mapping = {};
+ this.tree = this;
+ this.node_class = node_class;
+ }
+ }
+
+ Node.prototype.setData = function(o) {
+ var key, value, _results;
+ if (typeof o !== 'object') {
+ return this.name = o;
+ } else {
+ _results = [];
+ for (key in o) {
+ value = o[key];
+ if (key === 'label') {
+ _results.push(this.name = value);
+ } else {
+ _results.push(this[key] = value);
+ }
+ }
+ return _results;
+ }
+ };
+
+ Node.prototype.initFromData = function(data) {
+ var addChildren, addNode,
+ _this = this;
+ addNode = function(node_data) {
+ _this.setData(node_data);
+ if (node_data.children) {
+ return addChildren(node_data.children);
+ }
+ };
+ addChildren = function(children_data) {
+ var child, node, _i, _len;
+ for (_i = 0, _len = children_data.length; _i < _len; _i++) {
+ child = children_data[_i];
+ node = new _this.tree.node_class('');
+ node.initFromData(child);
+ _this.addChild(node);
+ }
+ return null;
+ };
+ addNode(data);
+ return null;
+ };
+
+ /*
+ Create tree from data.
+
+ Structure of data is:
+ [
+ {
+ label: 'node1',
+ children: [
+ { label: 'child1' },
+ { label: 'child2' }
+ ]
+ },
+ {
+ label: 'node2'
+ }
+ ]
+ */
+
+
+ Node.prototype.loadFromData = function(data) {
+ var node, o, _i, _len;
+ this.removeChildren();
+ for (_i = 0, _len = data.length; _i < _len; _i++) {
+ o = data[_i];
+ node = new this.tree.node_class(o);
+ this.addChild(node);
+ if (typeof o === 'object' && o.children) {
+ node.loadFromData(o.children);
+ }
+ }
+ return null;
+ };
+
+ /*
+ Add child.
+
+ tree.addChild(
+ new Node('child1')
+ );
+ */
+
+
+ Node.prototype.addChild = function(node) {
+ this.children.push(node);
+ return node._setParent(this);
+ };
+
+ /*
+ Add child at position. Index starts at 0.
+
+ tree.addChildAtPosition(
+ new Node('abc'),
+ 1
+ );
+ */
+
+
+ Node.prototype.addChildAtPosition = function(node, index) {
+ this.children.splice(index, 0, node);
+ return node._setParent(this);
+ };
+
+ Node.prototype._setParent = function(parent) {
+ this.parent = parent;
+ this.tree = parent.tree;
+ return this.tree.addNodeToIndex(this);
+ };
+
+ /*
+ Remove child. This also removes the children of the node.
+
+ tree.removeChild(tree.children[0]);
+ */
+
+
+ Node.prototype.removeChild = function(node) {
+ node.removeChildren();
+ return this._removeChild(node);
+ };
+
+ Node.prototype._removeChild = function(node) {
+ this.children.splice(this.getChildIndex(node), 1);
+ return this.tree.removeNodeFromIndex(node);
+ };
+
+ /*
+ Get child index.
+
+ var index = getChildIndex(node);
+ */
+
+
+ Node.prototype.getChildIndex = function(node) {
+ return $.inArray(node, this.children);
+ };
+
+ /*
+ Does the tree have children?
+
+ if (tree.hasChildren()) {
+ //
+ }
+ */
+
+
+ Node.prototype.hasChildren = function() {
+ return this.children.length !== 0;
+ };
+
+ Node.prototype.isFolder = function() {
+ return this.hasChildren() || this.load_on_demand;
+ };
+
+ /*
+ Iterate over all the nodes in the tree.
+
+ Calls callback with (node, level).
+
+ The callback must return true to continue the iteration on current node.
+
+ tree.iterate(
+ function(node, level) {
+ console.log(node.name);
+
+ // stop iteration after level 2
+ return (level <= 2);
+ }
+ );
+ */
+
+
+ Node.prototype.iterate = function(callback) {
+ var _iterate,
+ _this = this;
+ _iterate = function(node, level) {
+ var child, result, _i, _len, _ref1;
+ if (node.children) {
+ _ref1 = node.children;
+ for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
+ child = _ref1[_i];
+ result = callback(child, level);
+ if (_this.hasChildren() && result) {
+ _iterate(child, level + 1);
+ }
+ }
+ return null;
+ }
+ };
+ _iterate(this, 0);
+ return null;
+ };
+
+ /*
+ Move node relative to another node.
+
+ Argument position: Position.BEFORE, Position.AFTER or Position.Inside
+
+ // move node1 after node2
+ tree.moveNode(node1, node2, Position.AFTER);
+ */
+
+
+ Node.prototype.moveNode = function(moved_node, target_node, position) {
+ if (moved_node.isParentOf(target_node)) {
+ return;
+ }
+ moved_node.parent._removeChild(moved_node);
+ if (position === Position.AFTER) {
+ return target_node.parent.addChildAtPosition(moved_node, target_node.parent.getChildIndex(target_node) + 1);
+ } else if (position === Position.BEFORE) {
+ return target_node.parent.addChildAtPosition(moved_node, target_node.parent.getChildIndex(target_node));
+ } else if (position === Position.INSIDE) {
+ return target_node.addChildAtPosition(moved_node, 0);
+ }
+ };
+
+ /*
+ Get the tree as data.
+ */
+
+
+ Node.prototype.getData = function() {
+ var getDataFromNodes,
+ _this = this;
+ getDataFromNodes = function(nodes) {
+ var data, k, node, tmp_node, v, _i, _len;
+ data = [];
+ for (_i = 0, _len = nodes.length; _i < _len; _i++) {
+ node = nodes[_i];
+ tmp_node = {};
+ for (k in node) {
+ v = node[k];
+ if ((k !== 'parent' && k !== 'children' && k !== 'element' && k !== 'tree') && Object.prototype.hasOwnProperty.call(node, k)) {
+ tmp_node[k] = v;
+ }
+ }
+ if (node.hasChildren()) {
+ tmp_node.children = getDataFromNodes(node.children);
+ }
+ data.push(tmp_node);
+ }
+ return data;
+ };
+ return getDataFromNodes(this.children);
+ };
+
+ Node.prototype.getNodeByName = function(name) {
+ var result;
+ result = null;
+ this.iterate(function(node) {
+ if (node.name === name) {
+ result = node;
+ return false;
+ } else {
+ return true;
+ }
+ });
+ return result;
+ };
+
+ Node.prototype.addAfter = function(node_info) {
+ var child_index, node;
+ if (!this.parent) {
+ return null;
+ } else {
+ node = new this.tree.node_class(node_info);
+ child_index = this.parent.getChildIndex(this);
+ this.parent.addChildAtPosition(node, child_index + 1);
+ return node;
+ }
+ };
+
+ Node.prototype.addBefore = function(node_info) {
+ var child_index, node;
+ if (!this.parent) {
+ return null;
+ } else {
+ node = new this.tree.node_class(node_info);
+ child_index = this.parent.getChildIndex(this);
+ this.parent.addChildAtPosition(node, child_index);
+ return node;
+ }
+ };
+
+ Node.prototype.addParent = function(node_info) {
+ var child, new_parent, original_parent, _i, _len, _ref1;
+ if (!this.parent) {
+ return null;
+ } else {
+ new_parent = new this.tree.node_class(node_info);
+ new_parent._setParent(this.tree);
+ original_parent = this.parent;
+ _ref1 = original_parent.children;
+ for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
+ child = _ref1[_i];
+ new_parent.addChild(child);
+ }
+ original_parent.children = [];
+ original_parent.addChild(new_parent);
+ return new_parent;
+ }
+ };
+
+ Node.prototype.remove = function() {
+ if (this.parent) {
+ this.parent.removeChild(this);
+ return this.parent = null;
+ }
+ };
+
+ Node.prototype.append = function(node_info) {
+ var node;
+ node = new this.tree.node_class(node_info);
+ this.addChild(node);
+ return node;
+ };
+
+ Node.prototype.prepend = function(node_info) {
+ var node;
+ node = new this.tree.node_class(node_info);
+ this.addChildAtPosition(node, 0);
+ return node;
+ };
+
+ Node.prototype.isParentOf = function(node) {
+ var parent;
+ parent = node.parent;
+ while (parent) {
+ if (parent === this) {
+ return true;
+ }
+ parent = parent.parent;
+ }
+ return false;
+ };
+
+ Node.prototype.getLevel = function() {
+ var level, node;
+ level = 0;
+ node = this;
+ while (node.parent) {
+ level += 1;
+ node = node.parent;
+ }
+ return level;
+ };
+
+ Node.prototype.getNodeById = function(node_id) {
+ return this.id_mapping[node_id];
+ };
+
+ Node.prototype.addNodeToIndex = function(node) {
+ if (node.id) {
+ return this.id_mapping[node.id] = node;
+ }
+ };
+
+ Node.prototype.removeNodeFromIndex = function(node) {
+ if (node.id) {
+ return delete this.id_mapping[node.id];
+ }
+ };
+
+ Node.prototype.removeChildren = function() {
+ var _this = this;
+ this.iterate(function(child) {
+ _this.tree.removeNodeFromIndex(child);
+ return true;
+ });
+ return this.children = [];
+ };
+
+ Node.prototype.getPreviousSibling = function() {
+ var previous_index;
+ if (!this.parent) {
+ return null;
+ } else {
+ previous_index = this.parent.getChildIndex(this) - 1;
+ if (previous_index >= 0) {
+ return this.parent.children[previous_index];
+ } else {
+ return null;
+ }
+ }
+ };
+
+ Node.prototype.getNextSibling = function() {
+ var next_index;
+ if (!this.parent) {
+ return null;
+ } else {
+ next_index = this.parent.getChildIndex(this) + 1;
+ if (next_index < this.parent.children.length) {
+ return this.parent.children[next_index];
+ } else {
+ return null;
+ }
+ }
+ };
+
+ return Node;
+
+ })();
+
+ this.Tree.Node = Node;
+
+ /*
+ Copyright 2013 Marco Braak
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+
+ JqTreeWidget = (function(_super) {
+ __extends(JqTreeWidget, _super);
+
+ function JqTreeWidget() {
+ _ref1 = JqTreeWidget.__super__.constructor.apply(this, arguments);
+ return _ref1;
+ }
+
+ JqTreeWidget.prototype.defaults = {
+ autoOpen: false,
+ saveState: false,
+ dragAndDrop: false,
+ selectable: true,
+ useContextMenu: true,
+ onCanSelectNode: null,
+ onSetStateFromStorage: null,
+ onGetStateFromStorage: null,
+ onCreateLi: null,
+ onIsMoveHandle: null,
+ onCanMove: null,
+ onCanMoveTo: null,
+ onLoadFailed: null,
+ autoEscape: true,
+ dataUrl: null,
+ closedIcon: '&#x25ba;',
+ openedIcon: '&#x25bc;',
+ slide: true,
+ nodeClass: Node
+ };
+
+ JqTreeWidget.prototype.toggle = function(node, slide) {
+ if (slide == null) {
+ slide = true;
+ }
+ if (node.is_open) {
+ return this.closeNode(node, slide);
+ } else {
+ return this.openNode(node, slide);
+ }
+ };
+
+ JqTreeWidget.prototype.getTree = function() {
+ return this.tree;
+ };
+
+ JqTreeWidget.prototype.selectNode = function(node) {
+ return this._selectNode(node, true);
+ };
+
+ JqTreeWidget.prototype._selectNode = function(node, must_toggle) {
+ var canSelect, openParents, saveState,
+ _this = this;
+ if (must_toggle == null) {
+ must_toggle = false;
+ }
+ if (!this.select_node_handler) {
+ return;
+ }
+ canSelect = function() {
+ if (_this.options.onCanSelectNode) {
+ return _this.options.selectable && _this.options.onCanSelectNode(node);
+ } else {
+ return _this.options.selectable;
+ }
+ };
+ openParents = function() {
+ var parent;
+ parent = node.parent;
+ if (parent && parent.parent && !parent.is_open) {
+ return _this.openNode(parent, false);
+ }
+ };
+ saveState = function() {
+ if (_this.options.saveState) {
+ return _this.save_state_handler.saveState();
+ }
+ };
+ if (!node) {
+ this._deselectCurrentNode();
+ saveState();
+ return;
+ }
+ if (!canSelect()) {
+ return;
+ }
+ if (this.select_node_handler.isNodeSelected(node)) {
+ if (must_toggle) {
+ this._deselectCurrentNode();
+ this._triggerEvent('tree.select', {
+ node: null,
+ previous_node: node
+ });
+ }
+ } else {
+ this._deselectCurrentNode();
+ this.addToSelection(node);
+ this._triggerEvent('tree.select', {
+ node: node
+ });
+ openParents();
+ }
+ return saveState();
+ };
+
+ JqTreeWidget.prototype.getSelectedNode = function() {
+ return this.select_node_handler.getSelectedNode();
+ };
+
+ JqTreeWidget.prototype.toJson = function() {
+ return JSON.stringify(this.tree.getData());
+ };
+
+ JqTreeWidget.prototype.loadData = function(data, parent_node) {
+ return this._loadData(data, parent_node);
+ };
+
+ JqTreeWidget.prototype.loadDataFromUrl = function(url, parent_node, on_finished) {
+ if ($.type(url) !== 'string') {
+ on_finished = parent_node;
+ parent_node = url;
+ url = null;
+ }
+ return this._loadDataFromUrl(url, parent_node, on_finished);
+ };
+
+ JqTreeWidget.prototype._loadDataFromUrl = function(url_info, parent_node, on_finished) {
+ var $el, addLoadingClass, parseUrlInfo, removeLoadingClass,
+ _this = this;
+ $el = null;
+ addLoadingClass = function() {
+ var folder_element;
+ if (!parent_node) {
+ $el = _this.element;
+ } else {
+ folder_element = new FolderElement(parent_node, _this);
+ $el = folder_element.getLi();
+ }
+ return $el.addClass('jqtree-loading');
+ };
+ removeLoadingClass = function() {
+ if ($el) {
+ return $el.removeClass('jqtree-loading');
+ }
+ };
+ parseUrlInfo = function() {
+ if ($.type(url_info) === 'string') {
+ url_info = {
+ url: url_info
+ };
+ }
+ if (!url_info.method) {
+ return url_info.method = 'get';
+ }
+ };
+ addLoadingClass();
+ if (!url_info) {
+ url_info = this._getDataUrlInfo(parent_node);
+ }
+ parseUrlInfo();
+ return $.ajax({
+ url: url_info.url,
+ data: url_info.data,
+ type: url_info.method.toUpperCase(),
+ cache: false,
+ dataType: 'json',
+ success: function(response) {
+ var data;
+ if ($.isArray(response) || typeof response === 'object') {
+ data = response;
+ } else {
+ data = $.parseJSON(response);
+ }
+ removeLoadingClass();
+ _this._loadData(data, parent_node);
+ if (on_finished && $.isFunction(on_finished)) {
+ return on_finished();
+ }
+ },
+ error: function(response) {
+ removeLoadingClass();
+ if (_this.options.onLoadFailed) {
+ return _this.options.onLoadFailed(response);
+ }
+ }
+ });
+ };
+
+ JqTreeWidget.prototype._loadData = function(data, parent_node) {
+ var n, selected_nodes_under_parent, _i, _len;
+ this._triggerEvent('tree.load_data', {
+ tree_data: data
+ });
+ if (!parent_node) {
+ this._initTree(data);
+ } else {
+ selected_nodes_under_parent = this.select_node_handler.getSelectedNodes(parent_node);
+ for (_i = 0, _len = selected_nodes_under_parent.length; _i < _len; _i++) {
+ n = selected_nodes_under_parent[_i];
+ this.select_node_handler.removeFromSelection(n);
+ }
+ parent_node.loadFromData(data);
+ parent_node.load_on_demand = false;
+ this._refreshElements(parent_node.parent);
+ }
+ if (this.is_dragging) {
+ return this.dnd_handler.refreshHitAreas();
+ }
+ };
+
+ JqTreeWidget.prototype.getNodeById = function(node_id) {
+ return this.tree.getNodeById(node_id);
+ };
+
+ JqTreeWidget.prototype.getNodeByName = function(name) {
+ return this.tree.getNodeByName(name);
+ };
+
+ JqTreeWidget.prototype.openNode = function(node, slide) {
+ if (slide == null) {
+ slide = true;
+ }
+ return this._openNode(node, slide);
+ };
+
+ JqTreeWidget.prototype._openNode = function(node, slide, on_finished) {
+ var doOpenNode, parent,
+ _this = this;
+ if (slide == null) {
+ slide = true;
+ }
+ doOpenNode = function(_node, _slide, _on_finished) {
+ var folder_element;
+ folder_element = new FolderElement(_node, _this);
+ return folder_element.open(_on_finished, _slide);
+ };
+ if (node.isFolder()) {
+ if (node.load_on_demand) {
+ return this._loadFolderOnDemand(node, slide, on_finished);
+ } else {
+ parent = node.parent;
+ while (parent && !parent.is_open) {
+ if (parent.parent) {
+ doOpenNode(parent, false, null);
+ }
+ parent = parent.parent;
+ }
+ doOpenNode(node, slide, on_finished);
+ return this._saveState();
+ }
+ }
+ };
+
+ JqTreeWidget.prototype._loadFolderOnDemand = function(node, slide, on_finished) {
+ var _this = this;
+ if (slide == null) {
+ slide = true;
+ }
+ return this._loadDataFromUrl(null, node, function() {
+ return _this._openNode(node, slide, on_finished);
+ });
+ };
+
+ JqTreeWidget.prototype.closeNode = function(node, slide) {
+ if (slide == null) {
+ slide = true;
+ }
+ if (node.isFolder()) {
+ new FolderElement(node, this).close(slide);
+ return this._saveState();
+ }
+ };
+
+ JqTreeWidget.prototype.isDragging = function() {
+ return this.is_dragging;
+ };
+
+ JqTreeWidget.prototype.refreshHitAreas = function() {
+ return this.dnd_handler.refreshHitAreas();
+ };
+
+ JqTreeWidget.prototype.addNodeAfter = function(new_node_info, existing_node) {
+ var new_node;
+ new_node = existing_node.addAfter(new_node_info);
+ this._refreshElements(existing_node.parent);
+ return new_node;
+ };
+
+ JqTreeWidget.prototype.addNodeBefore = function(new_node_info, existing_node) {
+ var new_node;
+ new_node = existing_node.addBefore(new_node_info);
+ this._refreshElements(existing_node.parent);
+ return new_node;
+ };
+
+ JqTreeWidget.prototype.addParentNode = function(new_node_info, existing_node) {
+ var new_node;
+ new_node = existing_node.addParent(new_node_info);
+ this._refreshElements(new_node.parent);
+ return new_node;
+ };
+
+ JqTreeWidget.prototype.removeNode = function(node) {
+ var parent;
+ parent = node.parent;
+ if (parent) {
+ this.select_node_handler.removeFromSelection(node, true);
+ node.remove();
+ return this._refreshElements(parent.parent);
+ }
+ };
+
+ JqTreeWidget.prototype.appendNode = function(new_node_info, parent_node) {
+ var is_already_root_node, node;
+ if (!parent_node) {
+ parent_node = this.tree;
+ }
+ is_already_root_node = parent_node.isFolder();
+ node = parent_node.append(new_node_info);
+ if (is_already_root_node) {
+ this._refreshElements(parent_node);
+ } else {
+ this._refreshElements(parent_node.parent);
+ }
+ return node;
+ };
+
+ JqTreeWidget.prototype.prependNode = function(new_node_info, parent_node) {
+ var node;
+ if (!parent_node) {
+ parent_node = this.tree;
+ }
+ node = parent_node.prepend(new_node_info);
+ this._refreshElements(parent_node);
+ return node;
+ };
+
+ JqTreeWidget.prototype.updateNode = function(node, data) {
+ var id_is_changed;
+ id_is_changed = data.id && data.id !== node.id;
+ if (id_is_changed) {
+ this.tree.removeNodeFromIndex(node);
+ }
+ node.setData(data);
+ if (id_is_changed) {
+ this.tree.addNodeToIndex(node);
+ }
+ this._refreshElements(node.parent);
+ return this._selectCurrentNode();
+ };
+
+ JqTreeWidget.prototype.moveNode = function(node, target_node, position) {
+ var position_index;
+ position_index = Position.nameToIndex(position);
+ this.tree.moveNode(node, target_node, position_index);
+ return this._refreshElements();
+ };
+
+ JqTreeWidget.prototype.getStateFromStorage = function() {
+ return this.save_state_handler.getStateFromStorage();
+ };
+
+ JqTreeWidget.prototype.addToSelection = function(node) {
+ this.select_node_handler.addToSelection(node);
+ return this._getNodeElementForNode(node).select();
+ };
+
+ JqTreeWidget.prototype.getSelectedNodes = function() {
+ return this.select_node_handler.getSelectedNodes();
+ };
+
+ JqTreeWidget.prototype.isNodeSelected = function(node) {
+ return this.select_node_handler.isNodeSelected(node);
+ };
+
+ JqTreeWidget.prototype.removeFromSelection = function(node) {
+ this.select_node_handler.removeFromSelection(node);
+ return this._getNodeElementForNode(node).deselect();
+ };
+
+ JqTreeWidget.prototype.scrollToNode = function(node) {
+ var $element, top;
+ $element = $(node.element);
+ top = $element.offset().top - this.$el.offset().top;
+ return this.scroll_handler.scrollTo(top);
+ };
+
+ JqTreeWidget.prototype.getState = function() {
+ return this.save_state_handler.getState();
+ };
+
+ JqTreeWidget.prototype.setState = function(state) {
+ this.save_state_handler.setState(state);
+ return this._refreshElements();
+ };
+
+ JqTreeWidget.prototype._init = function() {
+ JqTreeWidget.__super__._init.call(this);
+ this.element = this.$el;
+ this.mouse_delay = 300;
+ this.is_initialized = false;
+ if (typeof SaveStateHandler !== "undefined" && SaveStateHandler !== null) {
+ this.save_state_handler = new SaveStateHandler(this);
+ } else {
+ this.options.saveState = false;
+ }
+ if (typeof SelectNodeHandler !== "undefined" && SelectNodeHandler !== null) {
+ this.select_node_handler = new SelectNodeHandler(this);
+ }
+ if (typeof DragAndDropHandler !== "undefined" && DragAndDropHandler !== null) {
+ this.dnd_handler = new DragAndDropHandler(this);
+ } else {
+ this.options.dragAndDrop = false;
+ }
+ if (typeof ScrollHandler !== "undefined" && ScrollHandler !== null) {
+ this.scroll_handler = new ScrollHandler(this);
+ }
+ if ((typeof KeyHandler !== "undefined" && KeyHandler !== null) && (typeof SelectNodeHandler !== "undefined" && SelectNodeHandler !== null)) {
+ this.key_handler = new KeyHandler(this);
+ }
+ this._initData();
+ this.element.click($.proxy(this._click, this));
+ if (this.options.useContextMenu) {
+ return this.element.bind('contextmenu', $.proxy(this._contextmenu, this));
+ }
+ };
+
+ JqTreeWidget.prototype._deinit = function() {
+ this.element.empty();
+ this.element.unbind();
+ this.key_handler.deinit();
+ this.tree = null;
+ return JqTreeWidget.__super__._deinit.call(this);
+ };
+
+ JqTreeWidget.prototype._initData = function() {
+ if (this.options.data) {
+ return this._loadData(this.options.data);
+ } else {
+ return this._loadDataFromUrl(this._getDataUrlInfo());
+ }
+ };
+
+ JqTreeWidget.prototype._getDataUrlInfo = function(node) {
+ var data, data_url, url_info;
+ data_url = this.options.dataUrl || this.element.data('url');
+ if ($.isFunction(data_url)) {
+ return data_url(node);
+ } else if ($.type(data_url) === 'string') {
+ url_info = {
+ url: data_url
+ };
+ if (node && node.id) {
+ data = {
+ node: node.id
+ };
+ url_info['data'] = data;
+ }
+ return url_info;
+ } else {
+ return data_url;
+ }
+ };
+
+ JqTreeWidget.prototype._initTree = function(data) {
+ this.tree = new this.options.nodeClass(null, true, this.options.nodeClass);
+ if (this.select_node_handler) {
+ this.select_node_handler.clear();
+ }
+ this.tree.loadFromData(data);
+ this._openNodes();
+ this._refreshElements();
+ if (!this.is_initialized) {
+ this.is_initialized = true;
+ return this._triggerEvent('tree.init');
+ }
+ };
+
+ JqTreeWidget.prototype._openNodes = function() {
+ var max_level;
+ if (this.options.saveState) {
+ if (this.save_state_handler.restoreState()) {
+ return;
+ }
+ }
+ if (this.options.autoOpen === false) {
+ return;
+ } else if (this.options.autoOpen === true) {
+ max_level = -1;
+ } else {
+ max_level = parseInt(this.options.autoOpen);
+ }
+ return this.tree.iterate(function(node, level) {
+ if (node.hasChildren()) {
+ node.is_open = true;
+ }
+ return level !== max_level;
+ });
+ };
+
+ JqTreeWidget.prototype._refreshElements = function(from_node) {
+ var $element, createFolderLi, createLi, createNodeLi, createUl, doCreateDomElements, escapeIfNecessary, is_root_node, node_element,
+ _this = this;
+ if (from_node == null) {
+ from_node = null;
+ }
+ escapeIfNecessary = function(value) {
+ if (_this.options.autoEscape) {
+ return html_escape(value);
+ } else {
+ return value;
+ }
+ };
+ createUl = function(is_root_node) {
+ var class_string;
+ if (is_root_node) {
+ class_string = 'jqtree-tree';
+ } else {
+ class_string = '';
+ }
+ return $("<ul class=\"jqtree_common " + class_string + "\"></ul>");
+ };
+ createLi = function(node) {
+ var $li;
+ if (node.isFolder()) {
+ $li = createFolderLi(node);
+ } else {
+ $li = createNodeLi(node);
+ }
+ if (_this.options.onCreateLi) {
+ _this.options.onCreateLi(node, $li);
+ }
+ return $li;
+ };
+ createNodeLi = function(node) {
+ var class_string, escaped_name, li_classes;
+ li_classes = ['jqtree_common'];
+ if (_this.select_node_handler && _this.select_node_handler.isNodeSelected(node)) {
+ li_classes.push('jqtree-selected');
+ }
+ class_string = li_classes.join(' ');
+ escaped_name = escapeIfNecessary(node.name);
+ return $("<li class=\"" + class_string + "\"><div class=\"jqtree-element jqtree_common\"><span class=\"jqtree-title jqtree_common\">" + escaped_name + "</span></div></li>");
+ };
+ createFolderLi = function(node) {
+ var button_char, button_classes, escaped_name, folder_classes, getButtonClasses, getFolderClasses;
+ getButtonClasses = function() {
+ var classes;
+ classes = ['jqtree-toggler'];
+ if (!node.is_open) {
+ classes.push('jqtree-closed');
+ }
+ return classes.join(' ');
+ };
+ getFolderClasses = function() {
+ var classes;
+ classes = ['jqtree-folder'];
+ if (!node.is_open) {
+ classes.push('jqtree-closed');
+ }
+ if (_this.select_node_handler && _this.select_node_handler.isNodeSelected(node)) {
+ classes.push('jqtree-selected');
+ }
+ return classes.join(' ');
+ };
+ button_classes = getButtonClasses();
+ folder_classes = getFolderClasses();
+ escaped_name = escapeIfNecessary(node.name);
+ if (node.is_open) {
+ button_char = _this.options.openedIcon;
+ } else {
+ button_char = _this.options.closedIcon;
+ }
+ return $("<li class=\"jqtree_common " + folder_classes + "\"><div class=\"jqtree-element jqtree_common\"><a class=\"jqtree_common " + button_classes + "\">" + button_char + "</a><span class=\"jqtree_common jqtree-title\">" + escaped_name + "</span></div></li>");
+ };
+ doCreateDomElements = function($element, children, is_root_node, is_open) {
+ var $li, $ul, child, _i, _len;
+ $ul = createUl(is_root_node);
+ $element.append($ul);
+ for (_i = 0, _len = children.length; _i < _len; _i++) {
+ child = children[_i];
+ $li = createLi(child);
+ $ul.append($li);
+ child.element = $li[0];
+ $li.data('node', child);
+ if (child.hasChildren()) {
+ doCreateDomElements($li, child.children, false, child.is_open);
+ }
+ }
+ return null;
+ };
+ if (from_node && from_node.parent) {
+ is_root_node = false;
+ node_element = this._getNodeElementForNode(from_node);
+ node_element.getUl().remove();
+ $element = node_element.$element;
+ } else {
+ from_node = this.tree;
+ $element = this.element;
+ $element.empty();
+ is_root_node = true;
+ }
+ doCreateDomElements($element, from_node.children, is_root_node, is_root_node);
+ return this._triggerEvent('tree.refresh');
+ };
+
+ JqTreeWidget.prototype._click = function(e) {
+ var $button, $el, $target, event, node;
+ $target = $(e.target);
+ $button = $target.closest('.jqtree-toggler');
+ if ($button.length) {
+ node = this._getNode($button);
+ if (node) {
+ this.toggle(node, this.options.slide);
+ e.preventDefault();
+ return e.stopPropagation();
+ }
+ } else {
+ $el = $target.closest('.jqtree-element');
+ if ($el.length) {
+ node = this._getNode($el);
+ if (node) {
+ event = this._triggerEvent('tree.click', {
+ node: node
+ });
+ if (!event.isDefaultPrevented()) {
+ return this._selectNode(node, true);
+ }
+ }
+ }
+ }
+ };
+
+ JqTreeWidget.prototype._getNode = function($element) {
+ var $li;
+ $li = $element.closest('li');
+ if ($li.length === 0) {
+ return null;
+ } else {
+ return $li.data('node');
+ }
+ };
+
+ JqTreeWidget.prototype._getNodeElementForNode = function(node) {
+ if (node.isFolder()) {
+ return new FolderElement(node, this);
+ } else {
+ return new NodeElement(node, this);
+ }
+ };
+
+ JqTreeWidget.prototype._getNodeElement = function($element) {
+ var node;
+ node = this._getNode($element);
+ if (node) {
+ return this._getNodeElementForNode(node);
+ } else {
+ return null;
+ }
+ };
+
+ JqTreeWidget.prototype._contextmenu = function(e) {
+ var $div, node;
+ $div = $(e.target).closest('ul.jqtree-tree .jqtree-element');
+ if ($div.length) {
+ node = this._getNode($div);
+ if (node) {
+ e.preventDefault();
+ e.stopPropagation();
+ this._triggerEvent('tree.contextmenu', {
+ node: node,
+ click_event: e
+ });
+ return false;
+ }
+ }
+ };
+
+ JqTreeWidget.prototype._saveState = function() {
+ if (this.options.saveState) {
+ return this.save_state_handler.saveState();
+ }
+ };
+
+ JqTreeWidget.prototype._mouseCapture = function(position_info) {
+ if (this.options.dragAndDrop) {
+ return this.dnd_handler.mouseCapture(position_info);
+ } else {
+ return false;
+ }
+ };
+
+ JqTreeWidget.prototype._mouseStart = function(position_info) {
+ if (this.options.dragAndDrop) {
+ return this.dnd_handler.mouseStart(position_info);
+ } else {
+ return false;
+ }
+ };
+
+ JqTreeWidget.prototype._mouseDrag = function(position_info) {
+ var result;
+ if (this.options.dragAndDrop) {
+ result = this.dnd_handler.mouseDrag(position_info);
+ if (this.scroll_handler) {
+ this.scroll_handler.checkScrolling();
+ }
+ return result;
+ } else {
+ return false;
+ }
+ };
+
+ JqTreeWidget.prototype._mouseStop = function(position_info) {
+ if (this.options.dragAndDrop) {
+ return this.dnd_handler.mouseStop(position_info);
+ } else {
+ return false;
+ }
+ };
+
+ JqTreeWidget.prototype._triggerEvent = function(event_name, values) {
+ var event;
+ event = $.Event(event_name);
+ $.extend(event, values);
+ this.element.trigger(event);
+ return event;
+ };
+
+ JqTreeWidget.prototype.testGenerateHitAreas = function(moving_node) {
+ this.dnd_handler.current_item = this._getNodeElementForNode(moving_node);
+ this.dnd_handler.generateHitAreas();
+ return this.dnd_handler.hit_areas;
+ };
+
+ JqTreeWidget.prototype._selectCurrentNode = function() {
+ var node, node_element;
+ node = this.getSelectedNode();
+ if (node) {
+ node_element = this._getNodeElementForNode(node);
+ if (node_element) {
+ return node_element.select();
+ }
+ }
+ };
+
+ JqTreeWidget.prototype._deselectCurrentNode = function() {
+ var node;
+ node = this.getSelectedNode();
+ if (node) {
+ return this.removeFromSelection(node);
+ }
+ };
+
+ return JqTreeWidget;
+
+ })(MouseWidget);
+
+ SimpleWidget.register(JqTreeWidget, 'tree');
+
+ NodeElement = (function() {
+ function NodeElement(node, tree_widget) {
+ this.init(node, tree_widget);
+ }
+
+ NodeElement.prototype.init = function(node, tree_widget) {
+ this.node = node;
+ this.tree_widget = tree_widget;
+ return this.$element = $(node.element);
+ };
+
+ NodeElement.prototype.getUl = function() {
+ return this.$element.children('ul:first');
+ };
+
+ NodeElement.prototype.getSpan = function() {
+ return this.$element.children('.jqtree-element').find('span.jqtree-title');
+ };
+
+ NodeElement.prototype.getLi = function() {
+ return this.$element;
+ };
+
+ NodeElement.prototype.addDropHint = function(position) {
+ if (position === Position.INSIDE) {
+ return new BorderDropHint(this.$element);
+ } else {
+ return new GhostDropHint(this.node, this.$element, position);
+ }
+ };
+
+ NodeElement.prototype.select = function() {
+ return this.getLi().addClass('jqtree-selected');
+ };
+
+ NodeElement.prototype.deselect = function() {
+ return this.getLi().removeClass('jqtree-selected');
+ };
+
+ return NodeElement;
+
+ })();
+
+ FolderElement = (function(_super) {
+ __extends(FolderElement, _super);
+
+ function FolderElement() {
+ _ref2 = FolderElement.__super__.constructor.apply(this, arguments);
+ return _ref2;
+ }
+
+ FolderElement.prototype.open = function(on_finished, slide) {
+ var $button, doOpen,
+ _this = this;
+ if (slide == null) {
+ slide = true;
+ }
+ if (!this.node.is_open) {
+ this.node.is_open = true;
+ $button = this.getButton();
+ $button.removeClass('jqtree-closed');
+ $button.html(this.tree_widget.options.openedIcon);
+ doOpen = function() {
+ _this.getLi().removeClass('jqtree-closed');
+ if (on_finished) {
+ on_finished();
+ }
+ return _this.tree_widget._triggerEvent('tree.open', {
+ node: _this.node
+ });
+ };
+ if (slide) {
+ return this.getUl().slideDown('fast', doOpen);
+ } else {
+ this.getUl().show();
+ return doOpen();
+ }
+ }
+ };
+
+ FolderElement.prototype.close = function(slide) {
+ var $button, doClose,
+ _this = this;
+ if (slide == null) {
+ slide = true;
+ }
+ if (this.node.is_open) {
+ this.node.is_open = false;
+ $button = this.getButton();
+ $button.addClass('jqtree-closed');
+ $button.html(this.tree_widget.options.closedIcon);
+ doClose = function() {
+ _this.getLi().addClass('jqtree-closed');
+ return _this.tree_widget._triggerEvent('tree.close', {
+ node: _this.node
+ });
+ };
+ if (slide) {
+ return this.getUl().slideUp('fast', doClose);
+ } else {
+ this.getUl().hide();
+ return doClose();
+ }
+ }
+ };
+
+ FolderElement.prototype.getButton = function() {
+ return this.$element.children('.jqtree-element').find('a.jqtree-toggler');
+ };
+
+ FolderElement.prototype.addDropHint = function(position) {
+ if (!this.node.is_open && position === Position.INSIDE) {
+ return new BorderDropHint(this.$element);
+ } else {
+ return new GhostDropHint(this.node, this.$element, position);
+ }
+ };
+
+ return FolderElement;
+
+ })(NodeElement);
+
+ html_escape = function(string) {
+ return ('' + string).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;').replace(/\//g, '&#x2F;');
+ };
+
+ _indexOf = function(array, item) {
+ var i, value, _i, _len;
+ for (i = _i = 0, _len = array.length; _i < _len; i = ++_i) {
+ value = array[i];
+ if (value === item) {
+ return i;
+ }
+ }
+ return -1;
+ };
+
+ indexOf = function(array, item) {
+ if (array.indexOf) {
+ return array.indexOf(item);
+ } else {
+ return _indexOf(array, item);
+ }
+ };
+
+ this.Tree.indexOf = indexOf;
+
+ this.Tree._indexOf = _indexOf;
+
+ if (!((this.JSON != null) && (this.JSON.stringify != null) && typeof this.JSON.stringify === 'function')) {
+ json_escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
+ json_meta = {
+ '\b': '\\b',
+ '\t': '\\t',
+ '\n': '\\n',
+ '\f': '\\f',
+ '\r': '\\r',
+ '"': '\\"',
+ '\\': '\\\\'
+ };
+ json_quote = function(string) {
+ json_escapable.lastIndex = 0;
+ if (json_escapable.test(string)) {
+ return '"' + string.replace(json_escapable, function(a) {
+ var c;
+ c = json_meta[a];
+ return (typeof c === 'string' ? c : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4));
+ }) + '"';
+ } else {
+ return '"' + string + '"';
+ }
+ };
+ json_str = function(key, holder) {
+ var i, k, partial, v, value, _i, _len;
+ value = holder[key];
+ switch (typeof value) {
+ case 'string':
+ return json_quote(value);
+ case 'number':
+ if (isFinite(value)) {
+ return String(value);
+ } else {
+ return 'null';
+ }
+ case 'boolean':
+ case 'null':
+ return String(value);
+ case 'object':
+ if (!value) {
+ return 'null';
+ }
+ partial = [];
+ if (Object.prototype.toString.apply(value) === '[object Array]') {
+ for (i = _i = 0, _len = value.length; _i < _len; i = ++_i) {
+ v = value[i];
+ partial[i] = json_str(i, value) || 'null';
+ }
+ return (partial.length === 0 ? '[]' : '[' + partial.join(',') + ']');
+ }
+ for (k in value) {
+ if (Object.prototype.hasOwnProperty.call(value, k)) {
+ v = json_str(k, value);
+ if (v) {
+ partial.push(json_quote(k) + ':' + v);
+ }
+ }
+ }
+ return (partial.length === 0 ? '{}' : '{' + partial.join(',') + '}');
+ }
+ };
+ if (this.JSON == null) {
+ this.JSON = {};
+ }
+ this.JSON.stringify = function(value) {
+ return json_str('', {
+ '': value
+ });
+ };
+ }
+
+ SaveStateHandler = (function() {
+ function SaveStateHandler(tree_widget) {
+ this.tree_widget = tree_widget;
+ }
+
+ SaveStateHandler.prototype.saveState = function() {
+ var state;
+ state = JSON.stringify(this.getState());
+ if (this.tree_widget.options.onSetStateFromStorage) {
+ return this.tree_widget.options.onSetStateFromStorage(state);
+ } else if (typeof localStorage !== "undefined" && localStorage !== null) {
+ return localStorage.setItem(this.getCookieName(), state);
+ } else if ($.cookie) {
+ $.cookie.raw = true;
+ return $.cookie(this.getCookieName(), state, {
+ path: '/'
+ });
+ }
+ };
+
+ SaveStateHandler.prototype.restoreState = function() {
+ var state;
+ state = this.getStateFromStorage();
+ if (state) {
+ this.setState($.parseJSON(state));
+ return true;
+ } else {
+ return false;
+ }
+ };
+
+ SaveStateHandler.prototype.getStateFromStorage = function() {
+ if (this.tree_widget.options.onGetStateFromStorage) {
+ return this.tree_widget.options.onGetStateFromStorage();
+ } else if (typeof localStorage !== "undefined" && localStorage !== null) {
+ return localStorage.getItem(this.getCookieName());
+ } else if ($.cookie) {
+ $.cookie.raw = true;
+ return $.cookie(this.getCookieName());
+ } else {
+ return null;
+ }
+ };
+
+ SaveStateHandler.prototype.getState = function() {
+ var open_nodes, selected_node, selected_node_id,
+ _this = this;
+ open_nodes = [];
+ this.tree_widget.tree.iterate(function(node) {
+ if (node.is_open && node.id && node.hasChildren()) {
+ open_nodes.push(node.id);
+ }
+ return true;
+ });
+ selected_node = this.tree_widget.getSelectedNode();
+ if (selected_node) {
+ selected_node_id = selected_node.id;
+ } else {
+ selected_node_id = '';
+ }
+ return {
+ open_nodes: open_nodes,
+ selected_node: selected_node_id
+ };
+ };
+
+ SaveStateHandler.prototype.setState = function(state) {
+ var open_nodes, selected_node, selected_node_id,
+ _this = this;
+ if (state) {
+ open_nodes = state.open_nodes;
+ selected_node_id = state.selected_node;
+ this.tree_widget.tree.iterate(function(node) {
+ node.is_open = node.id && node.hasChildren() && (indexOf(open_nodes, node.id) >= 0);
+ return true;
+ });
+ if (selected_node_id && this.tree_widget.select_node_handler) {
+ this.tree_widget.select_node_handler.clear();
+ selected_node = this.tree_widget.getNodeById(selected_node_id);
+ if (selected_node) {
+ return this.tree_widget.select_node_handler.addToSelection(selected_node);
+ }
+ }
+ }
+ };
+
+ SaveStateHandler.prototype.getCookieName = function() {
+ if (typeof this.tree_widget.options.saveState === 'string') {
+ return this.tree_widget.options.saveState;
+ } else {
+ return 'tree';
+ }
+ };
+
+ return SaveStateHandler;
+
+ })();
+
+ SelectNodeHandler = (function() {
+ function SelectNodeHandler(tree_widget) {
+ this.tree_widget = tree_widget;
+ this.clear();
+ }
+
+ SelectNodeHandler.prototype.getSelectedNode = function() {
+ var selected_nodes;
+ selected_nodes = this.getSelectedNodes();
+ if (selected_nodes.length) {
+ return selected_nodes[0];
+ } else {
+ return false;
+ }
+ };
+
+ SelectNodeHandler.prototype.getSelectedNodes = function() {
+ var id, node, selected_nodes;
+ if (this.selected_single_node) {
+ return [this.selected_single_node];
+ } else {
+ selected_nodes = [];
+ for (id in this.selected_nodes) {
+ node = this.tree_widget.getNodeById(id);
+ if (node) {
+ selected_nodes.push(node);
+ }
+ }
+ return selected_nodes;
+ }
+ };
+
+ SelectNodeHandler.prototype.isNodeSelected = function(node) {
+ if (node.id) {
+ return this.selected_nodes[node.id];
+ } else if (this.selected_single_node) {
+ return this.selected_single_node.element === node.element;
+ } else {
+ return false;
+ }
+ };
+
+ SelectNodeHandler.prototype.clear = function() {
+ this.selected_nodes = {};
+ return this.selected_single_node = null;
+ };
+
+ SelectNodeHandler.prototype.removeFromSelection = function(node, include_children) {
+ var _this = this;
+ if (include_children == null) {
+ include_children = false;
+ }
+ if (!node.id) {
+ if (node.element === this.selected_single_node.element) {
+ return this.selected_single_node = null;
+ }
+ } else {
+ delete this.selected_nodes[node.id];
+ if (include_children) {
+ return node.iterate(function(n) {
+ delete _this.selected_nodes[node.id];
+ return true;
+ });
+ }
+ }
+ };
+
+ SelectNodeHandler.prototype.addToSelection = function(node) {
+ if (node.id) {
+ return this.selected_nodes[node.id] = true;
+ } else {
+ return this.selected_single_node = node;
+ }
+ };
+
+ return SelectNodeHandler;
+
+ })();
+
+ DragAndDropHandler = (function() {
+ function DragAndDropHandler(tree_widget) {
+ this.tree_widget = tree_widget;
+ this.hovered_area = null;
+ this.$ghost = null;
+ this.hit_areas = [];
+ this.is_dragging = false;
+ }
+
+ DragAndDropHandler.prototype.mouseCapture = function(position_info) {
+ var $element, node_element;
+ $element = $(position_info.target);
+ if (this.tree_widget.options.onIsMoveHandle && !this.tree_widget.options.onIsMoveHandle($element)) {
+ return null;
+ }
+ node_element = this.tree_widget._getNodeElement($element);
+ if (node_element && this.tree_widget.options.onCanMove) {
+ if (!this.tree_widget.options.onCanMove(node_element.node)) {
+ node_element = null;
+ }
+ }
+ this.current_item = node_element;
+ return this.current_item !== null;
+ };
+
+ DragAndDropHandler.prototype.mouseStart = function(position_info) {
+ var offset;
+ this.refreshHitAreas();
+ offset = $(position_info.target).offset();
+ this.drag_element = new DragElement(this.current_item.node, position_info.page_x - offset.left, position_info.page_y - offset.top, this.tree_widget.element);
+ this.is_dragging = true;
+ this.current_item.$element.addClass('jqtree-moving');
+ return true;
+ };
+
+ DragAndDropHandler.prototype.mouseDrag = function(position_info) {
+ var area, can_move_to;
+ this.drag_element.move(position_info.page_x, position_info.page_y);
+ area = this.findHoveredArea(position_info.page_x, position_info.page_y);
+ can_move_to = this.canMoveToArea(area);
+ if (area) {
+ if (this.hovered_area !== area) {
+ this.hovered_area = area;
+ if (this.mustOpenFolderTimer(area)) {
+ this.startOpenFolderTimer(area.node);
+ }
+ if (can_move_to) {
+ this.updateDropHint();
+ }
+ }
+ } else {
+ this.removeHover();
+ this.removeDropHint();
+ this.stopOpenFolderTimer();
+ }
+ return true;
+ };
+
+ DragAndDropHandler.prototype.canMoveToArea = function(area) {
+ var position_name;
+ if (!area) {
+ return false;
+ } else if (this.tree_widget.options.onCanMoveTo) {
+ position_name = Position.getName(area.position);
+ return this.tree_widget.options.onCanMoveTo(this.current_item.node, area.node, position_name);
+ } else {
+ return true;
+ }
+ };
+
+ DragAndDropHandler.prototype.mouseStop = function(position_info) {
+ this.moveItem(position_info);
+ this.clear();
+ this.removeHover();
+ this.removeDropHint();
+ this.removeHitAreas();
+ if (this.current_item) {
+ this.current_item.$element.removeClass('jqtree-moving');
+ }
+ this.is_dragging = false;
+ return false;
+ };
+
+ DragAndDropHandler.prototype.refreshHitAreas = function() {
+ this.removeHitAreas();
+ return this.generateHitAreas();
+ };
+
+ DragAndDropHandler.prototype.removeHitAreas = function() {
+ return this.hit_areas = [];
+ };
+
+ DragAndDropHandler.prototype.clear = function() {
+ this.drag_element.remove();
+ return this.drag_element = null;
+ };
+
+ DragAndDropHandler.prototype.removeDropHint = function() {
+ if (this.previous_ghost) {
+ return this.previous_ghost.remove();
+ }
+ };
+
+ DragAndDropHandler.prototype.removeHover = function() {
+ return this.hovered_area = null;
+ };
+
+ DragAndDropHandler.prototype.generateHitAreas = function() {
+ var addPosition, getTop, groupPositions, handleAfterOpenFolder, handleClosedFolder, handleFirstNode, handleNode, handleOpenFolder, hit_areas, last_top, positions,
+ _this = this;
+ positions = [];
+ last_top = 0;
+ getTop = function($element) {
+ return $element.offset().top;
+ };
+ addPosition = function(node, position, top) {
+ positions.push({
+ top: top,
+ node: node,
+ position: position
+ });
+ return last_top = top;
+ };
+ groupPositions = function(handle_group) {
+ var group, position, previous_top, _i, _len;
+ previous_top = -1;
+ group = [];
+ for (_i = 0, _len = positions.length; _i < _len; _i++) {
+ position = positions[_i];
+ if (position.top !== previous_top) {
+ if (group.length) {
+ handle_group(group, previous_top, position.top);
+ }
+ previous_top = position.top;
+ group = [];
+ }
+ group.push(position);
+ }
+ return handle_group(group, previous_top, _this.tree_widget.element.offset().top + _this.tree_widget.element.height());
+ };
+ handleNode = function(node, next_node, $element) {
+ var top;
+ top = getTop($element);
+ if (node === _this.current_item.node) {
+ addPosition(node, Position.NONE, top);
+ } else {
+ addPosition(node, Position.INSIDE, top);
+ }
+ if (next_node === _this.current_item.node || node === _this.current_item.node) {
+ return addPosition(node, Position.NONE, top);
+ } else {
+ return addPosition(node, Position.AFTER, top);
+ }
+ };
+ handleOpenFolder = function(node, $element) {
+ if (node === _this.current_item.node) {
+ return false;
+ }
+ if (node.children[0] !== _this.current_item.node) {
+ addPosition(node, Position.INSIDE, getTop($element));
+ }
+ return true;
+ };
+ handleAfterOpenFolder = function(node, next_node, $element) {
+ if (node === _this.current_item.node || next_node === _this.current_item.node) {
+ return addPosition(node, Position.NONE, last_top);
+ } else {
+ return addPosition(node, Position.AFTER, last_top);
+ }
+ };
+ handleClosedFolder = function(node, next_node, $element) {
+ var top;
+ top = getTop($element);
+ if (node === _this.current_item.node) {
+ return addPosition(node, Position.NONE, top);
+ } else {
+ addPosition(node, Position.INSIDE, top);
+ if (next_node !== _this.current_item.node) {
+ return addPosition(node, Position.AFTER, top);
+ }
+ }
+ };
+ handleFirstNode = function(node, $element) {
+ if (node !== _this.current_item.node) {
+ return addPosition(node, Position.BEFORE, getTop($(node.element)));
+ }
+ };
+ this.iterateVisibleNodes(handleNode, handleOpenFolder, handleClosedFolder, handleAfterOpenFolder, handleFirstNode);
+ hit_areas = [];
+ groupPositions(function(positions_in_group, top, bottom) {
+ var area_height, area_top, position, _i, _len;
+ area_height = (bottom - top) / positions_in_group.length;
+ area_top = top;
+ for (_i = 0, _len = positions_in_group.length; _i < _len; _i++) {
+ position = positions_in_group[_i];
+ hit_areas.push({
+ top: area_top,
+ bottom: area_top + area_height,
+ node: position.node,
+ position: position.position
+ });
+ area_top += area_height;
+ }
+ return null;
+ });
+ return this.hit_areas = hit_areas;
+ };
+
+ DragAndDropHandler.prototype.iterateVisibleNodes = function(handle_node, handle_open_folder, handle_closed_folder, handle_after_open_folder, handle_first_node) {
+ var is_first_node, iterate,
+ _this = this;
+ is_first_node = true;
+ iterate = function(node, next_node) {
+ var $element, child, children_length, i, must_iterate_inside, _i, _len, _ref3;
+ must_iterate_inside = (node.is_open || !node.element) && node.hasChildren();
+ if (node.element) {
+ $element = $(node.element);
+ if (!$element.is(':visible')) {
+ return;
+ }
+ if (is_first_node) {
+ handle_first_node(node, $element);
+ is_first_node = false;
+ }
+ if (!node.hasChildren()) {
+ handle_node(node, next_node, $element);
+ } else if (node.is_open) {
+ if (!handle_open_folder(node, $element)) {
+ must_iterate_inside = false;
+ }
+ } else {
+ handle_closed_folder(node, next_node, $element);
+ }
+ }
+ if (must_iterate_inside) {
+ children_length = node.children.length;
+ _ref3 = node.children;
+ for (i = _i = 0, _len = _ref3.length; _i < _len; i = ++_i) {
+ child = _ref3[i];
+ if (i === (children_length - 1)) {
+ iterate(node.children[i], null);
+ } else {
+ iterate(node.children[i], node.children[i + 1]);
+ }
+ }
+ if (node.is_open) {
+ return handle_after_open_folder(node, next_node, $element);
+ }
+ }
+ };
+ return iterate(this.tree_widget.tree);
+ };
+
+ DragAndDropHandler.prototype.findHoveredArea = function(x, y) {
+ var area, high, low, mid, tree_offset;
+ tree_offset = this.tree_widget.element.offset();
+ if (x < tree_offset.left || y < tree_offset.top || x > (tree_offset.left + this.tree_widget.element.width()) || y > (tree_offset.top + this.tree_widget.element.height())) {
+ return null;
+ }
+ low = 0;
+ high = this.hit_areas.length;
+ while (low < high) {
+ mid = (low + high) >> 1;
+ area = this.hit_areas[mid];
+ if (y < area.top) {
+ high = mid;
+ } else if (y > area.bottom) {
+ low = mid + 1;
+ } else {
+ return area;
+ }
+ }
+ return null;
+ };
+
+ DragAndDropHandler.prototype.mustOpenFolderTimer = function(area) {
+ var node;
+ node = area.node;
+ return node.isFolder() && !node.is_open && area.position === Position.INSIDE;
+ };
+
+ DragAndDropHandler.prototype.updateDropHint = function() {
+ var node_element;
+ if (!this.hovered_area) {
+ return;
+ }
+ this.removeDropHint();
+ node_element = this.tree_widget._getNodeElementForNode(this.hovered_area.node);
+ return this.previous_ghost = node_element.addDropHint(this.hovered_area.position);
+ };
+
+ DragAndDropHandler.prototype.startOpenFolderTimer = function(folder) {
+ var openFolder,
+ _this = this;
+ openFolder = function() {
+ return _this.tree_widget._openNode(folder, _this.tree_widget.options.slide, function() {
+ _this.refreshHitAreas();
+ return _this.updateDropHint();
+ });
+ };
+ return this.open_folder_timer = setTimeout(openFolder, 500);
+ };
+
+ DragAndDropHandler.prototype.stopOpenFolderTimer = function() {
+ if (this.open_folder_timer) {
+ clearTimeout(this.open_folder_timer);
+ return this.open_folder_timer = null;
+ }
+ };
+
+ DragAndDropHandler.prototype.moveItem = function(position_info) {
+ var doMove, event, moved_node, position, previous_parent, target_node,
+ _this = this;
+ if (this.hovered_area && this.hovered_area.position !== Position.NONE && this.canMoveToArea(this.hovered_area)) {
+ moved_node = this.current_item.node;
+ target_node = this.hovered_area.node;
+ position = this.hovered_area.position;
+ previous_parent = moved_node.parent;
+ if (position === Position.INSIDE) {
+ this.hovered_area.node.is_open = true;
+ }
+ doMove = function() {
+ _this.tree_widget.tree.moveNode(moved_node, target_node, position);
+ _this.tree_widget.element.empty();
+ return _this.tree_widget._refreshElements();
+ };
+ event = this.tree_widget._triggerEvent('tree.move', {
+ move_info: {
+ moved_node: moved_node,
+ target_node: target_node,
+ position: Position.getName(position),
+ previous_parent: previous_parent,
+ do_move: doMove,
+ original_event: position_info.original_event
+ }
+ });
+ if (!event.isDefaultPrevented()) {
+ return doMove();
+ }
+ }
+ };
+
+ return DragAndDropHandler;
+
+ })();
+
+ DragElement = (function() {
+ function DragElement(node, offset_x, offset_y, $tree) {
+ this.offset_x = offset_x;
+ this.offset_y = offset_y;
+ this.$element = $("<span class=\"jqtree-title jqtree-dragging\">" + node.name + "</span>");
+ this.$element.css("position", "absolute");
+ $tree.append(this.$element);
+ }
+
+ DragElement.prototype.move = function(page_x, page_y) {
+ return this.$element.offset({
+ left: page_x - this.offset_x,
+ top: page_y - this.offset_y
+ });
+ };
+
+ DragElement.prototype.remove = function() {
+ return this.$element.remove();
+ };
+
+ return DragElement;
+
+ })();
+
+ GhostDropHint = (function() {
+ function GhostDropHint(node, $element, position) {
+ this.$element = $element;
+ this.node = node;
+ this.$ghost = $('<li class="jqtree_common jqtree-ghost"><span class="jqtree_common jqtree-circle"></span><span class="jqtree_common jqtree-line"></span></li>');
+ if (position === Position.AFTER) {
+ this.moveAfter();
+ } else if (position === Position.BEFORE) {
+ this.moveBefore();
+ } else if (position === Position.INSIDE) {
+ if (node.isFolder() && node.is_open) {
+ this.moveInsideOpenFolder();
+ } else {
+ this.moveInside();
+ }
+ }
+ }
+
+ GhostDropHint.prototype.remove = function() {
+ return this.$ghost.remove();
+ };
+
+ GhostDropHint.prototype.moveAfter = function() {
+ return this.$element.after(this.$ghost);
+ };
+
+ GhostDropHint.prototype.moveBefore = function() {
+ return this.$element.before(this.$ghost);
+ };
+
+ GhostDropHint.prototype.moveInsideOpenFolder = function() {
+ return $(this.node.children[0].element).before(this.$ghost);
+ };
+
+ GhostDropHint.prototype.moveInside = function() {
+ this.$element.after(this.$ghost);
+ return this.$ghost.addClass('jqtree-inside');
+ };
+
+ return GhostDropHint;
+
+ })();
+
+ BorderDropHint = (function() {
+ function BorderDropHint($element) {
+ var $div, width;
+ $div = $element.children('.jqtree-element');
+ width = $element.width() - 4;
+ this.$hint = $('<span class="jqtree-border"></span>');
+ $div.append(this.$hint);
+ this.$hint.css({
+ width: width,
+ height: $div.height() - 4
+ });
+ }
+
+ BorderDropHint.prototype.remove = function() {
+ return this.$hint.remove();
+ };
+
+ return BorderDropHint;
+
+ })();
+
+ ScrollHandler = (function() {
+ function ScrollHandler(tree_widget) {
+ this.tree_widget = tree_widget;
+ this.previous_top = -1;
+ this._initScrollParent();
+ }
+
+ ScrollHandler.prototype._initScrollParent = function() {
+ var $scroll_parent, getParentWithOverflow, setDocumentAsScrollParent,
+ _this = this;
+ getParentWithOverflow = function() {
+ var css_value, css_values, parent, scroll_parent, _i, _j, _len, _len1, _ref3, _ref4;
+ css_values = ['overflow', 'overflow-y'];
+ scroll_parent = null;
+ _ref3 = _this.tree_widget.$el.parents();
+ for (_i = 0, _len = _ref3.length; _i < _len; _i++) {
+ parent = _ref3[_i];
+ for (_j = 0, _len1 = css_values.length; _j < _len1; _j++) {
+ css_value = css_values[_j];
+ if ((_ref4 = $.css(parent, css_value)) === 'auto' || _ref4 === 'scroll') {
+ return $(parent);
+ }
+ }
+ }
+ return null;
+ };
+ setDocumentAsScrollParent = function() {
+ _this.scroll_parent_top = 0;
+ return _this.$scroll_parent = null;
+ };
+ if (this.tree_widget.$el.css('position') === 'fixed') {
+ setDocumentAsScrollParent();
+ }
+ $scroll_parent = getParentWithOverflow();
+ if ($scroll_parent && $scroll_parent.length && $scroll_parent[0].tagName !== 'HTML') {
+ this.$scroll_parent = $scroll_parent;
+ return this.scroll_parent_top = this.$scroll_parent.offset().top;
+ } else {
+ return setDocumentAsScrollParent();
+ }
+ };
+
+ ScrollHandler.prototype.checkScrolling = function() {
+ var hovered_area;
+ hovered_area = this.tree_widget.dnd_handler.hovered_area;
+ if (hovered_area && hovered_area.top !== this.previous_top) {
+ this.previous_top = hovered_area.top;
+ if (this.$scroll_parent) {
+ return this._handleScrollingWithScrollParent(hovered_area);
+ } else {
+ return this._handleScrollingWithDocument(hovered_area);
+ }
+ }
+ };
+
+ ScrollHandler.prototype._handleScrollingWithScrollParent = function(area) {
+ var distance_bottom;
+ distance_bottom = this.scroll_parent_top + this.$scroll_parent[0].offsetHeight - area.bottom;
+ if (distance_bottom < 20) {
+ this.$scroll_parent[0].scrollTop += 20;
+ this.tree_widget.refreshHitAreas();
+ return this.previous_top = -1;
+ } else if ((area.top - this.scroll_parent_top) < 20) {
+ this.$scroll_parent[0].scrollTop -= 20;
+ this.tree_widget.refreshHitAreas();
+ return this.previous_top = -1;
+ }
+ };
+
+ ScrollHandler.prototype._handleScrollingWithDocument = function(area) {
+ var distance_top;
+ distance_top = area.top - $(document).scrollTop();
+ if (distance_top < 20) {
+ return $(document).scrollTop($(document).scrollTop() - 20);
+ } else if ($(window).height() - (area.bottom - $(document).scrollTop()) < 20) {
+ return $(document).scrollTop($(document).scrollTop() + 20);
+ }
+ };
+
+ ScrollHandler.prototype.scrollTo = function(top) {
+ var tree_top;
+ if (this.$scroll_parent) {
+ return this.$scroll_parent[0].scrollTop = top;
+ } else {
+ tree_top = this.tree_widget.$el.offset().top;
+ return $(document).scrollTop(top + tree_top);
+ }
+ };
+
+ ScrollHandler.prototype.isScrolledIntoView = function(element) {
+ var $element, element_bottom, element_top, view_bottom, view_top;
+ $element = $(element);
+ if (this.$scroll_parent) {
+ view_top = 0;
+ view_bottom = this.$scroll_parent.height();
+ element_top = $element.offset().top - this.scroll_parent_top;
+ element_bottom = element_top + $element.height();
+ } else {
+ view_top = $(window).scrollTop();
+ view_bottom = view_top + $(window).height();
+ element_top = $element.offset().top;
+ element_bottom = element_top + $element.height();
+ }
+ return (element_bottom <= view_bottom) && (element_top >= view_top);
+ };
+
+ return ScrollHandler;
+
+ })();
+
+ KeyHandler = (function() {
+ var DOWN, LEFT, RIGHT, UP;
+
+ LEFT = 37;
+
+ UP = 38;
+
+ RIGHT = 39;
+
+ DOWN = 40;
+
+ function KeyHandler(tree_widget) {
+ this.tree_widget = tree_widget;
+ $(document).bind('keydown.jqtree', $.proxy(this.handleKeyDown, this));
+ }
+
+ KeyHandler.prototype.deinit = function() {
+ return $(document).unbind('keydown.jqtree');
+ };
+
+ KeyHandler.prototype.handleKeyDown = function(e) {
+ var current_node, key, moveDown, moveLeft, moveRight, moveUp, selectNode,
+ _this = this;
+ current_node = this.tree_widget.getSelectedNode();
+ selectNode = function(node) {
+ if (node) {
+ _this.tree_widget.selectNode(node);
+ if (_this.tree_widget.scroll_handler && (!_this.tree_widget.scroll_handler.isScrolledIntoView($(node.element).find('.jqtree-element')))) {
+ _this.tree_widget.scrollToNode(node);
+ }
+ return false;
+ } else {
+ return true;
+ }
+ };
+ moveDown = function() {
+ return selectNode(_this.getNextNode(current_node));
+ };
+ moveUp = function() {
+ return selectNode(_this.getPreviousNode(current_node));
+ };
+ moveRight = function() {
+ if (current_node.hasChildren() && !current_node.is_open) {
+ _this.tree_widget.openNode(current_node);
+ return false;
+ } else {
+ return true;
+ }
+ };
+ moveLeft = function() {
+ if (current_node.hasChildren() && current_node.is_open) {
+ _this.tree_widget.closeNode(current_node);
+ return false;
+ } else {
+ return true;
+ }
+ };
+ if (!current_node) {
+ return true;
+ } else {
+ key = e.which;
+ switch (key) {
+ case DOWN:
+ return moveDown();
+ case UP:
+ return moveUp();
+ case RIGHT:
+ return moveRight();
+ case LEFT:
+ return moveLeft();
+ }
+ }
+ };
+
+ KeyHandler.prototype.getNextNode = function(node, include_children) {
+ var next_sibling;
+ if (include_children == null) {
+ include_children = true;
+ }
+ if (include_children && node.hasChildren() && node.is_open) {
+ return node.children[0];
+ } else {
+ if (!node.parent) {
+ return null;
+ } else {
+ next_sibling = node.getNextSibling();
+ if (next_sibling) {
+ return next_sibling;
+ } else {
+ return this.getNextNode(node.parent, false);
+ }
+ }
+ }
+ };
+
+ KeyHandler.prototype.getPreviousNode = function(node) {
+ var previous_sibling;
+ if (!node.parent) {
+ return null;
+ } else {
+ previous_sibling = node.getPreviousSibling();
+ if (previous_sibling) {
+ if (!previous_sibling.hasChildren() || !previous_sibling.is_open) {
+ return previous_sibling;
+ } else {
+ return this.getLastChild(previous_sibling);
+ }
+ } else {
+ if (node.parent.parent) {
+ return node.parent;
+ } else {
+ return null;
+ }
+ }
+ }
+ };
+
+ KeyHandler.prototype.getLastChild = function(node) {
+ var last_child;
+ if (!node.hasChildren()) {
+ return null;
+ } else {
+ last_child = node.children[node.children.length - 1];
+ if (!last_child.hasChildren() || !last_child.is_open) {
+ return last_child;
+ } else {
+ return this.getLastChild(last_child);
+ }
+ }
+ };
+
+ return KeyHandler;
+
+ })();
+
+}).call(this);
diff --git a/tools/deep_memory_profiler/visualizer/utility.js b/tools/deep_memory_profiler/visualizer/utility.js
index f595fd0..cdb7243 100644
--- a/tools/deep_memory_profiler/visualizer/utility.js
+++ b/tools/deep_memory_profiler/visualizer/utility.js
@@ -6,22 +6,51 @@
* This function returns the first element index that >= target, when no element
* in the array >= target, return array.length.
* This function must be called in the shape of binarySearch(array, target).
- * @param {number} target
+ * @param {number} target
* @return {number}
*/
var binarySearch = function(target) {
- 'use strict';
+ 'use strict';
- var left = 0;
- var right = this.length - 1;
- while (left <= right) {
- var mid = Math.floor((left + right) / 2);
- if (this[mid] < target)
- left = mid + 1;
- else if (this[mid] > target)
- right = mid - 1;
- else
- return mid;
- }
- return left;
+ var left = 0;
+ var right = this.length - 1;
+ while (left <= right) {
+ var mid = Math.floor((left + right) / 2);
+ if (this[mid] < target)
+ left = mid + 1;
+ else if (this[mid] > target)
+ right = mid - 1;
+ else
+ return mid;
+ }
+ return left;
};
+
+/**
+ * Return the intersection set of two arrays.
+ * @param {Array.<*>} left
+ * @param {Array.<*>} right
+ * @return {Array.<*>}
+ */
+var intersection = function(left, right) {
+ return left.reduce(function(previous, current) {
+ if (right.indexOf(current) != -1)
+ previous.push(current);
+ return previous;
+ }, []);
+};
+
+/**
+ * Return the difference set of left with right.
+ * Notice: difference(left, right) differentiates with difference(right, left).
+ * @param {Array.<*>} left
+ * @param {Array.<*>} right
+ * @return {Array.<*>}
+ */
+var difference = function(left, right) {
+ return left.reduce(function(previous, current) {
+ if (right.indexOf(current) == -1)
+ previous.push(current);
+ return previous;
+ }, []);
+}; \ No newline at end of file