├── .gitignore ├── DOMTreeMap.js ├── Examples ├── css │ ├── Treemap.css │ ├── base.css │ └── gradient.png ├── example1.html ├── example1.js ├── example2.html ├── example2.js ├── example3.html └── example3.js └── README.mkd /.gitignore: -------------------------------------------------------------------------------- 1 | .project 2 | -------------------------------------------------------------------------------- /DOMTreeMap.js: -------------------------------------------------------------------------------- 1 | //TreeMap object. 2 | var TM = {}; 3 | 4 | (function() { 5 | /* 6 | Script: Core.js 7 | 8 | Description: 9 | 10 | Provides common utility functions and the Class object used internally by the library. 11 | 12 | Also provides the object for manipulating JSON tree structures 13 | 14 | Some of the Basic utility functions and the Class system are based in the MooTools Framework . Copyright (c) 2006-2010 Valerio Proietti, . MIT license . 15 | 16 | Author: 17 | 18 | Nicolas Garcia Belmonte 19 | 20 | Copyright: 21 | 22 | Copyright 2008-2010 by Nicolas Garcia Belmonte. 23 | 24 | Homepage: 25 | 26 | 27 | 28 | Version: 29 | 30 | 1.0 31 | 32 | License: 33 | 34 | BSD License 35 | 36 | > Redistribution and use in source and binary forms, with or without 37 | > modification, are permitted provided that the following conditions are met: 38 | > * Redistributions of source code must retain the above copyright 39 | > notice, this list of conditions and the following disclaimer. 40 | > * Redistributions in binary form must reproduce the above copyright 41 | > notice, this list of conditions and the following disclaimer in the 42 | > documentation and/or other materials provided with the distribution. 43 | > * Neither the name of the organization nor the 44 | > names of its contributors may be used to endorse or promote products 45 | > derived from this software without specific prior written permission. 46 | > 47 | > THIS SOFTWARE IS PROVIDED BY Nicolas Garcia Belmonte ``AS IS'' AND ANY 48 | > EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 49 | > WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 50 | > DISCLAIMED. IN NO EVENT SHALL Nicolas Garcia Belmonte BE LIABLE FOR ANY 51 | > DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 52 | > (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 53 | > LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 54 | > ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 55 | > (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 56 | > SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 57 | */ 58 | 59 | var $ = function(d) { return document.getElementById(d); }; 60 | 61 | $.empty = function() {}; 62 | 63 | $.lambda = function(value) { 64 | return (typeof value == 'function') ? value : function(){ 65 | return value; 66 | }; 67 | }; 68 | 69 | $.extend = function(original, extended){ 70 | for (var key in (extended || {})) original[key] = extended[key]; 71 | return original; 72 | }; 73 | 74 | $.splat = function(obj){ 75 | var type = $.type(obj); 76 | return (type) ? ((type != 'array') ? [obj] : obj) : []; 77 | }; 78 | 79 | $.type = function(elem) { 80 | return $.type.s.call(elem).match(/^\[object\s(.*)\]$/)[1].toLowerCase(); 81 | }; 82 | $.type.s = Object.prototype.toString; 83 | 84 | $.each = function(iterable, fn){ 85 | var type = $.type(iterable); 86 | if(type == 'object') { 87 | for (var key in iterable) fn(iterable[key], key); 88 | } else { 89 | for(var i=0; i < iterable.length; i++) fn(iterable[i], i); 90 | } 91 | }; 92 | 93 | $.merge = function(){ 94 | var mix = {}; 95 | for (var i = 0, l = arguments.length; i < l; i++){ 96 | var object = arguments[i]; 97 | if ($.type(object) != 'object') continue; 98 | for (var key in object){ 99 | var op = object[key], mp = mix[key]; 100 | mix[key] = (mp && $.type(op) == 'object' && $.type(mp) == 'object') ? $.merge(mp, op) : $.unlink(op); 101 | } 102 | } 103 | return mix; 104 | }; 105 | 106 | $.unlink = function(object){ 107 | var unlinked; 108 | switch ($.type(object)){ 109 | case 'object': 110 | unlinked = {}; 111 | for (var p in object) unlinked[p] = $.unlink(object[p]); 112 | break; 113 | case 'array': 114 | unlinked = []; 115 | for (var i = 0, l = object.length; i < l; i++) unlinked[i] = $.unlink(object[i]); 116 | break; 117 | default: return object; 118 | } 119 | return unlinked; 120 | }; 121 | 122 | $.rgbToHex = function(srcArray, array){ 123 | if (srcArray.length < 3) return null; 124 | if (srcArray.length == 4 && srcArray[3] == 0 && !array) return 'transparent'; 125 | var hex = []; 126 | for (var i = 0; i < 3; i++){ 127 | var bit = (srcArray[i] - 0).toString(16); 128 | hex.push((bit.length == 1) ? '0' + bit : bit); 129 | } 130 | return (array) ? hex : '#' + hex.join(''); 131 | }; 132 | 133 | $.hexToRgb = function(hex) { 134 | if(hex.length != 7) { 135 | hex = hex.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/); 136 | hex.shift(); 137 | if (hex.length != 3) return null; 138 | var rgb = []; 139 | for(var i=0; i<3; i++) { 140 | var value = hex[i]; 141 | if (value.length == 1) value += value; 142 | rgb.push(parseInt(value, 16)); 143 | } 144 | return rgb; 145 | } else { 146 | hex = parseInt(hex.slice(1), 16); 147 | return [ 148 | hex >> 16, 149 | hex >> 8 & 0xff, 150 | hex & 0xff 151 | ]; 152 | } 153 | } 154 | 155 | $.destroy = function(elem) { 156 | $.clean(elem); 157 | if(elem.parentNode) elem.parentNode.removeChild(elem); 158 | if(elem.clearAttributes) elem.clearAttributes(); 159 | }; 160 | 161 | $.clean = function(elem) { 162 | for(var ch = elem.childNodes, i=0; i < ch.length; i++) { 163 | $.destroy(ch[i]); 164 | } 165 | }; 166 | 167 | $.addEvent = function(obj, type, fn) { 168 | if (obj.addEventListener) 169 | obj.addEventListener(type, fn, false); 170 | else 171 | obj.attachEvent('on' + type, fn); 172 | }; 173 | 174 | $.hasClass = function(obj, klass) { 175 | return (' ' + obj.className + ' ').indexOf(' ' + klass + ' ') > -1; 176 | }; 177 | 178 | $.addClass = function(obj, klass) { 179 | if(!$.hasClass(obj, klass)) obj.className = (obj.className + " " + klass); 180 | }; 181 | 182 | $.removeClass = function(obj, klass) { 183 | obj.className = obj.className.replace(new RegExp('(^|\\s)' + klass + '(?:\\s|$)'), '$1'); 184 | }; 185 | 186 | $.getPos = function(elem) { 187 | if(elem.getBoundingClientRect) { 188 | var bound = elem.getBoundingClientRect(), html = elem.ownerDocument.documentElement; 189 | return { 190 | x: bound.left + html.scrollLeft - html.clientLeft, 191 | y: bound.top + html.scrollTop - html.clientTop 192 | }; 193 | } 194 | 195 | var offset = getOffsets(elem); 196 | var scroll = getScrolls(elem); 197 | 198 | return {x: offset.x - scroll.x, y: offset.y - scroll.y}; 199 | 200 | function getOffsets(elem) { 201 | var position = { x: 0, y: 0 }; 202 | while (elem && !isBody(elem)){ 203 | position.x += elem.offsetLeft; 204 | position.y += elem.offsetTop; 205 | elem = elem.offsetParent; 206 | } 207 | return position; 208 | } 209 | 210 | function getScrolls(elem){ 211 | var position = {x: 0, y: 0}; 212 | while (elem && !isBody(elem)){ 213 | position.x += elem.scrollLeft; 214 | position.y += elem.scrollTop; 215 | elem = elem.parentNode; 216 | } 217 | return position; 218 | } 219 | 220 | function isBody(element){ 221 | return (/^(?:body|html)$/i).test(element.tagName); 222 | } 223 | }; 224 | 225 | var Class = function(properties){ 226 | properties = properties || {}; 227 | var klass = function(){ 228 | for (var key in this){ 229 | if (typeof this[key] != 'function') this[key] = $.unlink(this[key]); 230 | } 231 | this.constructor = klass; 232 | if (Class.prototyping) return this; 233 | var instance = (this.initialize) ? this.initialize.apply(this, arguments) : this; 234 | return instance; 235 | }; 236 | 237 | for (var mutator in Class.Mutators){ 238 | if (!properties[mutator]) continue; 239 | properties = Class.Mutators[mutator](properties, properties[mutator]); 240 | delete properties[mutator]; 241 | } 242 | 243 | $.extend(klass, this); 244 | klass.constructor = Class; 245 | klass.prototype = properties; 246 | return klass; 247 | }; 248 | 249 | Class.Mutators = { 250 | 251 | Implements: function(self, klasses){ 252 | $.each($.splat(klasses), function(klass){ 253 | Class.prototying = klass; 254 | var instance = (typeof klass == 'function')? new klass : klass; 255 | for(var prop in instance) { 256 | if(!(prop in self)) { 257 | self[prop] = instance[prop]; 258 | } 259 | } 260 | delete Class.prototyping; 261 | }); 262 | return self; 263 | } 264 | 265 | }; 266 | 267 | $.extend(Class, { 268 | 269 | inherit: function(object, properties){ 270 | var caller = arguments.callee.caller; 271 | for (var key in properties){ 272 | var override = properties[key]; 273 | var previous = object[key]; 274 | var type = $.type(override); 275 | if (previous && type == 'function'){ 276 | if (override != previous){ 277 | if (caller){ 278 | override.__parent = previous; 279 | object[key] = override; 280 | } else { 281 | Class.override(object, key, override); 282 | } 283 | } 284 | } else if(type == 'object'){ 285 | object[key] = $.merge(previous, override); 286 | } else { 287 | object[key] = override; 288 | } 289 | } 290 | 291 | if (caller) object.parent = function(){ 292 | return arguments.callee.caller.__parent.apply(this, arguments); 293 | }; 294 | 295 | return object; 296 | }, 297 | 298 | override: function(object, name, method){ 299 | var parent = Class.prototyping; 300 | if (parent && object[name] != parent[name]) parent = null; 301 | var override = function(){ 302 | var previous = this.parent; 303 | this.parent = parent ? parent[name] : object[name]; 304 | var value = method.apply(this, arguments); 305 | this.parent = previous; 306 | return value; 307 | }; 308 | object[name] = override; 309 | } 310 | 311 | }); 312 | 313 | 314 | Class.prototype.implement = function(){ 315 | var proto = this.prototype; 316 | $.each(Array.prototype.slice.call(arguments || []), function(properties){ 317 | Class.inherit(proto, properties); 318 | }); 319 | return this; 320 | }; 321 | 322 | var Event = { 323 | getPos: function(e, win) { 324 | // get mouse position 325 | win = win || window; 326 | e = e || win.event; 327 | var doc = win.document; 328 | doc = doc.html || doc.body; 329 | var page = { 330 | x: e.pageX || e.clientX + doc.scrollLeft, 331 | y: e.pageY || e.clientY + doc.scrollTop 332 | }; 333 | return page; 334 | } 335 | }; 336 | 337 | /* 338 | Object: TM.Util 339 | 340 | Some common JSON tree manipulation methods. 341 | */ 342 | TM.Util = { 343 | 344 | /* 345 | Method: prune 346 | 347 | Clears all tree nodes having depth greater than maxLevel. 348 | 349 | Parameters: 350 | 351 | tree - A JSON tree object. For more information please see . 352 | maxLevel - An integer specifying the maximum level allowed for this tree. All nodes having depth greater than max level will be deleted. 353 | 354 | */ 355 | prune: function(tree, maxLevel) { 356 | this.each(tree, function(elem, i) { 357 | if(i == maxLevel && elem.children) { 358 | delete elem.children; 359 | elem.children = []; 360 | } 361 | }); 362 | }, 363 | 364 | /* 365 | Method: getParent 366 | 367 | Returns the parent node of the node having _id_ as id. 368 | 369 | Parameters: 370 | 371 | tree - A JSON tree object. See also . 372 | id - The _id_ of the child node whose parent will be returned. 373 | 374 | Returns: 375 | 376 | A tree JSON node if any, or false otherwise. 377 | 378 | */ 379 | getParent: function(tree, id) { 380 | if(tree.id == id) return false; 381 | var ch = tree.children; 382 | if(ch && ch.length > 0) { 383 | for(var i=0; i. 403 | id - A node *unique* identifier. 404 | 405 | Returns: 406 | 407 | A subtree having a root node matching the given id. Returns null if no subtree matching the id is found. 408 | 409 | */ 410 | getSubtree: function(tree, id) { 411 | if(tree.id == id) return tree; 412 | for(var i=0, ch=tree.children; i. 427 | maxLevel - _optional_ A subtree's max level. 428 | 429 | Returns: 430 | 431 | An array having objects with two properties. 432 | 433 | - The _node_ property contains the leaf node. 434 | - The _level_ property specifies the depth of the node. 435 | 436 | */ 437 | getLeaves: function (node, maxLevel) { 438 | var leaves = [], levelsToShow = maxLevel || Number.MAX_VALUE; 439 | this.each(node, function(elem, i) { 440 | if(i < levelsToShow && 441 | (!elem.children || elem.children.length == 0 )) { 442 | leaves.push({ 443 | 'node':elem, 444 | 'level':levelsToShow - i 445 | }); 446 | } 447 | }); 448 | return leaves; 449 | }, 450 | 451 | 452 | /* 453 | Method: eachLevel 454 | 455 | Iterates on tree nodes with relative depth less or equal than a specified level. 456 | 457 | Parameters: 458 | 459 | tree - A JSON tree or subtree. See also . 460 | initLevel - An integer specifying the initial relative level. Usually zero. 461 | toLevel - An integer specifying a top level. This method will iterate only through nodes with depth less than or equal this number. 462 | action - A function that receives a node and an integer specifying the actual level of the node. 463 | 464 | Example: 465 | (start code js) 466 | TM.Util.eachLevel(tree, 0, 3, function(node, depth) { 467 | alert(node.name + ' ' + depth); 468 | }); 469 | (end code) 470 | */ 471 | eachLevel: function(tree, initLevel, toLevel, action) { 472 | if(initLevel <= toLevel) { 473 | action(tree, initLevel); 474 | for(var i=0, ch = tree.children; i. 488 | action - A function that receives a node. 489 | 490 | Example: 491 | (start code js) 492 | TM.Util.each(tree, function(node) { 493 | alert(node.name); 494 | }); 495 | (end code) 496 | 497 | */ 498 | each: function(tree, action) { 499 | this.eachLevel(tree, 0, Number.MAX_VALUE, action); 500 | }, 501 | 502 | /* 503 | Method: loadSubtrees 504 | 505 | Appends subtrees to leaves by requesting new subtrees 506 | with the _request_ method. 507 | 508 | Parameters: 509 | 510 | tree - A JSON tree node. . 511 | controller - An object that implements a request method. 512 | 513 | Example: 514 | (start code js) 515 | TM.Util.loadSubtrees(leafNode, { 516 | request: function(nodeId, level, onComplete) { 517 | //Pseudo-code to make an ajax request for a new subtree 518 | // that has as root id _nodeId_ and depth _level_ ... 519 | Ajax.request({ 520 | 'url': 'http://subtreerequesturl/', 521 | 522 | onSuccess: function(json) { 523 | onComplete.onComplete(nodeId, json); 524 | } 525 | }); 526 | } 527 | }); 528 | (end code) 529 | */ 530 | loadSubtrees: function(tree, controller) { 531 | var maxLevel = controller.request && controller.levelsToShow; 532 | var leaves = this.getLeaves(tree, maxLevel), 533 | len = leaves.length, 534 | selectedNode = {}; 535 | if(len == 0) controller.onComplete(); 536 | for(var i=0, counter=0; i based visualizations , , * 594 | 595 | - _onCreateLabel(domElement, node)_ This method receives the label dom element as first parameter, and the corresponding as second parameter. This method will only be called on label creation. Note that a is a node from the input tree/graph you provided to the visualization. If you want to know more about what kind of JSON tree/graph format is used to feed the visualizations, you can take a look at . This method proves useful when adding events to the labels used by the JIT. 596 | - _onPlaceLabel(domElement, node)_ This method receives the label dom element as first parameter and the corresponding as second parameter. This method is called each time a label has been placed on the visualization, and thus it allows you to update the labels properties, such as size or position. Note that onPlaceLabel will be triggered after updating the labels positions. That means that, for example, the left and top css properties are already updated to match the nodes positions. 597 | - _onBeforePlotNode(node)_ This method is triggered right before plotting a given node. The _node_ parameter is the to be plotted. 598 | This method is useful for changing a node style right before plotting it. 599 | - _onAfterPlotNode(node)_ This method is triggered right after plotting a given node. The _node_ parameter is the plotted. 600 | - _onBeforePlotLine(adj)_ This method is triggered right before plotting an edge. The _adj_ parameter is a object. 601 | This method is useful for adding some styles to a particular edge before being plotted. 602 | - _onAfterPlotLine(adj)_ This method is triggered right after plotting an edge. The _adj_ parameter is the plotted. 603 | 604 | *Used in (Treemap) and DOM based visualizations* 605 | 606 | - _onCreateElement(content, node, isLeaf, elem1, elem2)_ This method is called on each newly created node. 607 | 608 | Parameters: 609 | content - The div wrapper element with _content_ className. 610 | node - The corresponding JSON tree node (See also ). 611 | isLeaf - Whether is a leaf or inner node. If the node's an inner tree node, elem1 and elem2 will become the _head_ and _body_ div elements respectively. 612 | If the node's a _leaf_, then elem1 will become the div leaf element. 613 | 614 | - _onDestroyElement(content, node, isLeaf, elem1, elem2)_ This method is called before collecting each node. Takes the same parameters as onCreateElement. 615 | 616 | *Used in and * 617 | 618 | - _request(nodeId, level, onComplete)_ This method is used for buffering information into the visualization. When clicking on an empty node, 619 | the visualization will make a request for this node's subtrees, specifying a given level for this subtree (defined by _levelsToShow_). Once the request is completed, the _onComplete_ 620 | object should be called with the given result. 621 | 622 | */ 623 | Options.Controller = { 624 | $extend: true, 625 | 626 | onBeforeCompute: $.empty, 627 | onAfterCompute: $.empty, 628 | onCreateLabel: $.empty, 629 | onPlaceLabel: $.empty, 630 | onComplete: $.empty, 631 | onBeforePlotLine:$.empty, 632 | onAfterPlotLine: $.empty, 633 | onBeforePlotNode:$.empty, 634 | onAfterPlotNode: $.empty, 635 | onCreateElement: $.empty, 636 | onDestroyElement:$.empty, 637 | request: false 638 | }; 639 | 640 | /* 641 | Object: Options.Tips 642 | 643 | Options for Tips 644 | 645 | Description: 646 | 647 | Options for Tool Tips. 648 | 649 | Implemented by: 650 | 651 | 652 | 653 | These configuration parameters are currently used by . 654 | 655 | 656 | - _enable_ If *true*, a tooltip will be shown when a node is hovered. The tooltip is a div DOM element having "tip" as CSS class. Default's *false*. 657 | - _offsetX_ An offset added to the current tooltip x-position (which is the same as the current mouse position). Default's 20. 658 | - _offsetY_ An offset added to the current tooltip y-position (which is the same as the current mouse position). Default's 20. 659 | - _onShow(tooltip, node, isLeaf, domElement)_ Implement this method to change the HTML content of the tooltip when hovering a node. 660 | 661 | Parameters: 662 | tooltip - The tooltip div element. 663 | node - The corresponding JSON tree node (See also ). 664 | isLeaf - Whether is a leaf or inner node. 665 | domElement - The current hovered DOM element. 666 | 667 | */ 668 | Options.Tips = { 669 | $extend: false, 670 | 671 | enable: false, // TODO(nico) change allow for enable 672 | attachToDOM: true, 673 | attachToCanvas: false, 674 | offsetX: 20, 675 | offsetY: 20, 676 | onShow: $.empty 677 | }; 678 | 679 | /* 680 | * File: Extras.js 681 | * 682 | * Provides Extras such as Tips and Style Effects. 683 | * 684 | * Description: 685 | * 686 | * Provides the and classes and functions. 687 | * 688 | */ 689 | 690 | /* 691 | * Provides the initialization function for and implemented 692 | * by all main visualizations. 693 | * 694 | */ 695 | var Extras = { 696 | initializeExtras: function() { 697 | var tips = this.config.Tips; 698 | if(tips) { 699 | this.tips = new Tips(this); 700 | } 701 | } 702 | }; 703 | 704 | 705 | /* 706 | Class: Tips 707 | 708 | A class containing tip related functions. This class is used internally. 709 | 710 | Used by: 711 | 712 | , , , , , , 713 | 714 | See also: 715 | 716 | 717 | */ 718 | 719 | var Tips = new Class({ 720 | initialize: function(viz) { 721 | this.viz = viz; 722 | this.controller = this.config = viz.config; 723 | // add tooltip 724 | if(this.config.Tips.enable && document.body) { 725 | var tip = document.getElementById('_tooltip') || document.createElement('div'); 726 | tip.id = '_tooltip'; 727 | tip.className = 'tip'; 728 | var style = tip.style; 729 | style.position = 'absolute'; 730 | style.display = 'none'; 731 | style.zIndex = 13000; 732 | document.body.appendChild(tip); 733 | this.tip = tip; 734 | this.node = false; 735 | } 736 | }, 737 | 738 | attach: function(node, elem) { 739 | if(this.config.Tips.enable) { 740 | var that = this, cont = this.controller; 741 | $.addEvent(elem, 'mouseover', function(e){ 742 | cont.Tips.onShow(that.tip, node, elem); 743 | }); 744 | $.addEvent(elem, 'mouseout', function(e){ 745 | that.tip.style.display = 'none'; 746 | }); 747 | // Add mousemove event handler 748 | $.addEvent(elem, 'mousemove', function(e, win){ 749 | var pos = Event.getPos(e, win); 750 | that.setTooltipPosition(pos); 751 | }); 752 | } 753 | }, 754 | 755 | onClick: $.empty, 756 | onRightClick: $.empty, 757 | 758 | onMousemove: function(node, opt) { 759 | if(!node) { 760 | this.tip.style.display = 'none'; 761 | this.node = false; 762 | return; 763 | } 764 | if(!this.node || this.node.id != node.id) { 765 | this.node = node; 766 | this.config.Tips.onShow(this.tip, node); 767 | } 768 | this.setTooltipPosition(opt.position); 769 | }, 770 | 771 | setTooltipPosition: function(pos) { 772 | var tip = this.tip, style = tip.style, cont = this.config; 773 | style.display = ''; 774 | // get window dimensions 775 | win = { 776 | 'height': document.body.clientHeight, 777 | 'width': document.body.clientWidth 778 | }; 779 | // get tooltip dimensions 780 | var obj = { 781 | 'width': tip.offsetWidth, 782 | 'height': tip.offsetHeight 783 | }; 784 | // set tooltip position 785 | var x = cont.Tips.offsetX, y = cont.Tips.offsetY; 786 | style.top = ((pos.y + y + obj.height > win.height)? 787 | (pos.y - obj.height - y) : pos.y + y) + 'px'; 788 | style.left = ((pos.x + obj.width + x > win.width)? 789 | (pos.x - obj.width - x) : pos.x + x) + 'px'; 790 | } 791 | }); 792 | 793 | 794 | /* 795 | * File: Treemap.js 796 | * 797 | * Implements the class and other derived classes. 798 | * 799 | * Description: 800 | * 801 | * A Treemap is an information visualization technique, proven very useful when displaying large hierarchical structures on a constrained space. The idea behind a Treemap is to describe hierarchical relations as 'containment'. That means that if node B is child of node A, then B 'is contained' in A. 802 | * 803 | * Inspired by: 804 | * 805 | * Squarified Treemaps (Mark Bruls, Kees Huizing, and Jarke J. van Wijk) 806 | * 807 | * 808 | * 809 | * Tree visualization with tree-maps: 2-d space-filling approach (Ben Shneiderman) 810 | * 811 | * 812 | * 813 | * Disclaimer: 814 | * 815 | * This visualization was built from scratch, taking only these papers as inspiration, and only shares some features with the Treemap papers mentioned above. 816 | * 817 | */ 818 | 819 | /* 820 | Class: TM 821 | 822 | Abstract Treemap class. 823 | 824 | Implements: 825 | 826 | 827 | 828 | Implemented By: 829 | 830 | , and . 831 | 832 | Description: 833 | 834 | Implements layout and configuration options inherited by , and . 835 | 836 | All Treemap constructors take the same configuration object as parameter. 837 | 838 | Two special _data_ keys are read from the JSON tree structure loaded by to calculate 839 | node's color and dimensions. These properties are $.area (for nodes dimensions) and $.color. Both of these properties are floats. 840 | 841 | This means that the tree structure defined in should now look more like this 842 | 843 | (start code js) 844 | var json = { 845 | "id": "aUniqueIdentifier", 846 | "name": "usually a nodes name", 847 | "data": { 848 | "$.area": 33, //some float value 849 | "$.color": 36, //-optional- some float value 850 | "some key": "some value", 851 | "some other key": "some other value" 852 | }, 853 | "children": [ 'other nodes or empty' ] 854 | }; 855 | (end code) 856 | 857 | If you want to know more about JSON tree structures and the _data_ property please read . 858 | 859 | Configuration: 860 | 861 | *General* 862 | 863 | - _rootId_ The id of the div container where the Treemap will be injected. Default's 'infovis'. 864 | - _orientation_ For and only. The layout algorithm orientation. Possible values are 'h' or 'v'. 865 | - _levelsToShow_ Max depth of the plotted tree. Useful when using the request method. 866 | - _addLeftClickHandler_ Add a left click event handler to zoom in the Treemap view when clicking a node. Default's *false*. 867 | - _addRightClickHandler_ Add a right click event handler to zoom out the Treemap view. Default's *false*. 868 | - _selectPathOnHover_ If setted to *true* all nodes contained in the path between the hovered node and the root node will have an *in-path* CSS class. Default's *false*. 869 | 870 | *Nodes* 871 | 872 | There are two kinds of Treemap nodes. 873 | 874 | (see treemapnode.png) 875 | 876 | Inner nodes are nodes having children, like _Pearl Jam_. 877 | 878 | These nodes are represented by three div elements. A _content_ element, a _head_ element (where the title goes) and a _body_ element, where the children are laid out. 879 | 880 | (start code xml) 881 |
882 |
Pearl Jam
883 |
...other nodes here...
884 |
885 | (end code) 886 | 887 | Leaves are optionally colored nodes laying at the "bottom" of the tree. For example, _Yield_, _Vs._ and _Riot Act_ are leaves. 888 | 889 | These nodes are represented by two div elements. A _content_ element and a wrapped _leaf_ element 890 | 891 | (start code xml) 892 |
893 |
Yield
894 |
895 | (end code) 896 | 897 | There are some configuration properties regarding Treemap nodes 898 | 899 | - _titleHeight_ The height of the title (_head_) div container. Default's 13. 900 | - _offset_ The separation offset between the _content_ div element and its contained div(s). Default's 4. 901 | 902 | *Color* 903 | 904 | _Color_ is an object containing as properties 905 | 906 | - _enable_ If *true*, the algorithm will check for the JSON node data _$.color_ property to add some color to the Treemap leaves. 907 | This color is calculated by interpolating a node's $.color value range with a real RGB color range. 908 | By specifying min|maxValues for the $.color property and min|maxColorValues for the RGB counterparts, the visualization is able to 909 | interpolate color values and assign a proper color to the leaf node. Default's *false*. 910 | - _minValue_ The minimum value expected for the $.color value property. Used for interpolating. Default's -100. 911 | - _maxValue_ The maximum value expected for the $.color value property. Used for interpolating. Default's 100. 912 | - _minColorValue_ A three-element RGB array defining the color to be assigned to the _$.color_ having _minValue_ as value. Default's [255, 0, 50]. 913 | - _maxColorValue_ A three-element RGB array defining the color to be assigned to the _$.color_ having _maxValue_ as value. Default's [0, 255, 50]. 914 | 915 | *Tips* 916 | 917 | See . 918 | 919 | *Controller options* 920 | 921 | See . 922 | 923 | See also , and . 924 | 925 | 926 | */ 927 | TM.Base = $.extend({ 928 | layout: { 929 | orientation: "h", 930 | vertical: function() { 931 | return this.orientation == "v"; 932 | }, 933 | horizontal: function() { 934 | return this.orientation == "h"; 935 | }, 936 | change: function() { 937 | this.orientation = this.vertical()? "h" : "v"; 938 | } 939 | }, 940 | 941 | config: { 942 | orientation: "h", 943 | titleHeight: 13, 944 | rootId: 'infovis', 945 | offset:4, 946 | levelsToShow: 3, 947 | addLeftClickHandler: false, 948 | addRightClickHandler: false, 949 | selectPathOnHover: false, 950 | 951 | Tips: Options.Tips, 952 | 953 | Color: { 954 | enable: false, 955 | minValue: -100, 956 | maxValue: 100, 957 | minColorValue: [255, 0, 50], 958 | maxColorValue: [0, 255, 50] 959 | } 960 | }, 961 | 962 | 963 | initialize: function(controller) { 964 | this.tree = null; 965 | this.shownTree = null; 966 | this.controller = this.config = $.merge(Options.Controller, 967 | this.config, 968 | controller); 969 | this.rootId = this.config.rootId; 970 | this.layout.orientation = this.config.orientation; 971 | // add tips 972 | this.initializeExtras(); 973 | // purge 974 | var that = this; 975 | var fn = function() { 976 | that.empty(); 977 | if(window.CollectGarbage) window.CollectGarbage(); 978 | delete fn; 979 | }; 980 | if(window.addEventListener) { 981 | window.addEventListener('unload', fn, false); 982 | } else { 983 | window.attachEvent('onunload', fn); 984 | } 985 | }, 986 | 987 | /* 988 | Method: each 989 | 990 | Traverses head and leaf nodes applying a given function 991 | 992 | Parameters: 993 | 994 | f - A function that takes as parameters the same as the onCreateElement and onDestroyElement methods described in . 995 | */ 996 | each: function(f) { 997 | (function rec(elem) { 998 | if(!elem) return; 999 | var ch = elem.childNodes, len = ch.length; 1000 | if(len > 0) { 1001 | f.apply(this, [elem, len === 1, ch[0], ch[1]]); 1002 | } 1003 | if (len > 1) { 1004 | for(var chi = ch[1].childNodes, i=0; i 1030 | 1031 | Returns: 1032 | 1033 | A boolean value specifying if the node is a tree leaf or not. 1034 | 1035 | */ 1036 | leaf: function(tree) { 1037 | return tree.children == 0; 1038 | }, 1039 | 1040 | /* 1041 | Method: createBox 1042 | 1043 | Constructs the proper DOM layout from a json node. 1044 | 1045 | If the node's an _inner node_, 1046 | this method calls , and 1047 | to create the following HTML structure 1048 | 1049 | (start code xml) 1050 |
1051 |
[Node name]
1052 |
[Node's children]
1053 |
1054 | (end code) 1055 | 1056 | If the node's a leaf node, it creates the following structure 1057 | by calling , 1058 | 1059 | (start code xml) 1060 |
1061 |
[Node name]
1062 |
1063 | (end code) 1064 | 1065 | 1066 | Parameters: 1067 | 1068 | json - A JSON subtree. See also . 1069 | coord - A coordinates object specifying width, height, left and top style properties. 1070 | html - html to inject into the _body_ element if the node is an inner Tree node. 1071 | 1072 | Returns: 1073 | 1074 | The HTML structure described above. 1075 | 1076 | See also: 1077 | 1078 | , , , , . 1079 | 1080 | */ 1081 | createBox: function(json, coord, html) { 1082 | var box; 1083 | if(!this.leaf(json)) { 1084 | box = this.headBox(json, coord) + this.bodyBox(html, coord); 1085 | } else { 1086 | box = this.leafBox(json, coord); 1087 | } 1088 | return this.contentBox(json, coord, box); 1089 | }, 1090 | 1091 | /* 1092 | Method: plot 1093 | 1094 | Renders the Treemap. 1095 | 1096 | Parameters: 1097 | 1098 | json - A JSON tree structure preprocessed by some Treemap layout algorithm. 1099 | 1100 | Returns: 1101 | 1102 | The HTML to inject to the main visualization container. 1103 | 1104 | See also: 1105 | 1106 | . 1107 | 1108 | 1109 | */ 1110 | plot: function(json) { 1111 | var coord = json.coord, html = ""; 1112 | 1113 | if(this.leaf(json)) 1114 | return this.createBox(json, coord, null); 1115 | 1116 | for(var i=0, ch=json.children; i 1) { 1120 | html+= this.plot(chi); 1121 | } 1122 | } 1123 | return this.createBox(json, coord, html); 1124 | }, 1125 | 1126 | 1127 | /* 1128 | Method: headBox 1129 | 1130 | Creates the _head_ div dom element that usually contains the name of a parent JSON tree node. 1131 | 1132 | Parameters: 1133 | 1134 | json - A JSON subtree. See also . 1135 | coord - width and height base coordinate object. 1136 | 1137 | Returns: 1138 | 1139 | A new _head_ div dom element that has _head_ as class name. 1140 | 1141 | See also: 1142 | 1143 | . 1144 | 1145 | */ 1146 | headBox: function(json, coord) { 1147 | var config = this.config, offst = config.offset; 1148 | var c = { 1149 | 'height': config.titleHeight + "px", 1150 | 'width': (coord.width - offst) + "px", 1151 | 'left': offst / 2 + "px" 1152 | }; 1153 | return "
" 1154 | + json.name + "
"; 1155 | }, 1156 | 1157 | /* 1158 | Method: bodyBox 1159 | 1160 | Creates the _body_ div dom element that usually contains a subtree dom element layout. 1161 | 1162 | Parameters: 1163 | 1164 | html - html that should be contained in the body html. 1165 | coord - width and height base coordinate object. 1166 | 1167 | Returns: 1168 | 1169 | A new _body_ div dom element that has _body_ as class name. 1170 | 1171 | See also: 1172 | 1173 | . 1174 | 1175 | */ 1176 | bodyBox: function(html, coord) { 1177 | var config = this.config, 1178 | th = config.titleHeight, 1179 | offst = config.offset; 1180 | var c = { 1181 | 'width': (coord.width - offst) + "px", 1182 | 'height':(coord.height - offst - th) + "px", 1183 | 'top': (th + offst / 2) + "px", 1184 | 'left': (offst / 2) + "px" 1185 | }; 1186 | return "
" + html + "
"; 1188 | }, 1189 | 1190 | 1191 | 1192 | /* 1193 | Method: contentBox 1194 | 1195 | Creates the _content_ div dom element that usually contains a _leaf_ div dom element or _head_ and _body_ div dom elements. 1196 | 1197 | Parameters: 1198 | 1199 | json - A JSON node. See also . 1200 | coord - An object containing width, height, left and top coordinates. 1201 | html - input html wrapped by this tag. 1202 | 1203 | Returns: 1204 | 1205 | A new _content_ div dom element that has _content_ as class name. 1206 | 1207 | See also: 1208 | 1209 | . 1210 | 1211 | */ 1212 | contentBox: function(json, coord, html) { 1213 | var c = {}; 1214 | for(var i in coord) c[i] = coord[i] + "px"; 1215 | return "
" + html + "
"; 1217 | }, 1218 | 1219 | 1220 | /* 1221 | Method: leafBox 1222 | 1223 | Creates the _leaf_ div dom element that usually contains nothing else. 1224 | 1225 | Parameters: 1226 | 1227 | json - A JSON subtree. See also . 1228 | coord - base with and height coordinate object. 1229 | 1230 | Returns: 1231 | 1232 | A new _leaf_ div dom element having _leaf_ as class name. 1233 | 1234 | See also: 1235 | 1236 | . 1237 | 1238 | 1239 | */ 1240 | leafBox: function(json, coord) { 1241 | var config = this.config; 1242 | var backgroundColor = config.Color.enable && this.setColor(json), 1243 | offst = config.offset, 1244 | width = coord.width - offst, 1245 | height = coord.height - offst; 1246 | var c = { 1247 | 'top': (offst / 2) + "px", 1248 | 'height':height + "px", 1249 | 'width': width + "px", 1250 | 'left': (offst / 2) + "px" 1251 | }; 1252 | if(backgroundColor) c['background-color'] = backgroundColor; 1253 | return "
" 1254 | + json.name + "
"; 1255 | }, 1256 | 1257 | 1258 | /* 1259 | Method: setColor 1260 | 1261 | Calculates an hexa color string based on the _$.color_ data node property. 1262 | 1263 | This method is called by to assign an hexadecimal color to each leaf node. 1264 | 1265 | This color is calculated by making a linear interpolation between _$.color_ max and min values and 1266 | RGB max and min values so that 1267 | 1268 | > hex = (maxColorValue - minColorValue) / (maxValue - minValue) * (x - minValue) + minColorValue 1269 | 1270 | where _x_ range is [minValue, maxValue] and 1271 | 1272 | - _minValue_ 1273 | - _maxValue_ 1274 | - _minColorValue_ 1275 | - _maxColorValue_ 1276 | 1277 | are defined in the configuration object. 1278 | 1279 | This method is called by iif _Color.enable_ is setted to _true_. 1280 | 1281 | Sometimes linear interpolation for coloring is just not enough. In that case you can re-implement this 1282 | method so that it fits your coloring needs. 1283 | 1284 | Some people might find useful to implement their own coloring interpolation method and to assign the resulting hex string 1285 | to the _$.color_ property. In that case we could re-implement the method like this 1286 | 1287 | (start code js) 1288 | //TM.Strip, TM.SliceAndDice also work 1289 | TM.Squarified.implement({ 1290 | 'setColor': function(json) { 1291 | return json.data.$color; 1292 | } 1293 | }); 1294 | (end code) 1295 | 1296 | So that it returns the previously assigned hex string. 1297 | 1298 | Parameters: 1299 | 1300 | json - A JSON tree node. 1301 | 1302 | Returns: 1303 | 1304 | A String that represents a color in hex value. 1305 | 1306 | */ 1307 | setColor: function(json) { 1308 | var c = this.config.Color, 1309 | maxcv = c.maxColorValue, 1310 | mincv = c.minColorValue, 1311 | maxv = c.maxValue, 1312 | minv = c.minValue, 1313 | diff = maxv - minv, 1314 | x = (json.data.$color - 0); 1315 | // linear interpolation 1316 | var comp = function(i, x) { 1317 | return Math.round((((maxcv[i] - mincv[i]) / diff) * (x - minv) + mincv[i])); 1318 | }; 1319 | 1320 | return $.rgbToHex([ comp(0, x), comp(1, x), comp(2, x) ]); 1321 | }, 1322 | 1323 | /* 1324 | Method: enter 1325 | 1326 | Sets the _elem_ parameter as root and performs the layout. 1327 | 1328 | Parameters: 1329 | 1330 | elem - A JSON Tree node. See also . 1331 | */ 1332 | enter: function(elem) { 1333 | this.view(elem.parentNode.id); 1334 | }, 1335 | 1336 | /* 1337 | Method: onLeftClick 1338 | 1339 | Sets the _elem_ parameter as root and performs the layout. 1340 | This method is called when _addLeftClickHandler_ is *true* and a 1341 | node is left-clicked. You can override this method to add some custom behavior 1342 | when the node is left clicked though. 1343 | 1344 | An Example for overriding this method could be 1345 | (start code js) 1346 | //TM.Strip or TM.SliceAndDice also work 1347 | TM.Squarified.implement({ 1348 | 'onLeftClick': function(elem) { 1349 | //some custom code... 1350 | } 1351 | }); 1352 | (end code) 1353 | 1354 | 1355 | Parameters: 1356 | 1357 | elem - A JSON Tree node. See also . 1358 | 1359 | See also: 1360 | 1361 | 1362 | */ 1363 | onLeftClick: function(elem) { 1364 | this.enter(elem); 1365 | }, 1366 | 1367 | /* 1368 | Method: out 1369 | 1370 | Sets the _parent_ node of the currently shown subtree as root and performs the layout. 1371 | 1372 | */ 1373 | out: function() { 1374 | var parent = TM.Util.getParent(this.tree, this.shownTree.id); 1375 | if(parent) { 1376 | if(this.controller.request) 1377 | TM.Util.prune(parent, this.config.levelsToShow); 1378 | this.view(parent.id); 1379 | } 1380 | }, 1381 | 1382 | /* 1383 | Method: onRightClick 1384 | 1385 | Sets the _parent_ node of the currently shown subtree as root and performs the layout. 1386 | This method is called when _addRightClickHandler_ is *true* and a 1387 | node is right-clicked. You can override this method to add some custom behavior 1388 | when the node is right-clicked though. 1389 | 1390 | An Example for overriding this method could be 1391 | (start code js) 1392 | //TM.Strip or TM.SliceAndDice also work 1393 | TM.Squarified.implement({ 1394 | 'onRightClick': function() { 1395 | //some custom code... 1396 | } 1397 | }); 1398 | (end code) 1399 | 1400 | See also: 1401 | 1402 | 1403 | 1404 | */ 1405 | onRightClick: function() { 1406 | this.out(); 1407 | }, 1408 | 1409 | /* 1410 | Method: view 1411 | 1412 | Sets the root of the treemap to the specified node id and performs the layout. 1413 | 1414 | Parameters: 1415 | 1416 | id - A node identifier 1417 | */ 1418 | view: function(id) { 1419 | var config = this.config, that = this; 1420 | var post = { 1421 | onComplete: function() { 1422 | that.loadTree(id); 1423 | $(config.rootId).focus(); 1424 | } 1425 | }; 1426 | 1427 | if (this.controller.request) { 1428 | var TUtil = TM.Util; 1429 | TUtil.loadSubtrees(TUtil.getSubtree(this.tree, id), 1430 | $.merge(this.controller, post)); 1431 | } else { 1432 | post.onComplete(); 1433 | } 1434 | }, 1435 | 1436 | /* 1437 | Method: resetPath 1438 | 1439 | Sets an 'in-path' className for _leaf_ and _head_ elements which belong to the path between the given tree node 1440 | and the visualization's root node. 1441 | 1442 | Parameters: 1443 | 1444 | tree - A JSON tree node. See also . 1445 | */ 1446 | resetPath: function(tree) { 1447 | var root = this.rootId, previous = this.resetPath.previous; 1448 | this.resetPath.previous = tree || false; 1449 | function getParent(c) { 1450 | var p = c.parentNode; 1451 | return p && (p.id != root) && p; 1452 | }; 1453 | function toggleInPath(elem, remove) { 1454 | if(elem) { 1455 | var container = $(elem.id); 1456 | if(container) { 1457 | var parent = getParent(container); 1458 | while(parent) { 1459 | elem = parent.childNodes[0]; 1460 | if($.hasClass(elem, 'in-path')) { 1461 | if(remove == undefined || !!remove) $.removeClass(elem, 'in-path'); 1462 | } else { 1463 | if(!remove) $.addClass(elem, 'in-path'); 1464 | } 1465 | parent = getParent(parent); 1466 | } 1467 | } 1468 | } 1469 | }; 1470 | toggleInPath(previous, true); 1471 | toggleInPath(tree, false); 1472 | }, 1473 | 1474 | 1475 | /* 1476 | Method: initializeElements 1477 | 1478 | Traverses the DOM tree applying the onCreateElement method. 1479 | 1480 | The onCreateElement controller method should attach events and add some behavior to the DOM element 1481 | node created. *By default, the Treemap wont add any event to its elements.* 1482 | */ 1483 | initializeElements: function() { 1484 | var cont = this.controller, that = this; 1485 | var ff = $.lambda(false); 1486 | this.each(function(content, isLeaf, elem1, elem2) { 1487 | var tree = TM.Util.getSubtree(that.tree, content.id); 1488 | cont.onCreateElement(content, tree, isLeaf, elem1, elem2); 1489 | 1490 | // eliminate context menu when right clicking 1491 | if(cont.addRightClickHandler) elem1.oncontextmenu = ff; 1492 | 1493 | // add click handlers 1494 | if(cont.addLeftClickHandler || cont.addRightClickHandler) { 1495 | $.addEvent(elem1, 'mouseup', function(e) { 1496 | var rightClick = (e.which == 3 || e.button == 2); 1497 | if (rightClick) { 1498 | if(cont.addRightClickHandler) that.onRightClick(); 1499 | } 1500 | else { 1501 | if(cont.addLeftClickHandler) that.onLeftClick(elem1); 1502 | } 1503 | 1504 | // prevent default 1505 | if (e.preventDefault) 1506 | e.preventDefault(); 1507 | else 1508 | e.returnValue = false; 1509 | }); 1510 | } 1511 | 1512 | // add path selection on hovering nodes 1513 | if(cont.selectPathOnHover) { 1514 | $.addEvent(elem1, 'mouseover', function(e){ 1515 | if(cont.selectPathOnHover) { 1516 | if (isLeaf) { 1517 | $.addClass(elem1, 'over-leaf'); 1518 | } 1519 | else { 1520 | $.addClass(elem1, 'over-head'); 1521 | $.addClass(content, 'over-content'); 1522 | } 1523 | if (content.id) 1524 | that.resetPath(tree); 1525 | } 1526 | }); 1527 | 1528 | $.addEvent(elem1, 'mouseout', function(e){ 1529 | if(cont.selectPathOnHover) { 1530 | if (isLeaf) { 1531 | $.removeClass(elem1, 'over-leaf'); 1532 | } 1533 | else { 1534 | $.removeClass(elem1, 'over-head'); 1535 | $.removeClass(content, 'over-content'); 1536 | } 1537 | that.resetPath(); 1538 | } 1539 | }); 1540 | } 1541 | 1542 | // attach tips 1543 | that.tips.attach(tree, elem1); 1544 | }); 1545 | }, 1546 | 1547 | /* 1548 | Method: destroyElements 1549 | 1550 | Traverses the tree applying the onDestroyElement method. 1551 | 1552 | The onDestroyElement controller method should detach events and garbage collect the element. 1553 | *By default, the Treemap adds some garbage collect facilities for IE.* 1554 | */ 1555 | destroyElements: function() { 1556 | if(this.controller.onDestroyElement != $.empty) { 1557 | var cont = this.controller, that = this; 1558 | this.each(function(content, isLeaf, elem1, elem2) { 1559 | cont.onDestroyElement(content, TM.Util.getSubtree(that.tree, content.id), isLeaf, elem1, elem2); 1560 | }); 1561 | } 1562 | }, 1563 | 1564 | /* 1565 | Method: empty 1566 | 1567 | Empties the Treemap container (trying also to garbage collect things). 1568 | */ 1569 | empty: function() { 1570 | this.destroyElements(); 1571 | $.clean($(this.rootId)); 1572 | }, 1573 | 1574 | /* 1575 | Method: loadTree 1576 | 1577 | Loads the subtree specified by _id_ and plots it on the layout container. 1578 | 1579 | Parameters: 1580 | 1581 | id - A subtree id. 1582 | */ 1583 | loadTree: function(id) { 1584 | this.empty(); 1585 | this.loadJSON(TM.Util.getSubtree(this.tree, id)); 1586 | } 1587 | 1588 | }, Extras); 1589 | 1590 | /* 1591 | Class: TM.SliceAndDice 1592 | 1593 | A JavaScript implementation of the Slice and Dice Treemap algorithm. 1594 | 1595 | The constructor takes an _optional_ configuration object described in . 1596 | 1597 | This visualization (as all other Treemap visualizations) is fed with JSON Tree structures. 1598 | 1599 | The _$.area_ node data key is required for calculating rectangles dimensions. 1600 | 1601 | The _$.color_ node data key is required if _Color_ _allow_ is *true* and is used for calculating 1602 | leaves colors. 1603 | 1604 | Extends: 1605 | , 1606 | 1607 | Parameters: 1608 | 1609 | config - Configuration defined in . 1610 | 1611 | Example: 1612 | 1613 | 1614 | Here's a way of instanciating the will all its _optional_ configuration features 1615 | 1616 | (start code js) 1617 | 1618 | var tm = new TM.SliceAndDice({ 1619 | orientation: "h", 1620 | titleHeight: 13, 1621 | rootId: 'infovis', 1622 | offset:4, 1623 | levelsToShow: 3, 1624 | addLeftClickHandler: false, 1625 | addRightClickHandler: false, 1626 | selectPathOnHover: false, 1627 | 1628 | Color: { 1629 | enable: false, 1630 | minValue: -100, 1631 | maxValue: 100, 1632 | minColorValue: [255, 0, 50], 1633 | maxColorValue: [0, 255, 50] 1634 | }, 1635 | 1636 | Tips: { 1637 | enable: false, 1638 | offsetX; 20, 1639 | offsetY: 20, 1640 | onShow: function(tooltip, node, domElement) {} 1641 | }, 1642 | 1643 | onBeforeCompute: function(node) { 1644 | //Some stuff on before compute... 1645 | }, 1646 | onAfterCompute: function() { 1647 | //Some stuff on after compute... 1648 | }, 1649 | onCreateElement: function(content, node, isLeaf, head, body) { 1650 | //Some stuff onCreateElement 1651 | }, 1652 | onDestroyElement: function(content, node, isLeaf, head, body) { 1653 | //Some stuff onDestroyElement 1654 | }, 1655 | request: false 1656 | }); 1657 | tm.loadJSON(json); 1658 | 1659 | (end code) 1660 | 1661 | */ 1662 | TM.SliceAndDice = new Class({ 1663 | Implements: TM.Base, 1664 | /* 1665 | Method: loadJSON 1666 | 1667 | Loads the specified JSON tree and lays it on the main container. 1668 | 1669 | Parameters: 1670 | 1671 | json - A JSON Tree. See also . 1672 | */ 1673 | loadJSON: function (json) { 1674 | this.controller.onBeforeCompute(json); 1675 | var container = $(this.rootId), 1676 | config = this.config, 1677 | width = container.offsetWidth, 1678 | height = container.offsetHeight; 1679 | 1680 | var p = { 1681 | 'coord': { 1682 | 'top': 0, 1683 | 'left': 0, 1684 | 'width': width, 1685 | 'height': height + config.titleHeight + config.offset 1686 | } 1687 | }; 1688 | 1689 | if(this.tree == null) this.tree = json; 1690 | this.shownTree = json; 1691 | this.compute(p, json, this.layout.orientation); 1692 | container.innerHTML = this.plot(json); 1693 | this.initializeElements(); 1694 | this.controller.onAfterCompute(json); 1695 | }, 1696 | 1697 | /* 1698 | Method: compute 1699 | 1700 | Called by loadJSON to calculate recursively all node positions and lay out the tree. 1701 | 1702 | Parameters: 1703 | 1704 | par - The parent node of the json subtree. 1705 | json - A JSON subtree. See also . 1706 | orientation - The current orientation. This value is switched recursively. 1707 | */ 1708 | compute: function(par, json, orientation) { 1709 | var config = this.config, 1710 | coord = par.coord, 1711 | offst = config.offset, 1712 | width = coord.width - offst, 1713 | height = coord.height - offst - config.titleHeight, 1714 | pdata = par.data, 1715 | fact = (pdata && ("$.area" in pdata))? json.data.$area / pdata.$area : 1; 1716 | var otherSize, size, dim, pos, pos2; 1717 | 1718 | var horizontal = (orientation == "h"); 1719 | if(horizontal) { 1720 | orientation = 'v'; 1721 | otherSize = height; 1722 | size = Math.round(width * fact); 1723 | dim = 'height'; 1724 | pos = 'top'; 1725 | pos2 = 'left'; 1726 | } else { 1727 | orientation = 'h'; 1728 | otherSize = Math.round(height * fact); 1729 | size = width; 1730 | dim = 'width'; 1731 | pos = 'left'; 1732 | pos2 = 'top'; 1733 | } 1734 | json.coord = { 1735 | 'width':size, 1736 | 'height':otherSize, 1737 | 'top':0, 1738 | 'left':0 1739 | }; 1740 | var offsetSize = 0, tm = this; 1741 | $.each(json.children, function(elem){ 1742 | tm.compute(json, elem, orientation); 1743 | elem.coord[pos] = offsetSize; 1744 | elem.coord[pos2] = 0; 1745 | offsetSize += Math.floor(elem.coord[dim]); 1746 | }); 1747 | } 1748 | }); 1749 | 1750 | 1751 | /* 1752 | Class: TM.Area 1753 | 1754 | Abstract Treemap class containing methods that are common to 1755 | aspect ratio related algorithms such as and . 1756 | 1757 | Implemented by: 1758 | 1759 | , 1760 | */ 1761 | TM.Area = { 1762 | 1763 | /* 1764 | Method: loadJSON 1765 | 1766 | Loads the specified JSON tree and lays it on the main container. 1767 | 1768 | Parameters: 1769 | 1770 | json - A JSON tree. See also . 1771 | */ 1772 | loadJSON: function (json) { 1773 | this.controller.onBeforeCompute(json); 1774 | var container = $(this.rootId), 1775 | width = container.offsetWidth, 1776 | height = container.offsetHeight, 1777 | offst = this.config.offset, 1778 | offwdth = width - offst, 1779 | offhght = height - offst - this.config.titleHeight; 1780 | 1781 | json.coord = { 1782 | 'height': height, 1783 | 'width': width, 1784 | 'top': 0, 1785 | 'left': 0 1786 | }; 1787 | var coord = $.merge(json.coord, { 1788 | 'width': offwdth, 1789 | 'height': offhght 1790 | }); 1791 | 1792 | this.compute(json, coord); 1793 | container.innerHTML = this.plot(json); 1794 | if(this.tree == null) this.tree = json; 1795 | this.shownTree = json; 1796 | this.initializeElements(); 1797 | this.controller.onAfterCompute(json); 1798 | }, 1799 | 1800 | /* 1801 | Method: computeDim 1802 | 1803 | Computes dimensions and positions of a group of nodes 1804 | according to a custom layout row condition. 1805 | 1806 | Parameters: 1807 | 1808 | tail - An array of nodes. 1809 | initElem - An array of nodes (containing the initial node to be laid). 1810 | w - A fixed dimension where nodes will be layed out. 1811 | coord - A coordinates object specifying width, height, left and top style properties. 1812 | comp - A custom comparison function 1813 | */ 1814 | computeDim: function(tail, initElem, w, coord, comp) { 1815 | if(tail.length + initElem.length == 1) { 1816 | var l = (tail.length == 1)? tail : initElem; 1817 | this.layoutLast(l, w, coord); 1818 | return; 1819 | } 1820 | if(tail.length >= 2 && initElem.length == 0) { 1821 | initElem = [tail[0]]; 1822 | tail = tail.slice(1); 1823 | } 1824 | if(tail.length == 0) { 1825 | if(initElem.length > 0) this.layoutRow(initElem, w, coord); 1826 | return; 1827 | } 1828 | var c = tail[0]; 1829 | if(comp(initElem, w) >= comp([c].concat(initElem), w)) { 1830 | this.computeDim(tail.slice(1), initElem.concat([c]), w, coord, comp); 1831 | } else { 1832 | var newCoords = this.layoutRow(initElem, w, coord); 1833 | this.computeDim(tail, [], newCoords.dim, newCoords, comp); 1834 | } 1835 | }, 1836 | 1837 | 1838 | /* 1839 | Method: worstAspectRatio 1840 | 1841 | Calculates the worst aspect ratio of a group of rectangles. 1842 | 1843 | See also: 1844 | 1845 | 1846 | 1847 | Parameters: 1848 | 1849 | ch - An array of nodes. 1850 | w - The fixed dimension where rectangles are being laid out. 1851 | 1852 | Returns: 1853 | 1854 | The worst aspect ratio. 1855 | 1856 | 1857 | */ 1858 | worstAspectRatio: function(ch, w) { 1859 | if(!ch || ch.length == 0) return Number.MAX_VALUE; 1860 | var areaSum = 0, maxArea = 0, minArea = Number.MAX_VALUE; 1861 | for(var i=0; i area)? maxArea : area; 1866 | } 1867 | var sqw = w * w, sqAreaSum = areaSum * areaSum; 1868 | return Math.max(sqw * maxArea / sqAreaSum, 1869 | sqAreaSum / (sqw * minArea)); 1870 | }, 1871 | 1872 | /* 1873 | Method: avgAspectRatio 1874 | 1875 | Calculates the average aspect ratio of a group of rectangles. 1876 | 1877 | See also: 1878 | 1879 | 1880 | 1881 | Parameters: 1882 | 1883 | ch - An array of nodes. 1884 | w - The fixed dimension where rectangles are being laid out. 1885 | 1886 | Returns: 1887 | 1888 | The average aspect ratio. 1889 | 1890 | 1891 | */ 1892 | avgAspectRatio: function(ch, w) { 1893 | if(!ch || ch.length == 0) return Number.MAX_VALUE; 1894 | var arSum = 0; 1895 | for(var i=0; i h)? w / h : h / w; 1899 | } 1900 | return arSum / ch.length; 1901 | }, 1902 | 1903 | /* 1904 | layoutLast 1905 | 1906 | Performs the layout of the last computed sibling. 1907 | 1908 | Parameters: 1909 | 1910 | ch - An array of nodes. 1911 | w - A fixed dimension where nodes will be layed out. 1912 | coord - A coordinates object specifying width, height, left and top style properties. 1913 | */ 1914 | layoutLast: function(ch, w, coord) { 1915 | ch[0].coord = coord; 1916 | } 1917 | 1918 | }; 1919 | 1920 | 1921 | 1922 | 1923 | /* 1924 | Class: TM.Squarified 1925 | 1926 | A JavaScript implementation of the Squarified Treemap algorithm. 1927 | 1928 | The constructor takes an _optional_ configuration object described in . 1929 | 1930 | This visualization (as all other Treemap visualizations) is fed with JSON Tree structures. 1931 | 1932 | The _$.area_ node data key is required for calculating rectangles dimensions. 1933 | 1934 | The _$.color_ node data key is required if _Color_ _allow_ is *true* and is used for calculating 1935 | leaves colors. 1936 | 1937 | Extends: 1938 | , 1939 | 1940 | Parameters: 1941 | 1942 | config - Configuration defined in . 1943 | 1944 | Example: 1945 | 1946 | 1947 | Here's a way of instanciating the will all its _optional_ configuration features 1948 | 1949 | (start code js) 1950 | 1951 | var tm = new TM.Squarified({ 1952 | titleHeight: 13, 1953 | rootId: 'infovis', 1954 | offset:4, 1955 | levelsToShow: 3, 1956 | addLeftClickHandler: false, 1957 | addRightClickHandler: false, 1958 | selectPathOnHover: false, 1959 | 1960 | Color: { 1961 | enable: false, 1962 | minValue: -100, 1963 | maxValue: 100, 1964 | minColorValue: [255, 0, 50], 1965 | maxColorValue: [0, 255, 50] 1966 | }, 1967 | 1968 | Tips: { 1969 | enable: false, 1970 | offsetX: 20, 1971 | offsetY: 20, 1972 | onShow: function(tooltip, node, domElement) {} 1973 | }, 1974 | 1975 | onBeforeCompute: function(node) { 1976 | //Some stuff on before compute... 1977 | }, 1978 | onAfterCompute: function() { 1979 | //Some stuff on after compute... 1980 | }, 1981 | onCreateElement: function(content, node, isLeaf, head, body) { 1982 | //Some stuff onCreateElement 1983 | }, 1984 | onDestroyElement: function(content, node, isLeaf, head, body) { 1985 | //Some stuff onDestroyElement 1986 | }, 1987 | request: false 1988 | }); 1989 | 1990 | tm.loadJSON(json); 1991 | 1992 | (end code) 1993 | 1994 | */ 1995 | 1996 | TM.Squarified = new Class({ 1997 | Implements: [TM.Base, TM.Area], 1998 | 1999 | /* 2000 | Method: compute 2001 | 2002 | Called by loadJSON to calculate recursively all node positions and lay out the tree. 2003 | 2004 | Parameters: 2005 | 2006 | json - A JSON tree. See also . 2007 | coord - A coordinates object specifying width, height, left and top style properties. 2008 | */ 2009 | compute: function(json, coord) { 2010 | if (!(coord.width >= coord.height && this.layout.horizontal())) 2011 | this.layout.change(); 2012 | var ch = json.children, config = this.config; 2013 | if(ch.length > 0) { 2014 | this.processChildrenLayout(json, ch, coord); 2015 | for(var i=0; i= b._area); }); 2055 | var initElem = [ch[0]]; 2056 | var tail = ch.slice(1); 2057 | this.squarify(tail, initElem, minimumSideValue, coord); 2058 | }, 2059 | 2060 | /* 2061 | Method: squarify 2062 | 2063 | Performs an heuristic method to calculate div elements sizes in order to have a good aspect ratio. 2064 | 2065 | Parameters: 2066 | 2067 | tail - An array of nodes. 2068 | initElem - An array of nodes, containing the initial node to be laid out. 2069 | w - A fixed dimension where nodes will be laid out. 2070 | coord - A coordinates object specifying width, height, left and top style properties. 2071 | */ 2072 | squarify: function(tail, initElem, w, coord) { 2073 | this.computeDim(tail, initElem, w, coord, this.worstAspectRatio); 2074 | }, 2075 | 2076 | /* 2077 | Method: layoutRow 2078 | 2079 | Performs the layout of an array of nodes. 2080 | 2081 | Parameters: 2082 | 2083 | ch - An array of nodes. 2084 | w - A fixed dimension where nodes will be laid out. 2085 | coord - A coordinates object specifying width, height, left and top style properties. 2086 | */ 2087 | layoutRow: function(ch, w, coord) { 2088 | if(this.layout.horizontal()) { 2089 | return this.layoutV(ch, w, coord); 2090 | } else { 2091 | return this.layoutH(ch, w, coord); 2092 | } 2093 | }, 2094 | 2095 | layoutV: function(ch, w, coord) { 2096 | var totalArea = 0, rnd = Math.round; 2097 | $.each(ch, function(elem) { totalArea += elem._area; }); 2098 | var width = rnd(totalArea / w), top = 0; 2099 | for(var i=0; i constructor takes an _optional_ configuration object described in . 2156 | 2157 | This visualization (as all other Treemap visualizations) is fed with JSON Tree structures. 2158 | 2159 | The _$.area_ node data key is required for calculating rectangles dimensions. 2160 | 2161 | The _$.color_ node data key is required if _Color_ _allow_ is *true* and is used for calculating 2162 | leaves colors. 2163 | 2164 | Extends: 2165 | , 2166 | 2167 | Parameters: 2168 | 2169 | config - Configuration defined in . 2170 | 2171 | Example: 2172 | 2173 | 2174 | Here's a way of instanciating the will all its _optional_ configuration features 2175 | 2176 | (start code js) 2177 | 2178 | var tm = new TM.Strip({ 2179 | titleHeight: 13, 2180 | orientation: "h", 2181 | rootId: 'infovis', 2182 | offset:4, 2183 | levelsToShow: 3, 2184 | addLeftClickHandler: false, 2185 | addRightClickHandler: false, 2186 | selectPathOnHover: false, 2187 | 2188 | Color: { 2189 | enable: false, 2190 | minValue: -100, 2191 | maxValue: 100, 2192 | minColorValue: [255, 0, 50], 2193 | maxColorValue: [0, 255, 50] 2194 | }, 2195 | 2196 | Tips: { 2197 | enable: false, 2198 | offsetX: 20, 2199 | offsetY: 20, 2200 | onShow: function(tooltip, node, domElement) {} 2201 | }, 2202 | 2203 | onBeforeCompute: function(node) { 2204 | //Some stuff on before compute... 2205 | }, 2206 | onAfterCompute: function() { 2207 | //Some stuff on after compute... 2208 | }, 2209 | onCreateElement: function(content, node, isLeaf, head, body) { 2210 | //Some stuff onCreateElement 2211 | }, 2212 | onDestroyElement: function(content, node, isLeaf, head, body) { 2213 | //Some stuff onDestroyElement 2214 | }, 2215 | request: false 2216 | }); 2217 | 2218 | tm.loadJSON(json); 2219 | 2220 | (end code) 2221 | 2222 | */ 2223 | 2224 | TM.Strip = new Class({ 2225 | Implements: [TM.Base, TM.Area], 2226 | 2227 | /* 2228 | Method: compute 2229 | 2230 | Called by loadJSON to calculate recursively all node positions and lay out the tree. 2231 | 2232 | Parameters: 2233 | 2234 | json - A JSON subtree. See also . 2235 | coord - A coordinates object specifying width, height, left and top style properties. 2236 | */ 2237 | compute: function(json, coord) { 2238 | var ch = json.children, config = this.config; 2239 | if(ch.length > 0) { 2240 | this.processChildrenLayout(json, ch, coord); 2241 | for(var i=0; i 2 | 3 | 4 | 5 | Treemap - Squarified Treemap 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 |
23 | 24 | 25 | 26 |
27 |

28 | Squarified Treemap 29 |

30 | 31 | In this example a static JSON tree is loaded into a Squarified Treemap.

32 | Tooltips are manually added for each Treemap DOM node.

33 | Left click to set a node as root for the visualization.

34 | Right click to set the parent node as root for the visualization. 35 | 36 |
37 | 38 |
39 | 40 | 41 | 42 |
43 | 44 |
45 |
46 |
47 | 48 |
49 |
50 | 51 |
52 |
53 | 54 | 55 | -------------------------------------------------------------------------------- /Examples/example1.js: -------------------------------------------------------------------------------- 1 | var Log = { 2 | elem: false, 3 | write: function(text){ 4 | if (!this.elem) 5 | this.elem = document.getElementById('log'); 6 | this.elem.innerHTML = text; 7 | this.elem.style.left = (500 - this.elem.offsetWidth / 2) + 'px'; 8 | } 9 | }; 10 | 11 | function addEvent(obj, type, fn) { 12 | if (obj.addEventListener) obj.addEventListener(type, fn, false); 13 | else obj.attachEvent('on' + type, fn); 14 | }; 15 | 16 | 17 | function init(){ 18 | //init data 19 | var json = { 20 | children: [{ 21 | children: [{ 22 | children: [], 23 | data: { 24 | $area: "80", 25 | $color: "30", 26 | image: "http://images.amazon.com/images/P/B0007DAZW8.01.MZZZZZZZ.jpg" 27 | }, 28 | id: "056e4f3e-d505-4dad-8ec1-d04f521cbb56Human After All", 29 | name: "Human After All" 30 | }], 31 | data: { 32 | $area: 80 33 | }, 34 | id: "056e4f3e-d505-4dad-8ec1-d04f521cbb56a", 35 | name: "Daft Punk" 36 | }, { 37 | children: [{ 38 | children: [], 39 | data: { 40 | $area: "94", 41 | $color: "23", 42 | image: "http://cdn.last.fm/coverart/130x130/2393956.jpg" 43 | }, 44 | id: "f2fa5cf6-e0b0-4559-8641-e033f1a9e6fcClearing the Channel", 45 | name: "Clearing the Channel" 46 | }], 47 | data: { 48 | $area: 94 49 | }, 50 | id: "f2fa5cf6-e0b0-4559-8641-e033f1a9e6fca", 51 | name: "Sinch" 52 | }, { 53 | children: [{ 54 | children: [], 55 | data: { 56 | $area: "211", 57 | $color: "2", 58 | image: "http://cdn.last.fm/coverart/130x130/2564320-1193176651.jpg" 59 | }, 60 | id: "4bd95eea-b9f6-4d70-a36c-cfea77431553Music Bank", 61 | name: "Music Bank" 62 | }], 63 | data: { 64 | $area: 211 65 | }, 66 | id: "4bd95eea-b9f6-4d70-a36c-cfea77431553a", 67 | name: "Alice in Chains" 68 | }, { 69 | children: [{ 70 | children: [], 71 | data: { 72 | $area: "153", 73 | $color: "6", 74 | image: "http://userserve-ak.last.fm/serve/174s/8590493.jpg" 75 | }, 76 | id: "8bfac288-ccc5-448d-9573-c33ea2aa5c30One Hot Minute", 77 | name: "One Hot Minute" 78 | }, { 79 | children: [], 80 | data: { 81 | $area: "69", 82 | $color: "35", 83 | image: "http://userserve-ak.last.fm/serve/174s/8593509.jpg" 84 | }, 85 | id: "8bfac288-ccc5-448d-9573-c33ea2aa5c30Californication", 86 | name: "Californication" 87 | }, { 88 | children: [], 89 | data: { 90 | $area: "69", 91 | $color: "35", 92 | image: "http://userserve-ak.last.fm/serve/174s/8672727.jpg" 93 | }, 94 | id: "8bfac288-ccc5-448d-9573-c33ea2aa5c30Greatest Hits", 95 | name: "Greatest Hits" 96 | }], 97 | data: { 98 | $area: 291 99 | }, 100 | id: "8bfac288-ccc5-448d-9573-c33ea2aa5c30a", 101 | name: "Red Hot Chili Peppers" 102 | }, { 103 | children: [{ 104 | children: [], 105 | data: { 106 | $area: "114", 107 | $color: "15", 108 | image: "http://images.amazon.com/images/P/B0000UX5IY.01._SCMZZZZZZZ_.jpg" 109 | }, 110 | id: "ff6e677f-91dd-4986-a174-8db0474b1799Thicker Than Water", 111 | name: "Thicker Than Water" 112 | }, { 113 | children: [], 114 | data: { 115 | $area: "83", 116 | $color: "28", 117 | image: "http://userserve-ak.last.fm/serve/174s/8599099.jpg" 118 | }, 119 | id: "ff6e677f-91dd-4986-a174-8db0474b1799On and On", 120 | name: "On and On" 121 | }, { 122 | children: [], 123 | data: { 124 | $area: "62", 125 | $color: "41", 126 | image: "http://userserve-ak.last.fm/serve/174s/8664981.jpg" 127 | }, 128 | id: "ff6e677f-91dd-4986-a174-8db0474b1799Brushfire Fairytales", 129 | name: "Brushfire Fairytales" 130 | }], 131 | data: { 132 | $area: 259 133 | }, 134 | id: "ff6e677f-91dd-4986-a174-8db0474b1799a", 135 | name: "Jack Johnson" 136 | }, { 137 | children: [{ 138 | children: [], 139 | data: { 140 | $area: "65", 141 | $color: "38", 142 | image: "http://userserve-ak.last.fm/serve/174s/23727633.jpg" 143 | }, 144 | id: "83b9cbe7-9857-49e2-ab8e-b57b01038103Vs.", 145 | name: "Vs." 146 | }, { 147 | children: [], 148 | data: { 149 | $area: "61", 150 | $color: "44", 151 | image: "http://userserve-ak.last.fm/serve/174s/19611679.jpg" 152 | }, 153 | id: "83b9cbe7-9857-49e2-ab8e-b57b01038103Riot Act", 154 | name: "Riot Act" 155 | }, { 156 | children: [], 157 | data: { 158 | $area: "61", 159 | $color: "44", 160 | image: "http://userserve-ak.last.fm/serve/174s/17492447.jpg" 161 | }, 162 | id: "83b9cbe7-9857-49e2-ab8e-b57b01038103Yield", 163 | name: "Yield" 164 | }], 165 | data: { 166 | $area: 187 167 | }, 168 | id: "83b9cbe7-9857-49e2-ab8e-b57b01038103a", 169 | name: "Pearl Jam" 170 | }, { 171 | children: [{ 172 | children: [], 173 | data: { 174 | $area: "115", 175 | $color: "14", 176 | image: "http://userserve-ak.last.fm/serve/174s/17484209.jpg" 177 | }, 178 | id: "bfd085b8-0bbf-46b3-8ab9-193bca5c85e7Above", 179 | name: "Above" 180 | }], 181 | data: { 182 | $area: 115 183 | }, 184 | id: "bfd085b8-0bbf-46b3-8ab9-193bca5c85e7a", 185 | name: "Mad Season" 186 | }, { 187 | children: [{ 188 | children: [], 189 | data: { 190 | $area: "108", 191 | $color: "17", 192 | image: "http://images.amazon.com/images/P/B000002J8M.01._SCMZZZZZZZ_.jpg" 193 | }, 194 | id: "8c32bb01-58a3-453b-8050-8c0620edb0e5Tiny Music... Songs From the Vatican Gift Shop", 195 | name: "Tiny Music... Songs From the Vatican Gift Shop" 196 | }, { 197 | children: [], 198 | data: { 199 | $area: "60", 200 | $color: "46", 201 | image: "http://images.amazon.com/images/P/B000002IU3.01.MZZZZZZZ.jpg" 202 | }, 203 | id: "8c32bb01-58a3-453b-8050-8c0620edb0e5Core", 204 | name: "Core" 205 | }], 206 | data: { 207 | $area: 168 208 | }, 209 | id: "8c32bb01-58a3-453b-8050-8c0620edb0e5a", 210 | name: "Stone Temple Pilots" 211 | }, { 212 | children: [{ 213 | children: [], 214 | data: { 215 | $area: "63", 216 | $color: "40", 217 | image: "http://images.amazon.com/images/P/B00005NWLC.01.MZZZZZZZ.jpg" 218 | }, 219 | id: "7b2f87f6-db90-464e-a27a-deb4f7219e90Leitmotif", 220 | name: "Leitmotif" 221 | }], 222 | data: { 223 | $area: 63 224 | }, 225 | id: "7b2f87f6-db90-464e-a27a-deb4f7219e90a", 226 | name: "dredg" 227 | }, { 228 | children: [{ 229 | children: [], 230 | data: { 231 | $area: "188", 232 | $color: "3", 233 | image: "http://images.amazon.com/images/P/B000EULJLU.01._SCMZZZZZZZ_.jpg" 234 | }, 235 | id: "66fc5bf8-daa4-4241-b378-9bc9077939d210,000 Days", 236 | name: "10,000 Days" 237 | }], 238 | data: { 239 | $area: 188 240 | }, 241 | id: "66fc5bf8-daa4-4241-b378-9bc9077939d2a", 242 | name: "Tool" 243 | }, { 244 | children: [{ 245 | children: [], 246 | data: { 247 | $area: "62", 248 | $color: "41", 249 | image: "http://images.amazon.com/images/P/B00001P4TH.01._SCMZZZZZZZ_.jpg" 250 | }, 251 | id: "b7ffd2af-418f-4be2-bdd1-22f8b48613daThe Fragile (Left)", 252 | name: "The Fragile (Left)" 253 | }], 254 | data: { 255 | $area: 62 256 | }, 257 | id: "b7ffd2af-418f-4be2-bdd1-22f8b48613daa", 258 | name: "Nine Inch Nails" 259 | }, { 260 | children: [{ 261 | children: [], 262 | data: { 263 | $area: "240", 264 | $color: "1", 265 | image: "http://userserve-ak.last.fm/serve/174s/21881921.jpg" 266 | }, 267 | id: "a5585acd-9b65-49a7-a63b-3cc4ee18846eMother Love Bone", 268 | name: "Mother Love Bone" 269 | }], 270 | data: { 271 | $area: 240 272 | }, 273 | id: "a5585acd-9b65-49a7-a63b-3cc4ee18846ea", 274 | name: "Mother Love Bone" 275 | }, { 276 | children: [{ 277 | children: [], 278 | data: { 279 | $area: "67", 280 | $color: "37", 281 | image: "http://userserve-ak.last.fm/serve/174s/8634595.jpg" 282 | }, 283 | id: "7527f6c2-d762-4b88-b5e2-9244f1e34c46Around the Fur", 284 | name: "Around the Fur" 285 | }], 286 | data: { 287 | $area: 67 288 | }, 289 | id: "7527f6c2-d762-4b88-b5e2-9244f1e34c46a", 290 | name: "Deftones" 291 | }, { 292 | children: [{ 293 | children: [], 294 | data: { 295 | $area: "62", 296 | $color: "41", 297 | image: "http://images.amazon.com/images/P/B0000A5BYD.03.MZZZZZZZ.jpg" 298 | }, 299 | id: "7bdb6921-8380-422c-8514-87cf30d5d8ccIt All Makes Sense Now", 300 | name: "It All Makes Sense Now" 301 | }], 302 | data: { 303 | $area: 62 304 | }, 305 | id: "7bdb6921-8380-422c-8514-87cf30d5d8cca", 306 | name: "Kr\xF3m" 307 | }, { 308 | children: [{ 309 | children: [], 310 | data: { 311 | $area: "57", 312 | $color: "48", 313 | image: "http://images-eu.amazon.com/images/P/B00005IABM.02.MZZZZZZZ.jpg" 314 | }, 315 | id: "cb67438a-7f50-4f2b-a6f1-2bb2729fd53810,000 Hz Legend", 316 | name: "10,000 Hz Legend" 317 | }], 318 | data: { 319 | $area: 57 320 | }, 321 | id: "cb67438a-7f50-4f2b-a6f1-2bb2729fd538a", 322 | name: "Air" 323 | }, { 324 | children: [{ 325 | children: [], 326 | data: { 327 | $area: "168", 328 | $color: "5", 329 | image: "http://userserve-ak.last.fm/serve/174s/23091681.jpg" 330 | }, 331 | id: "c5998351-be49-49d8-8593-3e96f129c1fcMamagubida", 332 | name: "Mamagubida" 333 | }, { 334 | children: [], 335 | data: { 336 | $area: "141", 337 | $color: "7", 338 | image: "http://cdn.last.fm/flatness/catalogue/noimage/2/default_album_mega.png" 339 | }, 340 | id: "c5998351-be49-49d8-8593-3e96f129c1fcReggae \xE0 Coup de Cirque", 341 | name: "Reggae \xE0 Coup de Cirque" 342 | }, { 343 | children: [], 344 | data: { 345 | $area: "135", 346 | $color: "8", 347 | image: "http://userserve-ak.last.fm/serve/174s/16799743.jpg" 348 | }, 349 | id: "c5998351-be49-49d8-8593-3e96f129c1fcGrain de sable", 350 | name: "Grain de sable" 351 | }, { 352 | children: [], 353 | data: { 354 | $area: "80", 355 | $color: "30", 356 | image: "http://userserve-ak.last.fm/serve/174s/8635653.jpg" 357 | }, 358 | id: "c5998351-be49-49d8-8593-3e96f129c1fcFaut qu'ils s'activent...", 359 | name: "Faut qu'ils s'activent..." 360 | }], 361 | data: { 362 | $area: 524 363 | }, 364 | id: "c5998351-be49-49d8-8593-3e96f129c1fca", 365 | name: "Tryo" 366 | }, { 367 | children: [{ 368 | children: [], 369 | data: { 370 | $area: "57", 371 | $color: "48", 372 | image: "http://userserve-ak.last.fm/serve/174s/8634627.jpg" 373 | }, 374 | id: "4bb4e4e4-5f66-4509-98af-62dbb90c45c5The Sickness", 375 | name: "The Sickness" 376 | }], 377 | data: { 378 | $area: 57 379 | }, 380 | id: "4bb4e4e4-5f66-4509-98af-62dbb90c45c5a", 381 | name: "Disturbed" 382 | }, { 383 | children: [{ 384 | children: [], 385 | data: { 386 | $area: "72", 387 | $color: "34", 388 | image: "http://userserve-ak.last.fm/serve/174s/8673813.jpg" 389 | }, 390 | id: "95f5b748-d370-47fe-85bd-0af2dc450bc0Second-Hand Smoke", 391 | name: "Second-Hand Smoke" 392 | }], 393 | data: { 394 | $area: 72 395 | }, 396 | id: "95f5b748-d370-47fe-85bd-0af2dc450bc0a", 397 | name: "Sublime" 398 | }, { 399 | children: [{ 400 | children: [], 401 | data: { 402 | $area: "73", 403 | $color: "33", 404 | image: "http://userserve-ak.last.fm/serve/174s/7737695.jpg" 405 | }, 406 | id: "020bfbb4-05c3-4c86-b372-17825c262094Audioslave", 407 | name: "Audioslave" 408 | }], 409 | data: { 410 | $area: 73 411 | }, 412 | id: "020bfbb4-05c3-4c86-b372-17825c262094a", 413 | name: "Audioslave" 414 | }, { 415 | children: [{ 416 | children: [], 417 | data: { 418 | $area: "124", 419 | $color: "10", 420 | image: "http://userserve-ak.last.fm/serve/174s/8605651.jpg" 421 | }, 422 | id: "e9571c17-817f-4d34-ae3f-0c7a96f822c1Temple of the Dog", 423 | name: "Temple of the Dog" 424 | }], 425 | data: { 426 | $area: 124 427 | }, 428 | id: "e9571c17-817f-4d34-ae3f-0c7a96f822c1a", 429 | name: "Temple of the Dog" 430 | }, { 431 | children: [{ 432 | children: [], 433 | data: { 434 | $area: "82", 435 | $color: "29", 436 | image: "http://images.amazon.com/images/P/B0002ZEUKO.01._SCMZZZZZZZ_.jpg" 437 | }, 438 | id: "06fb1c8b-566e-4cb2-985b-b467c90781d4Are You Experienced?", 439 | name: "Are You Experienced?" 440 | }, { 441 | children: [], 442 | data: { 443 | $area: "64", 444 | $color: "39", 445 | image: "http://userserve-ak.last.fm/serve/174s/8729219.jpg" 446 | }, 447 | id: "06fb1c8b-566e-4cb2-985b-b467c90781d4First Rays of the New Rising Sun", 448 | name: "First Rays of the New Rising Sun" 449 | }], 450 | data: { 451 | $area: 146 452 | }, 453 | id: "06fb1c8b-566e-4cb2-985b-b467c90781d4a", 454 | name: "Jimi Hendrix" 455 | }, { 456 | children: [{ 457 | children: [], 458 | data: { 459 | $area: "56", 460 | $color: "50", 461 | image: "http://images.amazon.com/images/P/B0000DZDYN.01.MZZZZZZZ.jpg" 462 | }, 463 | id: "fbd2a255-1d57-4d31-ac11-65b671c19958The Singles 1992-2003", 464 | name: "The Singles 1992-2003" 465 | }], 466 | data: { 467 | $area: 56 468 | }, 469 | id: "fbd2a255-1d57-4d31-ac11-65b671c19958a", 470 | name: "No Doubt" 471 | }, { 472 | children: [{ 473 | children: [], 474 | data: { 475 | $area: "123", 476 | $color: "11", 477 | image: "http://userserve-ak.last.fm/serve/174s/11393921.jpg" 478 | }, 479 | id: "078a9376-3c04-4280-b7d7-b20e158f345dMer de Noms", 480 | name: "Mer de Noms" 481 | }, { 482 | children: [], 483 | data: { 484 | $area: "93", 485 | $color: "24", 486 | image: "http://userserve-ak.last.fm/serve/174s/11403219.jpg" 487 | }, 488 | id: "078a9376-3c04-4280-b7d7-b20e158f345dThirteenth Step", 489 | name: "Thirteenth Step" 490 | }], 491 | data: { 492 | $area: 216 493 | }, 494 | id: "078a9376-3c04-4280-b7d7-b20e158f345da", 495 | name: "A Perfect Circle" 496 | }, { 497 | children: [{ 498 | children: [], 499 | data: { 500 | $area: "109", 501 | $color: "16", 502 | image: "http://images.amazon.com/images/P/B00005LNP5.01._SCMZZZZZZZ_.jpg" 503 | }, 504 | id: "1fc56cff-f0a0-4ce2-ab1f-ac49cf3b073fElija y Gane", 505 | name: "Elija y Gane" 506 | }, { 507 | children: [], 508 | data: { 509 | $area: "85", 510 | $color: "26", 511 | image: "http://images.amazon.com/images/P/B0000B193V.01._SCMZZZZZZZ_.jpg" 512 | }, 513 | id: "1fc56cff-f0a0-4ce2-ab1f-ac49cf3b073fPara los Arboles", 514 | name: "Para los Arboles" 515 | }], 516 | data: { 517 | $area: 194 518 | }, 519 | id: "1fc56cff-f0a0-4ce2-ab1f-ac49cf3b073fa", 520 | name: "Luis Alberto Spinetta" 521 | }, { 522 | children: [{ 523 | children: [], 524 | data: { 525 | $area: "87", 526 | $color: "25", 527 | image: "http://userserve-ak.last.fm/serve/174s/8772827.jpg" 528 | }, 529 | id: "e795e03d-b5d5-4a5f-834d-162cfb308a2c4-Track Demos", 530 | name: "4-Track Demos" 531 | }, { 532 | children: [], 533 | data: { 534 | $area: "77", 535 | $color: "32", 536 | image: "http://userserve-ak.last.fm/serve/174s/9929071.jpg" 537 | }, 538 | id: "e795e03d-b5d5-4a5f-834d-162cfb308a2cRid of Me", 539 | name: "Rid of Me" 540 | }], 541 | data: { 542 | $area: 164 543 | }, 544 | id: "e795e03d-b5d5-4a5f-834d-162cfb308a2ca", 545 | name: "PJ Harvey" 546 | }, { 547 | children: [{ 548 | children: [], 549 | data: { 550 | $area: "102", 551 | $color: "19", 552 | image: "http://userserve-ak.last.fm/serve/174s/7410551.jpg" 553 | }, 554 | id: "e3e0abcd-7671-4482-a9d8-462f5acc9be5Make Yourself", 555 | name: "Make Yourself" 556 | }, { 557 | children: [], 558 | data: { 559 | $area: "84", 560 | $color: "27", 561 | image: "http://images.amazon.com/images/P/B00018D5CQ.01._SCMZZZZZZZ_.jpg" 562 | }, 563 | id: "e3e0abcd-7671-4482-a9d8-462f5acc9be5A Crow Left of the Murder", 564 | name: "A Crow Left of the Murder" 565 | }, { 566 | children: [], 567 | data: { 568 | $area: "60", 569 | $color: "46", 570 | image: "http://userserve-ak.last.fm/serve/174s/19681051.jpg" 571 | }, 572 | id: "e3e0abcd-7671-4482-a9d8-462f5acc9be5Morning View", 573 | name: "Morning View" 574 | }], 575 | data: { 576 | $area: 246 577 | }, 578 | id: "e3e0abcd-7671-4482-a9d8-462f5acc9be5a", 579 | name: "Incubus" 580 | }, { 581 | children: [{ 582 | children: [], 583 | data: { 584 | $area: "130", 585 | $color: "9", 586 | image: "http://userserve-ak.last.fm/serve/174s/15113951.jpg" 587 | }, 588 | id: "38c5cdab-5d6d-43d1-85b0-dac41bde186eNico", 589 | name: "Nico" 590 | }, { 591 | children: [], 592 | data: { 593 | $area: "120", 594 | $color: "12", 595 | image: "http://images.amazon.com/images/P/B00005V5PW.01.MZZZZZZZ.jpg" 596 | }, 597 | id: "38c5cdab-5d6d-43d1-85b0-dac41bde186eClassic Masters", 598 | name: "Classic Masters" 599 | }, { 600 | children: [], 601 | data: { 602 | $area: "103", 603 | $color: "18", 604 | image: "http://images.amazon.com/images/P/B000002TPF.01.MZZZZZZZ.jpg" 605 | }, 606 | id: "38c5cdab-5d6d-43d1-85b0-dac41bde186eSoup", 607 | name: "Soup" 608 | }, { 609 | children: [], 610 | data: { 611 | $area: "99", 612 | $color: "20", 613 | image: "http://userserve-ak.last.fm/serve/174s/15157989.jpg" 614 | }, 615 | id: "38c5cdab-5d6d-43d1-85b0-dac41bde186eBlind Melon", 616 | name: "Blind Melon" 617 | }], 618 | data: { 619 | $area: 452 620 | }, 621 | id: "38c5cdab-5d6d-43d1-85b0-dac41bde186ea", 622 | name: "Blind Melon" 623 | }, { 624 | children: [{ 625 | children: [], 626 | data: { 627 | $area: "173", 628 | $color: "4", 629 | image: "http://userserve-ak.last.fm/serve/174s/8590515.jpg" 630 | }, 631 | id: "153c9281-268f-4cf3-8938-f5a4593e5df4Superunknown", 632 | name: "Superunknown" 633 | }, { 634 | children: [], 635 | data: { 636 | $area: "117", 637 | $color: "13", 638 | image: "http://userserve-ak.last.fm/serve/174s/5269310.jpg" 639 | }, 640 | id: "153c9281-268f-4cf3-8938-f5a4593e5df4Louder Than Love", 641 | name: "Louder Than Love" 642 | }, { 643 | children: [], 644 | data: { 645 | $area: "96", 646 | $color: "21", 647 | image: "http://userserve-ak.last.fm/serve/174s/8600371.jpg" 648 | }, 649 | id: "153c9281-268f-4cf3-8938-f5a4593e5df4Down on the Upside", 650 | name: "Down on the Upside" 651 | }, { 652 | children: [], 653 | data: { 654 | $area: "95", 655 | $color: "22", 656 | image: "http://images.amazon.com/images/P/B000000M4A.01.MZZZZZZZ.jpg" 657 | }, 658 | id: "153c9281-268f-4cf3-8938-f5a4593e5df4Ultramega OK", 659 | name: "Ultramega OK" 660 | }], 661 | data: { 662 | $area: 481 663 | }, 664 | id: "153c9281-268f-4cf3-8938-f5a4593e5df4a", 665 | name: "Soundgarden" 666 | }], 667 | data: { 668 | $area: 4949 669 | }, 670 | id: "topAlbums", 671 | name: "top albums" 672 | }; 673 | //end 674 | 675 | var infovis = document.getElementById('infovis'); 676 | var w = infovis.offsetWidth, h = infovis.offsetHeight; 677 | infovis.style.width = w + 'px'; 678 | infovis.style.height = h + 'px'; 679 | 680 | //init tm 681 | var tm = new TM.Squarified({ 682 | //Where to inject the treemap. 683 | rootId: 'infovis', 684 | 685 | //Add click handlers for 686 | //zooming the Treemap in and out 687 | addLeftClickHandler: true, 688 | addRightClickHandler: true, 689 | 690 | //When hovering a node highlight the nodes 691 | //between the root node and the hovered node. This 692 | //is done by adding the 'in-path' CSS class to each node. 693 | selectPathOnHover: true, 694 | 695 | Color: { 696 | //Allow coloring 697 | enable: true, 698 | //Set min value and max value constraints 699 | //for the *$color* property value. 700 | //Default's to -100 and 100. 701 | minValue: 1, 702 | maxValue: 50, 703 | //Set color range. Default's to reddish and greenish. 704 | //It takes an array of three 705 | //integers as R, G and B values. 706 | minColorValue: [0, 255, 50], 707 | maxColorValue: [255, 0, 50] 708 | }, 709 | 710 | //Allow tips 711 | Tips: { 712 | enable: true, 713 | //add positioning offsets 714 | offsetX: 20, 715 | offsetY: 20, 716 | //implement the onShow method to 717 | //add content to the tooltip when a node 718 | //is hovered 719 | onShow: function(tip, node, isLeaf, domElement) { 720 | tip.innerHTML = "
" + node.name + "
" + 721 | "
" + this.makeHTMLFromData(node.data) + "
"; 722 | }, 723 | 724 | //Build the tooltip inner html by taking each node data property 725 | makeHTMLFromData: function(data){ 726 | var html = ''; 727 | html += "playcount" + ': ' + data.$area + '
'; 728 | if ("$color" in data) 729 | html += "rank" + ': ' + data.$color + '
'; 730 | if ("image" in data) 731 | html += ""; 732 | return html; 733 | } 734 | }, 735 | 736 | //Remove all element events before destroying it. 737 | onDestroyElement: function(content, tree, isLeaf, leaf){ 738 | if(leaf.clearAttributes) leaf.clearAttributes(); 739 | } 740 | }); 741 | 742 | //load JSON and plot 743 | tm.loadJSON(json); 744 | //end 745 | } 746 | -------------------------------------------------------------------------------- /Examples/example2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Treemap - Squarified Treemap with on-demand nodes 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 |
23 |
24 |

25 | Squarified Treemap with on-demand nodes 26 |

27 | 28 | This example shows how you can use the request controller method to create a Treemap with on demand nodes

29 | There should be only one level shown at a time.

30 | Clicking on a band should show a new treemap with its most listened albums. 31 | 32 |
33 | 34 |
35 | 36 | 37 | 38 |
39 | 40 |
41 |
42 |
43 | 44 |
45 | 46 |
47 |
48 | 49 | 50 | -------------------------------------------------------------------------------- /Examples/example2.js: -------------------------------------------------------------------------------- 1 | var Log = { 2 | elem: false, 3 | write: function(text){ 4 | if (!this.elem) 5 | this.elem = document.getElementById('log'); 6 | this.elem.innerHTML = text; 7 | this.elem.style.left = (500 - this.elem.offsetWidth / 2) + 'px'; 8 | } 9 | }; 10 | 11 | function addEvent(obj, type, fn) { 12 | if (obj.addEventListener) obj.addEventListener(type, fn, false); 13 | else obj.attachEvent('on' + type, fn); 14 | }; 15 | 16 | 17 | function init(){ 18 | 19 | //init data 20 | var json = "{children:[{children:[{children:[], data:{$area:\"80\", $color:\"30\", image:\"http://images.amazon.com/images/P/B0007DAZW8.01.MZZZZZZZ.jpg\"}, id:\"056e4f3e-d505-4dad-8ec1-d04f521cbb56Human After All\", name:\"Human After All\"}], data:{$area:80}, id:\"056e4f3e-d505-4dad-8ec1-d04f521cbb56a\", name:\"Daft Punk\"}, {children:[{children:[], data:{$area:\"94\", $color:\"23\", image:\"http://cdn.last.fm/coverart/130x130/2393956.jpg\"}, id:\"f2fa5cf6-e0b0-4559-8641-e033f1a9e6fcClearing the Channel\", name:\"Clearing the Channel\"}], data:{$area:94}, id:\"f2fa5cf6-e0b0-4559-8641-e033f1a9e6fca\", name:\"Sinch\"}, {children:[{children:[], data:{$area:\"211\", $color:\"2\", image:\"http://cdn.last.fm/coverart/130x130/2564320-1193176651.jpg\"}, id:\"4bd95eea-b9f6-4d70-a36c-cfea77431553Music Bank\", name:\"Music Bank\"}], data:{$area:211}, id:\"4bd95eea-b9f6-4d70-a36c-cfea77431553a\", name:\"Alice in Chains\"}, {children:[{children:[], data:{$area:\"153\", $color:\"6\", image:\"http://userserve-ak.last.fm/serve/174s/8590493.jpg\"}, id:\"8bfac288-ccc5-448d-9573-c33ea2aa5c30One Hot Minute\", name:\"One Hot Minute\"}, {children:[], data:{$area:\"69\", $color:\"35\", image:\"http://userserve-ak.last.fm/serve/174s/8593509.jpg\"}, id:\"8bfac288-ccc5-448d-9573-c33ea2aa5c30Californication\", name:\"Californication\"}, {children:[], data:{$area:\"69\", $color:\"35\", image:\"http://userserve-ak.last.fm/serve/174s/8672727.jpg\"}, id:\"8bfac288-ccc5-448d-9573-c33ea2aa5c30Greatest Hits\", name:\"Greatest Hits\"}], data:{$area:291}, id:\"8bfac288-ccc5-448d-9573-c33ea2aa5c30a\", name:\"Red Hot Chili Peppers\"}, {children:[{children:[], data:{$area:\"114\", $color:\"15\", image:\"http://images.amazon.com/images/P/B0000UX5IY.01._SCMZZZZZZZ_.jpg\"}, id:\"ff6e677f-91dd-4986-a174-8db0474b1799Thicker Than Water\", name:\"Thicker Than Water\"}, {children:[], data:{$area:\"83\", $color:\"28\", image:\"http://userserve-ak.last.fm/serve/174s/8599099.jpg\"}, id:\"ff6e677f-91dd-4986-a174-8db0474b1799On and On\", name:\"On and On\"}, {children:[], data:{$area:\"62\", $color:\"41\", image:\"http://userserve-ak.last.fm/serve/174s/8664981.jpg\"}, id:\"ff6e677f-91dd-4986-a174-8db0474b1799Brushfire Fairytales\", name:\"Brushfire Fairytales\"}], data:{$area:259}, id:\"ff6e677f-91dd-4986-a174-8db0474b1799a\", name:\"Jack Johnson\"}, {children:[{children:[], data:{$area:\"65\", $color:\"38\", image:\"http://userserve-ak.last.fm/serve/174s/23727633.jpg\"}, id:\"83b9cbe7-9857-49e2-ab8e-b57b01038103Vs.\", name:\"Vs.\"}, {children:[], data:{$area:\"61\", $color:\"44\", image:\"http://userserve-ak.last.fm/serve/174s/19611679.jpg\"}, id:\"83b9cbe7-9857-49e2-ab8e-b57b01038103Riot Act\", name:\"Riot Act\"}, {children:[], data:{$area:\"61\", $color:\"44\", image:\"http://userserve-ak.last.fm/serve/174s/17492447.jpg\"}, id:\"83b9cbe7-9857-49e2-ab8e-b57b01038103Yield\", name:\"Yield\"}], data:{$area:187}, id:\"83b9cbe7-9857-49e2-ab8e-b57b01038103a\", name:\"Pearl Jam\"}, {children:[{children:[], data:{$area:\"115\", $color:\"14\", image:\"http://userserve-ak.last.fm/serve/174s/17484209.jpg\"}, id:\"bfd085b8-0bbf-46b3-8ab9-193bca5c85e7Above\", name:\"Above\"}], data:{$area:115}, id:\"bfd085b8-0bbf-46b3-8ab9-193bca5c85e7a\", name:\"Mad Season\"}, {children:[{children:[], data:{$area:\"108\", $color:\"17\", image:\"http://images.amazon.com/images/P/B000002J8M.01._SCMZZZZZZZ_.jpg\"}, id:\"8c32bb01-58a3-453b-8050-8c0620edb0e5Tiny Music... Songs From the Vatican Gift Shop\", name:\"Tiny Music... Songs From the Vatican Gift Shop\"}, {children:[], data:{$area:\"60\", $color:\"46\", image:\"http://images.amazon.com/images/P/B000002IU3.01.MZZZZZZZ.jpg\"}, id:\"8c32bb01-58a3-453b-8050-8c0620edb0e5Core\", name:\"Core\"}], data:{$area:168}, id:\"8c32bb01-58a3-453b-8050-8c0620edb0e5a\", name:\"Stone Temple Pilots\"}, {children:[{children:[], data:{$area:\"63\", $color:\"40\", image:\"http://images.amazon.com/images/P/B00005NWLC.01.MZZZZZZZ.jpg\"}, id:\"7b2f87f6-db90-464e-a27a-deb4f7219e90Leitmotif\", name:\"Leitmotif\"}], data:{$area:63}, id:\"7b2f87f6-db90-464e-a27a-deb4f7219e90a\", name:\"dredg\"}, {children:[{children:[], data:{$area:\"188\", $color:\"3\", image:\"http://images.amazon.com/images/P/B000EULJLU.01._SCMZZZZZZZ_.jpg\"}, id:\"66fc5bf8-daa4-4241-b378-9bc9077939d210,000 Days\", name:\"10,000 Days\"}], data:{$area:188}, id:\"66fc5bf8-daa4-4241-b378-9bc9077939d2a\", name:\"Tool\"}, {children:[{children:[], data:{$area:\"62\", $color:\"41\", image:\"http://images.amazon.com/images/P/B00001P4TH.01._SCMZZZZZZZ_.jpg\"}, id:\"b7ffd2af-418f-4be2-bdd1-22f8b48613daThe Fragile (Left)\", name:\"The Fragile (Left)\"}], data:{$area:62}, id:\"b7ffd2af-418f-4be2-bdd1-22f8b48613daa\", name:\"Nine Inch Nails\"}, {children:[{children:[], data:{$area:\"240\", $color:\"1\", image:\"http://userserve-ak.last.fm/serve/174s/21881921.jpg\"}, id:\"a5585acd-9b65-49a7-a63b-3cc4ee18846eMother Love Bone\", name:\"Mother Love Bone\"}], data:{$area:240}, id:\"a5585acd-9b65-49a7-a63b-3cc4ee18846ea\", name:\"Mother Love Bone\"}, {children:[{children:[], data:{$area:\"67\", $color:\"37\", image:\"http://userserve-ak.last.fm/serve/174s/8634595.jpg\"}, id:\"7527f6c2-d762-4b88-b5e2-9244f1e34c46Around the Fur\", name:\"Around the Fur\"}], data:{$area:67}, id:\"7527f6c2-d762-4b88-b5e2-9244f1e34c46a\", name:\"Deftones\"}, {children:[{children:[], data:{$area:\"62\", $color:\"41\", image:\"http://images.amazon.com/images/P/B0000A5BYD.03.MZZZZZZZ.jpg\"}, id:\"7bdb6921-8380-422c-8514-87cf30d5d8ccIt All Makes Sense Now\", name:\"It All Makes Sense Now\"}], data:{$area:62}, id:\"7bdb6921-8380-422c-8514-87cf30d5d8cca\", name:\"Kr\xF3m\"}, {children:[{children:[], data:{$area:\"57\", $color:\"48\", image:\"http://images-eu.amazon.com/images/P/B00005IABM.02.MZZZZZZZ.jpg\"}, id:\"cb67438a-7f50-4f2b-a6f1-2bb2729fd53810,000 Hz Legend\", name:\"10,000 Hz Legend\"}], data:{$area:57}, id:\"cb67438a-7f50-4f2b-a6f1-2bb2729fd538a\", name:\"Air\"}, {children:[{children:[], data:{$area:\"168\", $color:\"5\", image:\"http://userserve-ak.last.fm/serve/174s/23091681.jpg\"}, id:\"c5998351-be49-49d8-8593-3e96f129c1fcMamagubida\", name:\"Mamagubida\"}, {children:[], data:{$area:\"141\", $color:\"7\", image:\"http://cdn.last.fm/flatness/catalogue/noimage/2/default_album_mega.png\"}, id:\"c5998351-be49-49d8-8593-3e96f129c1fcReggae \xE0 Coup de Cirque\", name:\"Reggae \xE0 Coup de Cirque\"}, {children:[], data:{$area:\"135\", $color:\"8\", image:\"http://userserve-ak.last.fm/serve/174s/16799743.jpg\"}, id:\"c5998351-be49-49d8-8593-3e96f129c1fcGrain de sable\", name:\"Grain de sable\"}, {children:[], data:{$area:\"80\", $color:\"30\", image:\"http://userserve-ak.last.fm/serve/174s/8635653.jpg\"}, id:\"c5998351-be49-49d8-8593-3e96f129c1fcFaut qu'ils s'activent...\", name:\"Faut qu'ils s'activent...\"}], data:{$area:524}, id:\"c5998351-be49-49d8-8593-3e96f129c1fca\", name:\"Tryo\"}, {children:[{children:[], data:{$area:\"57\", $color:\"48\", image:\"http://userserve-ak.last.fm/serve/174s/8634627.jpg\"}, id:\"4bb4e4e4-5f66-4509-98af-62dbb90c45c5The Sickness\", name:\"The Sickness\"}], data:{$area:57}, id:\"4bb4e4e4-5f66-4509-98af-62dbb90c45c5a\", name:\"Disturbed\"}, {children:[{children:[], data:{$area:\"72\", $color:\"34\", image:\"http://userserve-ak.last.fm/serve/174s/8673813.jpg\"}, id:\"95f5b748-d370-47fe-85bd-0af2dc450bc0Second-Hand Smoke\", name:\"Second-Hand Smoke\"}], data:{$area:72}, id:\"95f5b748-d370-47fe-85bd-0af2dc450bc0a\", name:\"Sublime\"}, {children:[{children:[], data:{$area:\"73\", $color:\"33\", image:\"http://userserve-ak.last.fm/serve/174s/7737695.jpg\"}, id:\"020bfbb4-05c3-4c86-b372-17825c262094Audioslave\", name:\"Audioslave\"}], data:{$area:73}, id:\"020bfbb4-05c3-4c86-b372-17825c262094a\", name:\"Audioslave\"}, {children:[{children:[], data:{$area:\"124\", $color:\"10\", image:\"http://userserve-ak.last.fm/serve/174s/8605651.jpg\"}, id:\"e9571c17-817f-4d34-ae3f-0c7a96f822c1Temple of the Dog\", name:\"Temple of the Dog\"}], data:{$area:124}, id:\"e9571c17-817f-4d34-ae3f-0c7a96f822c1a\", name:\"Temple of the Dog\"}, {children:[{children:[], data:{$area:\"82\", $color:\"29\", image:\"http://images.amazon.com/images/P/B0002ZEUKO.01._SCMZZZZZZZ_.jpg\"}, id:\"06fb1c8b-566e-4cb2-985b-b467c90781d4Are You Experienced?\", name:\"Are You Experienced?\"}, {children:[], data:{$area:\"64\", $color:\"39\", image:\"http://userserve-ak.last.fm/serve/174s/8729219.jpg\"}, id:\"06fb1c8b-566e-4cb2-985b-b467c90781d4First Rays of the New Rising Sun\", name:\"First Rays of the New Rising Sun\"}], data:{$area:146}, id:\"06fb1c8b-566e-4cb2-985b-b467c90781d4a\", name:\"Jimi Hendrix\"}, {children:[{children:[], data:{$area:\"56\", $color:\"50\", image:\"http://images.amazon.com/images/P/B0000DZDYN.01.MZZZZZZZ.jpg\"}, id:\"fbd2a255-1d57-4d31-ac11-65b671c19958The Singles 1992-2003\", name:\"The Singles 1992-2003\"}], data:{$area:56}, id:\"fbd2a255-1d57-4d31-ac11-65b671c19958a\", name:\"No Doubt\"}, {children:[{children:[], data:{$area:\"123\", $color:\"11\", image:\"http://userserve-ak.last.fm/serve/174s/11393921.jpg\"}, id:\"078a9376-3c04-4280-b7d7-b20e158f345dMer de Noms\", name:\"Mer de Noms\"}, {children:[], data:{$area:\"93\", $color:\"24\", image:\"http://userserve-ak.last.fm/serve/174s/11403219.jpg\"}, id:\"078a9376-3c04-4280-b7d7-b20e158f345dThirteenth Step\", name:\"Thirteenth Step\"}], data:{$area:216}, id:\"078a9376-3c04-4280-b7d7-b20e158f345da\", name:\"A Perfect Circle\"}, {children:[{children:[], data:{$area:\"109\", $color:\"16\", image:\"http://images.amazon.com/images/P/B00005LNP5.01._SCMZZZZZZZ_.jpg\"}, id:\"1fc56cff-f0a0-4ce2-ab1f-ac49cf3b073fElija y Gane\", name:\"Elija y Gane\"}, {children:[], data:{$area:\"85\", $color:\"26\", image:\"http://images.amazon.com/images/P/B0000B193V.01._SCMZZZZZZZ_.jpg\"}, id:\"1fc56cff-f0a0-4ce2-ab1f-ac49cf3b073fPara los Arboles\", name:\"Para los Arboles\"}], data:{$area:194}, id:\"1fc56cff-f0a0-4ce2-ab1f-ac49cf3b073fa\", name:\"Luis Alberto Spinetta\"}, {children:[{children:[], data:{$area:\"87\", $color:\"25\", image:\"http://userserve-ak.last.fm/serve/174s/8772827.jpg\"}, id:\"e795e03d-b5d5-4a5f-834d-162cfb308a2c4-Track Demos\", name:\"4-Track Demos\"}, {children:[], data:{$area:\"77\", $color:\"32\", image:\"http://userserve-ak.last.fm/serve/174s/9929071.jpg\"}, id:\"e795e03d-b5d5-4a5f-834d-162cfb308a2cRid of Me\", name:\"Rid of Me\"}], data:{$area:164}, id:\"e795e03d-b5d5-4a5f-834d-162cfb308a2ca\", name:\"PJ Harvey\"}, {children:[{children:[], data:{$area:\"102\", $color:\"19\", image:\"http://userserve-ak.last.fm/serve/174s/7410551.jpg\"}, id:\"e3e0abcd-7671-4482-a9d8-462f5acc9be5Make Yourself\", name:\"Make Yourself\"}, {children:[], data:{$area:\"84\", $color:\"27\", image:\"http://images.amazon.com/images/P/B00018D5CQ.01._SCMZZZZZZZ_.jpg\"}, id:\"e3e0abcd-7671-4482-a9d8-462f5acc9be5A Crow Left of the Murder\", name:\"A Crow Left of the Murder\"}, {children:[], data:{$area:\"60\", $color:\"46\", image:\"http://userserve-ak.last.fm/serve/174s/19681051.jpg\"}, id:\"e3e0abcd-7671-4482-a9d8-462f5acc9be5Morning View\", name:\"Morning View\"}], data:{$area:246}, id:\"e3e0abcd-7671-4482-a9d8-462f5acc9be5a\", name:\"Incubus\"}, {children:[{children:[], data:{$area:\"130\", $color:\"9\", image:\"http://userserve-ak.last.fm/serve/174s/15113951.jpg\"}, id:\"38c5cdab-5d6d-43d1-85b0-dac41bde186eNico\", name:\"Nico\"}, {children:[], data:{$area:\"120\", $color:\"12\", image:\"http://images.amazon.com/images/P/B00005V5PW.01.MZZZZZZZ.jpg\"}, id:\"38c5cdab-5d6d-43d1-85b0-dac41bde186eClassic Masters\", name:\"Classic Masters\"}, {children:[], data:{$area:\"103\", $color:\"18\", image:\"http://images.amazon.com/images/P/B000002TPF.01.MZZZZZZZ.jpg\"}, id:\"38c5cdab-5d6d-43d1-85b0-dac41bde186eSoup\", name:\"Soup\"}, {children:[], data:{$area:\"99\", $color:\"20\", image:\"http://userserve-ak.last.fm/serve/174s/15157989.jpg\"}, id:\"38c5cdab-5d6d-43d1-85b0-dac41bde186eBlind Melon\", name:\"Blind Melon\"}], data:{$area:452}, id:\"38c5cdab-5d6d-43d1-85b0-dac41bde186ea\", name:\"Blind Melon\"}, {children:[{children:[], data:{$area:\"173\", $color:\"4\", image:\"http://userserve-ak.last.fm/serve/174s/8590515.jpg\"}, id:\"153c9281-268f-4cf3-8938-f5a4593e5df4Superunknown\", name:\"Superunknown\"}, {children:[], data:{$area:\"117\", $color:\"13\", image:\"http://userserve-ak.last.fm/serve/174s/5269310.jpg\"}, id:\"153c9281-268f-4cf3-8938-f5a4593e5df4Louder Than Love\", name:\"Louder Than Love\"}, {children:[], data:{$area:\"96\", $color:\"21\", image:\"http://userserve-ak.last.fm/serve/174s/8600371.jpg\"}, id:\"153c9281-268f-4cf3-8938-f5a4593e5df4Down on the Upside\", name:\"Down on the Upside\"}, {children:[], data:{$area:\"95\", $color:\"22\", image:\"http://images.amazon.com/images/P/B000000M4A.01.MZZZZZZZ.jpg\"}, id:\"153c9281-268f-4cf3-8938-f5a4593e5df4Ultramega OK\", name:\"Ultramega OK\"}], data:{$area:481}, id:\"153c9281-268f-4cf3-8938-f5a4593e5df4a\", name:\"Soundgarden\"}], data:{$area:4949}, id:\"topAlbums\", name:\"top albums\"}"; 21 | //end 22 | var get = function(id){ 23 | return document.getElementById(id); 24 | }; 25 | var infovis = get('infovis'); 26 | var w = infovis.offsetWidth, h = infovis.offsetHeight; 27 | infovis.style.width = w + 'px'; 28 | infovis.style.height = h + 'px'; 29 | 30 | //init tm 31 | var tm = new TM.Squarified({ 32 | //The id of the treemap container 33 | rootId: 'infovis', 34 | //Set the max. depth to be shown for a subtree 35 | levelsToShow: 1, 36 | 37 | //Add click handlers for 38 | //zooming the Treemap in and out 39 | addLeftClickHandler: true, 40 | addRightClickHandler: true, 41 | 42 | //When hovering a node highlight the nodes 43 | //between the root node and the hovered node. This 44 | //is done by adding the 'in-path' CSS class to each node. 45 | selectPathOnHover: true, 46 | 47 | //Allow tips 48 | Tips: { 49 | allow: true, 50 | //add positioning offsets 51 | offsetX: 20, 52 | offsetY: 20, 53 | //implement the onShow method to 54 | //add content to the tooltip when a node 55 | //is hovered 56 | onShow: function(tip, node, isLeaf, domElement) { 57 | tip.innerHTML = "
" + node.name + "
" + 58 | "
" + this.makeHTMLFromData(node.data) + "
"; 59 | }, 60 | 61 | //Aux method: Build the tooltip inner html by using the data property 62 | makeHTMLFromData: function(data){ 63 | var html = ''; 64 | html += "playcount" + ': ' + data.$area + '
'; 65 | if ("$color" in data) 66 | html += "rank" + ': ' + data.$color + '
'; 67 | if ("image" in data) 68 | html += ""; 69 | return html; 70 | } 71 | }, 72 | 73 | //Implement this method for retrieving a requested 74 | //subtree that has as root a node with id = nodeId, 75 | //and level as depth. This method could also make a server-side 76 | //call for the requested subtree. When completed, the onComplete 77 | //callback method should be called. 78 | request: function(nodeId, level, onComplete){ 79 | var tree = eval('(' + json + ')'); 80 | var subtree = TM.Util.getSubtree(tree, nodeId); 81 | TM.Util.prune(subtree, 1); 82 | onComplete.onComplete(nodeId, subtree); 83 | }, 84 | 85 | //Remove all events for the element before destroying it. 86 | onDestroyElement: function(content, tree, isLeaf, leaf){ 87 | if(leaf.clearAttributes) leaf.clearAttributes(); 88 | } 89 | }); 90 | 91 | var pjson = eval('(' + json + ')'); 92 | TM.Util.prune(pjson, 1); 93 | tm.loadJSON(pjson); 94 | //end 95 | } 96 | -------------------------------------------------------------------------------- /Examples/example3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Treemap - Strip Cushioned Treemap 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 |
23 | 24 | 25 | 26 |
27 |

28 | Strip Cushioned Treemap 29 |

30 | 31 | In this example a static JSON tree is loaded into a Strip Treemap.

32 | Tooltips are manually added for each Treemap DOM node.

33 | Also an image is added as background for each leaf node to simulate cushions.

34 | Left click to set a node as root for the visualization.

35 | Right click to set the parent node as root for the visualization. 36 | 37 |
38 | 39 |
40 | 41 | 42 | 43 |
44 | 45 |
46 |
47 |
48 | 49 |
50 |
51 | 52 |
53 |
54 | 55 | 56 | -------------------------------------------------------------------------------- /Examples/example3.js: -------------------------------------------------------------------------------- 1 | var Log = { 2 | elem: false, 3 | write: function(text){ 4 | if (!this.elem) 5 | this.elem = document.getElementById('log'); 6 | this.elem.innerHTML = text; 7 | this.elem.style.left = (500 - this.elem.offsetWidth / 2) + 'px'; 8 | } 9 | }; 10 | 11 | function addEvent(obj, type, fn) { 12 | if (obj.addEventListener) obj.addEventListener(type, fn, false); 13 | else obj.attachEvent('on' + type, fn); 14 | }; 15 | 16 | 17 | function init(){ 18 | //init data 19 | var json = { 20 | children: [{ 21 | children: [{ 22 | children: [], 23 | data: { 24 | $area: "80", 25 | $color: "30", 26 | image: "http://images.amazon.com/images/P/B0007DAZW8.01.MZZZZZZZ.jpg" 27 | }, 28 | id: "056e4f3e-d505-4dad-8ec1-d04f521cbb56Human After All", 29 | name: "Human After All" 30 | }], 31 | data: { 32 | $area: 80 33 | }, 34 | id: "056e4f3e-d505-4dad-8ec1-d04f521cbb56a", 35 | name: "Daft Punk" 36 | }, { 37 | children: [{ 38 | children: [], 39 | data: { 40 | $area: "94", 41 | $color: "23", 42 | image: "http://cdn.last.fm/coverart/130x130/2393956.jpg" 43 | }, 44 | id: "f2fa5cf6-e0b0-4559-8641-e033f1a9e6fcClearing the Channel", 45 | name: "Clearing the Channel" 46 | }], 47 | data: { 48 | $area: 94 49 | }, 50 | id: "f2fa5cf6-e0b0-4559-8641-e033f1a9e6fca", 51 | name: "Sinch" 52 | }, { 53 | children: [{ 54 | children: [], 55 | data: { 56 | $area: "211", 57 | $color: "2", 58 | image: "http://cdn.last.fm/coverart/130x130/2564320-1193176651.jpg" 59 | }, 60 | id: "4bd95eea-b9f6-4d70-a36c-cfea77431553Music Bank", 61 | name: "Music Bank" 62 | }], 63 | data: { 64 | $area: 211 65 | }, 66 | id: "4bd95eea-b9f6-4d70-a36c-cfea77431553a", 67 | name: "Alice in Chains" 68 | }, { 69 | children: [{ 70 | children: [], 71 | data: { 72 | $area: "153", 73 | $color: "6", 74 | image: "http://userserve-ak.last.fm/serve/174s/8590493.jpg" 75 | }, 76 | id: "8bfac288-ccc5-448d-9573-c33ea2aa5c30One Hot Minute", 77 | name: "One Hot Minute" 78 | }, { 79 | children: [], 80 | data: { 81 | $area: "69", 82 | $color: "35", 83 | image: "http://userserve-ak.last.fm/serve/174s/8593509.jpg" 84 | }, 85 | id: "8bfac288-ccc5-448d-9573-c33ea2aa5c30Californication", 86 | name: "Californication" 87 | }, { 88 | children: [], 89 | data: { 90 | $area: "69", 91 | $color: "35", 92 | image: "http://userserve-ak.last.fm/serve/174s/8672727.jpg" 93 | }, 94 | id: "8bfac288-ccc5-448d-9573-c33ea2aa5c30Greatest Hits", 95 | name: "Greatest Hits" 96 | }], 97 | data: { 98 | $area: 291 99 | }, 100 | id: "8bfac288-ccc5-448d-9573-c33ea2aa5c30a", 101 | name: "Red Hot Chili Peppers" 102 | }, { 103 | children: [{ 104 | children: [], 105 | data: { 106 | $area: "114", 107 | $color: "15", 108 | image: "http://images.amazon.com/images/P/B0000UX5IY.01._SCMZZZZZZZ_.jpg" 109 | }, 110 | id: "ff6e677f-91dd-4986-a174-8db0474b1799Thicker Than Water", 111 | name: "Thicker Than Water" 112 | }, { 113 | children: [], 114 | data: { 115 | $area: "83", 116 | $color: "28", 117 | image: "http://userserve-ak.last.fm/serve/174s/8599099.jpg" 118 | }, 119 | id: "ff6e677f-91dd-4986-a174-8db0474b1799On and On", 120 | name: "On and On" 121 | }, { 122 | children: [], 123 | data: { 124 | $area: "62", 125 | $color: "41", 126 | image: "http://userserve-ak.last.fm/serve/174s/8664981.jpg" 127 | }, 128 | id: "ff6e677f-91dd-4986-a174-8db0474b1799Brushfire Fairytales", 129 | name: "Brushfire Fairytales" 130 | }], 131 | data: { 132 | $area: 259 133 | }, 134 | id: "ff6e677f-91dd-4986-a174-8db0474b1799a", 135 | name: "Jack Johnson" 136 | }, { 137 | children: [{ 138 | children: [], 139 | data: { 140 | $area: "65", 141 | $color: "38", 142 | image: "http://userserve-ak.last.fm/serve/174s/23727633.jpg" 143 | }, 144 | id: "83b9cbe7-9857-49e2-ab8e-b57b01038103Vs.", 145 | name: "Vs." 146 | }, { 147 | children: [], 148 | data: { 149 | $area: "61", 150 | $color: "44", 151 | image: "http://userserve-ak.last.fm/serve/174s/19611679.jpg" 152 | }, 153 | id: "83b9cbe7-9857-49e2-ab8e-b57b01038103Riot Act", 154 | name: "Riot Act" 155 | }, { 156 | children: [], 157 | data: { 158 | $area: "61", 159 | $color: "44", 160 | image: "http://userserve-ak.last.fm/serve/174s/17492447.jpg" 161 | }, 162 | id: "83b9cbe7-9857-49e2-ab8e-b57b01038103Yield", 163 | name: "Yield" 164 | }], 165 | data: { 166 | $area: 187 167 | }, 168 | id: "83b9cbe7-9857-49e2-ab8e-b57b01038103a", 169 | name: "Pearl Jam" 170 | }, { 171 | children: [{ 172 | children: [], 173 | data: { 174 | $area: "115", 175 | $color: "14", 176 | image: "http://userserve-ak.last.fm/serve/174s/17484209.jpg" 177 | }, 178 | id: "bfd085b8-0bbf-46b3-8ab9-193bca5c85e7Above", 179 | name: "Above" 180 | }], 181 | data: { 182 | $area: 115 183 | }, 184 | id: "bfd085b8-0bbf-46b3-8ab9-193bca5c85e7a", 185 | name: "Mad Season" 186 | }, { 187 | children: [{ 188 | children: [], 189 | data: { 190 | $area: "108", 191 | $color: "17", 192 | image: "http://images.amazon.com/images/P/B000002J8M.01._SCMZZZZZZZ_.jpg" 193 | }, 194 | id: "8c32bb01-58a3-453b-8050-8c0620edb0e5Tiny Music... Songs From the Vatican Gift Shop", 195 | name: "Tiny Music... Songs From the Vatican Gift Shop" 196 | }, { 197 | children: [], 198 | data: { 199 | $area: "60", 200 | $color: "46", 201 | image: "http://images.amazon.com/images/P/B000002IU3.01.MZZZZZZZ.jpg" 202 | }, 203 | id: "8c32bb01-58a3-453b-8050-8c0620edb0e5Core", 204 | name: "Core" 205 | }], 206 | data: { 207 | $area: 168 208 | }, 209 | id: "8c32bb01-58a3-453b-8050-8c0620edb0e5a", 210 | name: "Stone Temple Pilots" 211 | }, { 212 | children: [{ 213 | children: [], 214 | data: { 215 | $area: "63", 216 | $color: "40", 217 | image: "http://images.amazon.com/images/P/B00005NWLC.01.MZZZZZZZ.jpg" 218 | }, 219 | id: "7b2f87f6-db90-464e-a27a-deb4f7219e90Leitmotif", 220 | name: "Leitmotif" 221 | }], 222 | data: { 223 | $area: 63 224 | }, 225 | id: "7b2f87f6-db90-464e-a27a-deb4f7219e90a", 226 | name: "dredg" 227 | }, { 228 | children: [{ 229 | children: [], 230 | data: { 231 | $area: "188", 232 | $color: "3", 233 | image: "http://images.amazon.com/images/P/B000EULJLU.01._SCMZZZZZZZ_.jpg" 234 | }, 235 | id: "66fc5bf8-daa4-4241-b378-9bc9077939d210,000 Days", 236 | name: "10,000 Days" 237 | }], 238 | data: { 239 | $area: 188 240 | }, 241 | id: "66fc5bf8-daa4-4241-b378-9bc9077939d2a", 242 | name: "Tool" 243 | }, { 244 | children: [{ 245 | children: [], 246 | data: { 247 | $area: "62", 248 | $color: "41", 249 | image: "http://images.amazon.com/images/P/B00001P4TH.01._SCMZZZZZZZ_.jpg" 250 | }, 251 | id: "b7ffd2af-418f-4be2-bdd1-22f8b48613daThe Fragile (Left)", 252 | name: "The Fragile (Left)" 253 | }], 254 | data: { 255 | $area: 62 256 | }, 257 | id: "b7ffd2af-418f-4be2-bdd1-22f8b48613daa", 258 | name: "Nine Inch Nails" 259 | }, { 260 | children: [{ 261 | children: [], 262 | data: { 263 | $area: "240", 264 | $color: "1", 265 | image: "http://userserve-ak.last.fm/serve/174s/21881921.jpg" 266 | }, 267 | id: "a5585acd-9b65-49a7-a63b-3cc4ee18846eMother Love Bone", 268 | name: "Mother Love Bone" 269 | }], 270 | data: { 271 | $area: 240 272 | }, 273 | id: "a5585acd-9b65-49a7-a63b-3cc4ee18846ea", 274 | name: "Mother Love Bone" 275 | }, { 276 | children: [{ 277 | children: [], 278 | data: { 279 | $area: "67", 280 | $color: "37", 281 | image: "http://userserve-ak.last.fm/serve/174s/8634595.jpg" 282 | }, 283 | id: "7527f6c2-d762-4b88-b5e2-9244f1e34c46Around the Fur", 284 | name: "Around the Fur" 285 | }], 286 | data: { 287 | $area: 67 288 | }, 289 | id: "7527f6c2-d762-4b88-b5e2-9244f1e34c46a", 290 | name: "Deftones" 291 | }, { 292 | children: [{ 293 | children: [], 294 | data: { 295 | $area: "62", 296 | $color: "41", 297 | image: "http://images.amazon.com/images/P/B0000A5BYD.03.MZZZZZZZ.jpg" 298 | }, 299 | id: "7bdb6921-8380-422c-8514-87cf30d5d8ccIt All Makes Sense Now", 300 | name: "It All Makes Sense Now" 301 | }], 302 | data: { 303 | $area: 62 304 | }, 305 | id: "7bdb6921-8380-422c-8514-87cf30d5d8cca", 306 | name: "Kr\xF3m" 307 | }, { 308 | children: [{ 309 | children: [], 310 | data: { 311 | $area: "57", 312 | $color: "48", 313 | image: "http://images-eu.amazon.com/images/P/B00005IABM.02.MZZZZZZZ.jpg" 314 | }, 315 | id: "cb67438a-7f50-4f2b-a6f1-2bb2729fd53810,000 Hz Legend", 316 | name: "10,000 Hz Legend" 317 | }], 318 | data: { 319 | $area: 57 320 | }, 321 | id: "cb67438a-7f50-4f2b-a6f1-2bb2729fd538a", 322 | name: "Air" 323 | }, { 324 | children: [{ 325 | children: [], 326 | data: { 327 | $area: "168", 328 | $color: "5", 329 | image: "http://userserve-ak.last.fm/serve/174s/23091681.jpg" 330 | }, 331 | id: "c5998351-be49-49d8-8593-3e96f129c1fcMamagubida", 332 | name: "Mamagubida" 333 | }, { 334 | children: [], 335 | data: { 336 | $area: "141", 337 | $color: "7", 338 | image: "http://cdn.last.fm/flatness/catalogue/noimage/2/default_album_mega.png" 339 | }, 340 | id: "c5998351-be49-49d8-8593-3e96f129c1fcReggae \xE0 Coup de Cirque", 341 | name: "Reggae \xE0 Coup de Cirque" 342 | }, { 343 | children: [], 344 | data: { 345 | $area: "135", 346 | $color: "8", 347 | image: "http://userserve-ak.last.fm/serve/174s/16799743.jpg" 348 | }, 349 | id: "c5998351-be49-49d8-8593-3e96f129c1fcGrain de sable", 350 | name: "Grain de sable" 351 | }, { 352 | children: [], 353 | data: { 354 | $area: "80", 355 | $color: "30", 356 | image: "http://userserve-ak.last.fm/serve/174s/8635653.jpg" 357 | }, 358 | id: "c5998351-be49-49d8-8593-3e96f129c1fcFaut qu'ils s'activent...", 359 | name: "Faut qu'ils s'activent..." 360 | }], 361 | data: { 362 | $area: 524 363 | }, 364 | id: "c5998351-be49-49d8-8593-3e96f129c1fca", 365 | name: "Tryo" 366 | }, { 367 | children: [{ 368 | children: [], 369 | data: { 370 | $area: "57", 371 | $color: "48", 372 | image: "http://userserve-ak.last.fm/serve/174s/8634627.jpg" 373 | }, 374 | id: "4bb4e4e4-5f66-4509-98af-62dbb90c45c5The Sickness", 375 | name: "The Sickness" 376 | }], 377 | data: { 378 | $area: 57 379 | }, 380 | id: "4bb4e4e4-5f66-4509-98af-62dbb90c45c5a", 381 | name: "Disturbed" 382 | }, { 383 | children: [{ 384 | children: [], 385 | data: { 386 | $area: "72", 387 | $color: "34", 388 | image: "http://userserve-ak.last.fm/serve/174s/8673813.jpg" 389 | }, 390 | id: "95f5b748-d370-47fe-85bd-0af2dc450bc0Second-Hand Smoke", 391 | name: "Second-Hand Smoke" 392 | }], 393 | data: { 394 | $area: 72 395 | }, 396 | id: "95f5b748-d370-47fe-85bd-0af2dc450bc0a", 397 | name: "Sublime" 398 | }, { 399 | children: [{ 400 | children: [], 401 | data: { 402 | $area: "73", 403 | $color: "33", 404 | image: "http://userserve-ak.last.fm/serve/174s/7737695.jpg" 405 | }, 406 | id: "020bfbb4-05c3-4c86-b372-17825c262094Audioslave", 407 | name: "Audioslave" 408 | }], 409 | data: { 410 | $area: 73 411 | }, 412 | id: "020bfbb4-05c3-4c86-b372-17825c262094a", 413 | name: "Audioslave" 414 | }, { 415 | children: [{ 416 | children: [], 417 | data: { 418 | $area: "124", 419 | $color: "10", 420 | image: "http://userserve-ak.last.fm/serve/174s/8605651.jpg" 421 | }, 422 | id: "e9571c17-817f-4d34-ae3f-0c7a96f822c1Temple of the Dog", 423 | name: "Temple of the Dog" 424 | }], 425 | data: { 426 | $area: 124 427 | }, 428 | id: "e9571c17-817f-4d34-ae3f-0c7a96f822c1a", 429 | name: "Temple of the Dog" 430 | }, { 431 | children: [{ 432 | children: [], 433 | data: { 434 | $area: "82", 435 | $color: "29", 436 | image: "http://images.amazon.com/images/P/B0002ZEUKO.01._SCMZZZZZZZ_.jpg" 437 | }, 438 | id: "06fb1c8b-566e-4cb2-985b-b467c90781d4Are You Experienced?", 439 | name: "Are You Experienced?" 440 | }, { 441 | children: [], 442 | data: { 443 | $area: "64", 444 | $color: "39", 445 | image: "http://userserve-ak.last.fm/serve/174s/8729219.jpg" 446 | }, 447 | id: "06fb1c8b-566e-4cb2-985b-b467c90781d4First Rays of the New Rising Sun", 448 | name: "First Rays of the New Rising Sun" 449 | }], 450 | data: { 451 | $area: 146 452 | }, 453 | id: "06fb1c8b-566e-4cb2-985b-b467c90781d4a", 454 | name: "Jimi Hendrix" 455 | }, { 456 | children: [{ 457 | children: [], 458 | data: { 459 | $area: "56", 460 | $color: "50", 461 | image: "http://images.amazon.com/images/P/B0000DZDYN.01.MZZZZZZZ.jpg" 462 | }, 463 | id: "fbd2a255-1d57-4d31-ac11-65b671c19958The Singles 1992-2003", 464 | name: "The Singles 1992-2003" 465 | }], 466 | data: { 467 | $area: 56 468 | }, 469 | id: "fbd2a255-1d57-4d31-ac11-65b671c19958a", 470 | name: "No Doubt" 471 | }, { 472 | children: [{ 473 | children: [], 474 | data: { 475 | $area: "123", 476 | $color: "11", 477 | image: "http://userserve-ak.last.fm/serve/174s/11393921.jpg" 478 | }, 479 | id: "078a9376-3c04-4280-b7d7-b20e158f345dMer de Noms", 480 | name: "Mer de Noms" 481 | }, { 482 | children: [], 483 | data: { 484 | $area: "93", 485 | $color: "24", 486 | image: "http://userserve-ak.last.fm/serve/174s/11403219.jpg" 487 | }, 488 | id: "078a9376-3c04-4280-b7d7-b20e158f345dThirteenth Step", 489 | name: "Thirteenth Step" 490 | }], 491 | data: { 492 | $area: 216 493 | }, 494 | id: "078a9376-3c04-4280-b7d7-b20e158f345da", 495 | name: "A Perfect Circle" 496 | }, { 497 | children: [{ 498 | children: [], 499 | data: { 500 | $area: "109", 501 | $color: "16", 502 | image: "http://images.amazon.com/images/P/B00005LNP5.01._SCMZZZZZZZ_.jpg" 503 | }, 504 | id: "1fc56cff-f0a0-4ce2-ab1f-ac49cf3b073fElija y Gane", 505 | name: "Elija y Gane" 506 | }, { 507 | children: [], 508 | data: { 509 | $area: "85", 510 | $color: "26", 511 | image: "http://images.amazon.com/images/P/B0000B193V.01._SCMZZZZZZZ_.jpg" 512 | }, 513 | id: "1fc56cff-f0a0-4ce2-ab1f-ac49cf3b073fPara los Arboles", 514 | name: "Para los Arboles" 515 | }], 516 | data: { 517 | $area: 194 518 | }, 519 | id: "1fc56cff-f0a0-4ce2-ab1f-ac49cf3b073fa", 520 | name: "Luis Alberto Spinetta" 521 | }, { 522 | children: [{ 523 | children: [], 524 | data: { 525 | $area: "87", 526 | $color: "25", 527 | image: "http://userserve-ak.last.fm/serve/174s/8772827.jpg" 528 | }, 529 | id: "e795e03d-b5d5-4a5f-834d-162cfb308a2c4-Track Demos", 530 | name: "4-Track Demos" 531 | }, { 532 | children: [], 533 | data: { 534 | $area: "77", 535 | $color: "32", 536 | image: "http://userserve-ak.last.fm/serve/174s/9929071.jpg" 537 | }, 538 | id: "e795e03d-b5d5-4a5f-834d-162cfb308a2cRid of Me", 539 | name: "Rid of Me" 540 | }], 541 | data: { 542 | $area: 164 543 | }, 544 | id: "e795e03d-b5d5-4a5f-834d-162cfb308a2ca", 545 | name: "PJ Harvey" 546 | }, { 547 | children: [{ 548 | children: [], 549 | data: { 550 | $area: "102", 551 | $color: "19", 552 | image: "http://userserve-ak.last.fm/serve/174s/7410551.jpg" 553 | }, 554 | id: "e3e0abcd-7671-4482-a9d8-462f5acc9be5Make Yourself", 555 | name: "Make Yourself" 556 | }, { 557 | children: [], 558 | data: { 559 | $area: "84", 560 | $color: "27", 561 | image: "http://images.amazon.com/images/P/B00018D5CQ.01._SCMZZZZZZZ_.jpg" 562 | }, 563 | id: "e3e0abcd-7671-4482-a9d8-462f5acc9be5A Crow Left of the Murder", 564 | name: "A Crow Left of the Murder" 565 | }, { 566 | children: [], 567 | data: { 568 | $area: "60", 569 | $color: "46", 570 | image: "http://userserve-ak.last.fm/serve/174s/19681051.jpg" 571 | }, 572 | id: "e3e0abcd-7671-4482-a9d8-462f5acc9be5Morning View", 573 | name: "Morning View" 574 | }], 575 | data: { 576 | $area: 246 577 | }, 578 | id: "e3e0abcd-7671-4482-a9d8-462f5acc9be5a", 579 | name: "Incubus" 580 | }, { 581 | children: [{ 582 | children: [], 583 | data: { 584 | $area: "130", 585 | $color: "9", 586 | image: "http://userserve-ak.last.fm/serve/174s/15113951.jpg" 587 | }, 588 | id: "38c5cdab-5d6d-43d1-85b0-dac41bde186eNico", 589 | name: "Nico" 590 | }, { 591 | children: [], 592 | data: { 593 | $area: "120", 594 | $color: "12", 595 | image: "http://images.amazon.com/images/P/B00005V5PW.01.MZZZZZZZ.jpg" 596 | }, 597 | id: "38c5cdab-5d6d-43d1-85b0-dac41bde186eClassic Masters", 598 | name: "Classic Masters" 599 | }, { 600 | children: [], 601 | data: { 602 | $area: "103", 603 | $color: "18", 604 | image: "http://images.amazon.com/images/P/B000002TPF.01.MZZZZZZZ.jpg" 605 | }, 606 | id: "38c5cdab-5d6d-43d1-85b0-dac41bde186eSoup", 607 | name: "Soup" 608 | }, { 609 | children: [], 610 | data: { 611 | $area: "99", 612 | $color: "20", 613 | image: "http://userserve-ak.last.fm/serve/174s/15157989.jpg" 614 | }, 615 | id: "38c5cdab-5d6d-43d1-85b0-dac41bde186eBlind Melon", 616 | name: "Blind Melon" 617 | }], 618 | data: { 619 | $area: 452 620 | }, 621 | id: "38c5cdab-5d6d-43d1-85b0-dac41bde186ea", 622 | name: "Blind Melon" 623 | }, { 624 | children: [{ 625 | children: [], 626 | data: { 627 | $area: "173", 628 | $color: "4", 629 | image: "http://userserve-ak.last.fm/serve/174s/8590515.jpg" 630 | }, 631 | id: "153c9281-268f-4cf3-8938-f5a4593e5df4Superunknown", 632 | name: "Superunknown" 633 | }, { 634 | children: [], 635 | data: { 636 | $area: "117", 637 | $color: "13", 638 | image: "http://userserve-ak.last.fm/serve/174s/5269310.jpg" 639 | }, 640 | id: "153c9281-268f-4cf3-8938-f5a4593e5df4Louder Than Love", 641 | name: "Louder Than Love" 642 | }, { 643 | children: [], 644 | data: { 645 | $area: "96", 646 | $color: "21", 647 | image: "http://userserve-ak.last.fm/serve/174s/8600371.jpg" 648 | }, 649 | id: "153c9281-268f-4cf3-8938-f5a4593e5df4Down on the Upside", 650 | name: "Down on the Upside" 651 | }, { 652 | children: [], 653 | data: { 654 | $area: "95", 655 | $color: "22", 656 | image: "http://images.amazon.com/images/P/B000000M4A.01.MZZZZZZZ.jpg" 657 | }, 658 | id: "153c9281-268f-4cf3-8938-f5a4593e5df4Ultramega OK", 659 | name: "Ultramega OK" 660 | }], 661 | data: { 662 | $area: 481 663 | }, 664 | id: "153c9281-268f-4cf3-8938-f5a4593e5df4a", 665 | name: "Soundgarden" 666 | }], 667 | data: { 668 | $area: 4949 669 | }, 670 | id: "topAlbums", 671 | name: "top albums" 672 | }; 673 | //end 674 | 675 | var infovis = document.getElementById('infovis'); 676 | var w = infovis.offsetWidth, h = infovis.offsetHeight; 677 | infovis.style.width = w + 'px'; 678 | infovis.style.height = h + 'px'; 679 | 680 | //init tm 681 | //You can also do TM.SliceAndDice and TM.Squarified 682 | var tm = new TM.Strip({ 683 | //Where to inject the treemap. 684 | rootId: 'infovis', 685 | 686 | titleHeight: 0, 687 | orientation: "h", 688 | offset: 0, 689 | 690 | //Add click handlers for 691 | //zooming the Treemap in and out 692 | addLeftClickHandler: true, 693 | addRightClickHandler: true, 694 | 695 | //When hovering a node highlight the nodes 696 | //between the root node and the hovered node. This 697 | //is done by adding the 'in-path' CSS class to each node. 698 | selectPathOnHover: true, 699 | 700 | Color: { 701 | //Allow coloring 702 | enable: true, 703 | //Set min value and max value constraints 704 | //for the *$color* property value. 705 | //Default's to -100 and 100. 706 | minValue: 1, 707 | maxValue: 50, 708 | //Set color range. Default's to reddish and greenish. 709 | //It takes an array of three 710 | //integers as R, G and B values. 711 | minColorValue: [0, 255, 50], 712 | maxColorValue: [255, 0, 50] 713 | }, 714 | 715 | //Allow tips 716 | Tips: { 717 | enable: true, 718 | //add positioning offsets 719 | offsetX: 20, 720 | offsetY: 20, 721 | //implement the onShow method to 722 | //add content to the tooltip when a node 723 | //is hovered 724 | onShow: function(tip, node, isLeaf, domElement) { 725 | tip.innerHTML = "
" + node.name + "
" + 726 | "
" + this.makeHTMLFromData(node.data) + "
"; 727 | }, 728 | 729 | //Build the tooltip inner html by taking each node data property 730 | makeHTMLFromData: function(data){ 731 | var html = ''; 732 | html += "playcount" + ': ' + data.$area + '
'; 733 | if ("$color" in data) 734 | html += "rank" + ': ' + data.$color + '
'; 735 | if ("image" in data) 736 | html += ""; 737 | return html; 738 | } 739 | }, 740 | 741 | //This method is invoked when a DOM element is created. 742 | //Its useful to set DOM event handlers here or manipulate 743 | //the DOM Treemap nodes. 744 | onCreateElement: function(content, tree, isLeaf, leaf){ 745 | //Add background image 746 | if(isLeaf) { 747 | var style = leaf.style, 748 | width = parseInt(style.width) - 2, 749 | height = parseInt(style.height) - 2; 750 | 751 | leaf.innerHTML = tree.name + 752 | ""; 755 | 756 | style.width = width + "px"; 757 | style.height = height + "px"; 758 | } 759 | }, 760 | 761 | //Remove all events for the element before destroying it. 762 | onDestroyElement: function(content, tree, isLeaf, leaf){ 763 | if(leaf.clearAttributes) leaf.clearAttributes(); 764 | } 765 | }); 766 | 767 | //load JSON and plot 768 | tm.loadJSON(json); 769 | //end 770 | } 771 | -------------------------------------------------------------------------------- /README.mkd: -------------------------------------------------------------------------------- 1 | DOMTreeMap 2 | ========= 3 | 4 | DOMTreeMap is a robust, library agnostic, HTML/CSS/JavaScript TreeMap visualization library. 5 | 6 | ![A Cushion Strip TreeMap example](http://blog.thejit.org/assets/static/img/cushion.png) 7 | 8 | Description 9 | --------- 10 | 11 | DOMTreeMap implements multiple TreeMap tiling algorithms: Strip, Squarified and SliceAndDice. 12 | The library also provides built-in support for Colored nodes and HTML Tooltips. 13 | 14 | DOMTreeMap is extensible in many ways. Since DOMTreeMap nodes are HTML elements they're very easy 15 | to customize with CSS and add behavior with JavaScript. 16 | 17 | If you want to know more about how to use this library please take a look at the examples. 18 | The same TreeMap implementation can be found [here](http://thejit.org/demos). 19 | 20 | Status 21 | --------- 22 | 23 | No formal releases (since I have to tweak the documentation a little bit). The library is fully functional though, as can be seen in the Examples. 24 | 25 | Download 26 | --------- 27 | 28 | Clone the repo from GitHub 29 | 30 | $ git clone git://github.com/philogb/dom-treemap.git 31 | 32 | And play with the examples! 33 | 34 | 35 | Example 36 | --------- 37 | 38 | Here's an instanciation example (taken from example1.js). 39 | 40 | var tm = new TM.Squarified({ 41 | //Where to inject the treemap. 42 | rootId: 'infovis', 43 | 44 | //Add click handlers for 45 | //zooming the Treemap in and out 46 | addLeftClickHandler: true, 47 | addRightClickHandler: true, 48 | 49 | //When hovering a node highlight the nodes 50 | //between the root node and the hovered node. This 51 | //is done by adding the 'in-path' CSS class to each node. 52 | selectPathOnHover: true, 53 | 54 | Color: { 55 | //Allow coloring 56 | enable: true, 57 | //Set min value and max value constraints 58 | //for the *$color* property value. 59 | //Default's to -100 and 100. 60 | minValue: 1, 61 | maxValue: 50, 62 | //Set color range. Default's to reddish and greenish. 63 | //It takes an array of three 64 | //integers as R, G and B values. 65 | minColorValue: [0, 255, 50], 66 | maxColorValue: [255, 0, 50] 67 | }, 68 | 69 | //Allow tips 70 | Tips: { 71 | enable: true, 72 | //add positioning offsets 73 | offsetX: 20, 74 | offsetY: 20, 75 | //implement the onShow method to 76 | //add content to the tooltip when a node 77 | //is hovered 78 | onShow: function(tip, node, isLeaf, domElement) { 79 | tip.innerHTML = "
" + node.name + "
" + 80 | "
" + this.makeHTMLFromData(node.data) + "
"; 81 | }, 82 | 83 | //Build the tooltip inner html by taking each node data property 84 | makeHTMLFromData: function(data){ 85 | var html = ''; 86 | html += "playcount" + ': ' + data.$area + '
'; 87 | if ("$color" in data) 88 | html += "rank" + ': ' + data.$color + '
'; 89 | if ("image" in data) 90 | html += ""; 91 | return html; 92 | } 93 | }, 94 | 95 | //Remove all element events before destroying it. 96 | onDestroyElement: function(content, tree, isLeaf, leaf){ 97 | if(leaf.clearAttributes) leaf.clearAttributes(); 98 | } 99 | }); 100 | 101 | //load JSON and plot 102 | tm.loadJSON(json); 103 | 104 | 105 | License 106 | --------- 107 | 108 | BSD License. 109 | 110 | Redistribution and use in source and binary forms, with or without 111 | modification, are permitted provided that the following conditions are met: 112 | * Redistributions of source code must retain the above copyright 113 | notice, this list of conditions and the following disclaimer. 114 | * Redistributions in binary form must reproduce the above copyright 115 | notice, this list of conditions and the following disclaimer in the 116 | documentation and/or other materials provided with the distribution. 117 | * Neither the name of the organization nor the 118 | names of its contributors may be used to endorse or promote products 119 | derived from this software without specific prior written permission. 120 | 121 | THIS SOFTWARE IS PROVIDED BY Nicolas Garcia Belmonte ``AS IS'' AND ANY 122 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 123 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 124 | DISCLAIMED. IN NO EVENT SHALL Nicolas Garcia Belmonte BE LIABLE FOR ANY 125 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 126 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 127 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 128 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 129 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 130 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 131 | --------------------------------------------------------------------------------