/*1Copyright (c) 2011 Sencha Inc. - Author: Nicolas Garcia Belmonte (http://philogb.github.com/)23Permission is hereby granted, free of charge, to any person obtaining a copy4of this software and associated documentation files (the "Software"), to deal5in the Software without restriction, including without limitation the rights6to use, copy, modify, merge, publish, distribute, sublicense, and/or sell7copies of the Software, and to permit persons to whom the Software is8furnished to do so, subject to the following conditions:910The above copyright notice and this permission notice shall be included in11all copies or substantial portions of the Software.1213THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR14IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,15FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE16AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER17LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,18OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN19THE SOFTWARE.20*/21(function () {2223/*24File: Core.js2526/*27Object: $jit2829Defines the namespace for all library Classes and Objects.30This variable is the *only* global variable defined in the Toolkit.31There are also other interesting properties attached to this variable described below.32*/33window.$jit = function(w) {34w = w || window;35for(var k in $jit) {36if($jit[k].$extend) {37w[k] = $jit[k];38}39}40};4142$jit.version = '2.0.1';43/*44Object: $jit.id4546Works just like *document.getElementById*4748Example:49(start code js)50var element = $jit.id('elementId');51(end code)5253*/5455/*56Object: $jit.util5758Contains utility functions.5960Some of the utility functions and the Class system were based in the MooTools Framework61<http://mootools.net>. Copyright (c) 2006-2010 Valerio Proietti, <http://mad4milk.net/>.62MIT license <http://mootools.net/license.txt>.6364These methods are generally also implemented in DOM manipulation frameworks like JQuery, MooTools and Prototype.65I'd suggest you to use the functions from those libraries instead of using these, since their functions66are widely used and tested in many different platforms/browsers. Use these functions only if you have to.6768*/69var $ = function(d) {70return document.getElementById(d);71};7273$.empty = function() {74};7576/*77Method: extend7879Augment an object by appending another object's properties.8081Parameters:8283original - (object) The object to be extended.84extended - (object) An object which properties are going to be appended to the original object.8586Example:87(start code js)88$jit.util.extend({ 'a': 1, 'b': 2 }, { 'b': 3, 'c': 4 }); //{ 'a':1, 'b': 3, 'c': 4 }89(end code)90*/91$.extend = function(original, extended) {92for ( var key in (extended || {}))93original[key] = extended[key];94return original;95};9697$.lambda = function(value) {98return (typeof value == 'function') ? value : function() {99return value;100};101};102103$.time = Date.now || function() {104return +new Date;105};106107/*108Method: splat109110Returns an array wrapping *obj* if *obj* is not an array. Returns *obj* otherwise.111112Parameters:113114obj - (mixed) The object to be wrapped in an array.115116Example:117(start code js)118$jit.util.splat(3); //[3]119$jit.util.splat([3]); //[3]120(end code)121*/122$.splat = function(obj) {123var type = $.type(obj);124return type ? ((type != 'array') ? [ obj ] : obj) : [];125};126127$.type = function(elem) {128var type = $.type.s.call(elem).match(/^\[object\s(.*)\]$/)[1].toLowerCase();129if(type != 'object') return type;130if(elem && elem.$$family) return elem.$$family;131return (elem && elem.nodeName && elem.nodeType == 1)? 'element' : type;132};133$.type.s = Object.prototype.toString;134135/*136Method: each137138Iterates through an iterable applying *f*.139140Parameters:141142iterable - (array) The original array.143fn - (function) The function to apply to the array elements.144145Example:146(start code js)147$jit.util.each([3, 4, 5], function(n) { alert('number ' + n); });148(end code)149*/150$.each = function(iterable, fn) {151var type = $.type(iterable);152if (type == 'object') {153for ( var key in iterable)154fn(iterable[key], key);155} else {156for ( var i = 0, l = iterable.length; i < l; i++)157fn(iterable[i], i);158}159};160161$.indexOf = function(array, item) {162if(Array.indexOf) return array.indexOf(item);163for(var i=0,l=array.length; i<l; i++) {164if(array[i] === item) return i;165}166return -1;167};168169/*170Method: map171172Maps or collects an array by applying *f*.173174Parameters:175176array - (array) The original array.177f - (function) The function to apply to the array elements.178179Example:180(start code js)181$jit.util.map([3, 4, 5], function(n) { return n*n; }); //[9, 16, 25]182(end code)183*/184$.map = function(array, f) {185var ans = [];186$.each(array, function(elem, i) {187ans.push(f(elem, i));188});189return ans;190};191192/*193Method: reduce194195Iteratively applies the binary function *f* storing the result in an accumulator.196197Parameters:198199array - (array) The original array.200f - (function) The function to apply to the array elements.201opt - (optional|mixed) The starting value for the acumulator.202203Example:204(start code js)205$jit.util.reduce([3, 4, 5], function(x, y) { return x + y; }, 0); //12206(end code)207*/208$.reduce = function(array, f, opt) {209var l = array.length;210if(l==0) return opt;211var acum = arguments.length == 3? opt : array[--l];212while(l--) {213acum = f(acum, array[l]);214}215return acum;216};217218/*219Method: merge220221Merges n-objects and their sub-objects creating a new, fresh object.222223Parameters:224225An arbitrary number of objects.226227Example:228(start code js)229$jit.util.merge({ 'a': 1, 'b': 2 }, { 'b': 3, 'c': 4 }); //{ 'a':1, 'b': 3, 'c': 4 }230(end code)231*/232$.merge = function() {233var mix = {};234for ( var i = 0, l = arguments.length; i < l; i++) {235var object = arguments[i];236if ($.type(object) != 'object')237continue;238for ( var key in object) {239var op = object[key], mp = mix[key];240mix[key] = (mp && $.type(op) == 'object' && $.type(mp) == 'object') ? $241.merge(mp, op) : $.unlink(op);242}243}244return mix;245};246247$.unlink = function(object) {248var unlinked;249switch ($.type(object)) {250case 'object':251unlinked = {};252for ( var p in object)253unlinked[p] = $.unlink(object[p]);254break;255case 'array':256unlinked = [];257for ( var i = 0, l = object.length; i < l; i++)258unlinked[i] = $.unlink(object[i]);259break;260default:261return object;262}263return unlinked;264};265266$.zip = function() {267if(arguments.length === 0) return [];268for(var j=0, ans=[], l=arguments.length, ml=arguments[0].length; j<ml; j++) {269for(var i=0, row=[]; i<l; i++) {270row.push(arguments[i][j]);271}272ans.push(row);273}274return ans;275};276277/*278Method: rgbToHex279280Converts an RGB array into a Hex string.281282Parameters:283284srcArray - (array) An array with R, G and B values285286Example:287(start code js)288$jit.util.rgbToHex([255, 255, 255]); //'#ffffff'289(end code)290*/291$.rgbToHex = function(srcArray, array) {292if (srcArray.length < 3)293return null;294if (srcArray.length == 4 && srcArray[3] == 0 && !array)295return 'transparent';296var hex = [];297for ( var i = 0; i < 3; i++) {298var bit = (srcArray[i] - 0).toString(16);299hex.push(bit.length == 1 ? '0' + bit : bit);300}301return array ? hex : '#' + hex.join('');302};303304/*305Method: hexToRgb306307Converts an Hex color string into an RGB array.308309Parameters:310311hex - (string) A color hex string.312313Example:314(start code js)315$jit.util.hexToRgb('#fff'); //[255, 255, 255]316(end code)317*/318$.hexToRgb = function(hex) {319if (hex.length != 7) {320hex = hex.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);321hex.shift();322if (hex.length != 3)323return null;324var rgb = [];325for ( var i = 0; i < 3; i++) {326var value = hex[i];327if (value.length == 1)328value += value;329rgb.push(parseInt(value, 16));330}331return rgb;332} else {333hex = parseInt(hex.slice(1), 16);334return [ hex >> 16, hex >> 8 & 0xff, hex & 0xff ];335}336};337338$.destroy = function(elem) {339$.clean(elem);340if (elem.parentNode)341elem.parentNode.removeChild(elem);342if (elem.clearAttributes)343elem.clearAttributes();344};345346$.clean = function(elem) {347for (var ch = elem.childNodes, i = 0, l = ch.length; i < l; i++) {348$.destroy(ch[i]);349}350};351352/*353Method: addEvent354355Cross-browser add event listener.356357Parameters:358359obj - (obj) The Element to attach the listener to.360type - (string) The listener type. For example 'click', or 'mousemove'.361fn - (function) The callback function to be used when the event is fired.362363Example:364(start code js)365$jit.util.addEvent(elem, 'click', function(){ alert('hello'); });366(end code)367*/368$.addEvent = function(obj, type, fn) {369if (obj.addEventListener)370obj.addEventListener(type, fn, false);371else372obj.attachEvent('on' + type, fn);373};374375$.addEvents = function(obj, typeObj) {376for(var type in typeObj) {377$.addEvent(obj, type, typeObj[type]);378}379};380381$.hasClass = function(obj, klass) {382return (' ' + obj.className + ' ').indexOf(' ' + klass + ' ') > -1;383};384385$.addClass = function(obj, klass) {386if (!$.hasClass(obj, klass))387obj.className = (obj.className + " " + klass);388};389390$.removeClass = function(obj, klass) {391obj.className = obj.className.replace(new RegExp(392'(^|\\s)' + klass + '(?:\\s|$)'), '$1');393};394395$.getPos = function(elem) {396var offset = getOffsets(elem);397var scroll = getScrolls(elem);398return {399x: offset.x - scroll.x,400y: offset.y - scroll.y401};402403function getOffsets(elem) {404var position = {405x: 0,406y: 0407};408while (elem && !isBody(elem)) {409position.x += elem.offsetLeft;410position.y += elem.offsetTop;411elem = elem.offsetParent;412}413return position;414}415416function getScrolls(elem) {417var position = {418x: 0,419y: 0420};421while (elem && !isBody(elem)) {422position.x += elem.scrollLeft;423position.y += elem.scrollTop;424elem = elem.parentNode;425}426return position;427}428429function isBody(element) {430return (/^(?:body|html)$/i).test(element.tagName);431}432};433434$.event = {435get: function(e, win) {436win = win || window;437return e || win.event;438},439getWheel: function(e) {440return e.wheelDelta? e.wheelDelta / 120 : -(e.detail || 0) / 3;441},442isRightClick: function(e) {443return (e.which == 3 || e.button == 2);444},445getPos: function(e, win) {446// get mouse position447win = win || window;448e = e || win.event;449var doc = win.document;450doc = doc.documentElement || doc.body;451//make touch event handling better452if(e.touches && e.touches.length) {453e = e.touches[0];454}455var page = {456x: e.pageX || (e.clientX + doc.scrollLeft),457y: e.pageY || (e.clientY + doc.scrollTop)458};459return page;460},461stop: function(e) {462if (e.stopPropagation) e.stopPropagation();463e.cancelBubble = true;464if (e.preventDefault) e.preventDefault();465else e.returnValue = false;466}467};468469$jit.util = $jit.id = $;470471var Class = function(properties) {472properties = properties || {};473var klass = function() {474for ( var key in this) {475if (typeof this[key] != 'function')476this[key] = $.unlink(this[key]);477}478this.constructor = klass;479if (Class.prototyping)480return this;481var instance = this.initialize ? this.initialize.apply(this, arguments)482: this;483//typize484this.$$family = 'class';485return instance;486};487488for ( var mutator in Class.Mutators) {489if (!properties[mutator])490continue;491properties = Class.Mutators[mutator](properties, properties[mutator]);492delete properties[mutator];493}494495$.extend(klass, this);496klass.constructor = Class;497klass.prototype = properties;498return klass;499};500501Class.Mutators = {502503Implements: function(self, klasses) {504$.each($.splat(klasses), function(klass) {505Class.prototyping = klass;506var instance = (typeof klass == 'function') ? new klass : klass;507for ( var prop in instance) {508if (!(prop in self)) {509self[prop] = instance[prop];510}511}512delete Class.prototyping;513});514return self;515}516517};518519$.extend(Class, {520521inherit: function(object, properties) {522for ( var key in properties) {523var override = properties[key];524var previous = object[key];525var type = $.type(override);526if (previous && type == 'function') {527if (override != previous) {528Class.override(object, key, override);529}530} else if (type == 'object') {531object[key] = $.merge(previous, override);532} else {533object[key] = override;534}535}536return object;537},538539override: function(object, name, method) {540var parent = Class.prototyping;541if (parent && object[name] != parent[name])542parent = null;543var override = function() {544var previous = this.parent;545this.parent = parent ? parent[name] : object[name];546var value = method.apply(this, arguments);547this.parent = previous;548return value;549};550object[name] = override;551}552553});554555Class.prototype.implement = function() {556var proto = this.prototype;557$.each(Array.prototype.slice.call(arguments || []), function(properties) {558Class.inherit(proto, properties);559});560return this;561};562563$jit.Class = Class;564565/*566Object: $jit.json567568Provides JSON utility functions.569570Most of these functions are JSON-tree traversal and manipulation functions.571*/572$jit.json = {573/*574Method: prune575576Clears all tree nodes having depth greater than maxLevel.577578Parameters:579580tree - (object) A JSON tree object. For more information please see <Loader.loadJSON>.581maxLevel - (number) An integer specifying the maximum level allowed for this tree. All nodes having depth greater than max level will be deleted.582583*/584prune: function(tree, maxLevel) {585this.each(tree, function(elem, i) {586if (i == maxLevel && elem.children) {587delete elem.children;588elem.children = [];589}590});591},592/*593Method: getParent594595Returns the parent node of the node having _id_ as id.596597Parameters:598599tree - (object) A JSON tree object. See also <Loader.loadJSON>.600id - (string) The _id_ of the child node whose parent will be returned.601602Returns:603604A tree JSON node if any, or false otherwise.605606*/607getParent: function(tree, id) {608if (tree.id == id)609return false;610var ch = tree.children;611if (ch && ch.length > 0) {612for ( var i = 0; i < ch.length; i++) {613if (ch[i].id == id)614return tree;615else {616var ans = this.getParent(ch[i], id);617if (ans)618return ans;619}620}621}622return false;623},624/*625Method: getSubtree626627Returns the subtree that matches the given id.628629Parameters:630631tree - (object) A JSON tree object. See also <Loader.loadJSON>.632id - (string) A node *unique* identifier.633634Returns:635636A subtree having a root node matching the given id. Returns null if no subtree matching the id is found.637638*/639getSubtree: function(tree, id) {640if (tree.id == id)641return tree;642for ( var i = 0, ch = tree.children; ch && i < ch.length; i++) {643var t = this.getSubtree(ch[i], id);644if (t != null)645return t;646}647return null;648},649/*650Method: eachLevel651652Iterates on tree nodes with relative depth less or equal than a specified level.653654Parameters:655656tree - (object) A JSON tree or subtree. See also <Loader.loadJSON>.657initLevel - (number) An integer specifying the initial relative level. Usually zero.658toLevel - (number) An integer specifying a top level. This method will iterate only through nodes with depth less than or equal this number.659action - (function) A function that receives a node and an integer specifying the actual level of the node.660661Example:662(start code js)663$jit.json.eachLevel(tree, 0, 3, function(node, depth) {664alert(node.name + ' ' + depth);665});666(end code)667*/668eachLevel: function(tree, initLevel, toLevel, action) {669if (initLevel <= toLevel) {670action(tree, initLevel);671if(!tree.children) return;672for ( var i = 0, ch = tree.children; i < ch.length; i++) {673this.eachLevel(ch[i], initLevel + 1, toLevel, action);674}675}676},677/*678Method: each679680A JSON tree iterator.681682Parameters:683684tree - (object) A JSON tree or subtree. See also <Loader.loadJSON>.685action - (function) A function that receives a node.686687Example:688(start code js)689$jit.json.each(tree, function(node) {690alert(node.name);691});692(end code)693694*/695each: function(tree, action) {696this.eachLevel(tree, 0, Number.MAX_VALUE, action);697}698};699700701/*702An object containing multiple type of transformations.703*/704705$jit.Trans = {706$extend: true,707708linear: function(p){709return p;710}711};712713var Trans = $jit.Trans;714715(function(){716717var makeTrans = function(transition, params){718params = $.splat(params);719return $.extend(transition, {720easeIn: function(pos){721return transition(pos, params);722},723easeOut: function(pos){724return 1 - transition(1 - pos, params);725},726easeInOut: function(pos){727return (pos <= 0.5)? transition(2 * pos, params) / 2 : (2 - transition(7282 * (1 - pos), params)) / 2;729}730});731};732733var transitions = {734735Pow: function(p, x){736return Math.pow(p, x[0] || 6);737},738739Expo: function(p){740return Math.pow(2, 8 * (p - 1));741},742743Circ: function(p){744return 1 - Math.sin(Math.acos(p));745},746747Sine: function(p){748return 1 - Math.sin((1 - p) * Math.PI / 2);749},750751Back: function(p, x){752x = x[0] || 1.618;753return Math.pow(p, 2) * ((x + 1) * p - x);754},755756Bounce: function(p){757var value;758for ( var a = 0, b = 1; 1; a += b, b /= 2) {759if (p >= (7 - 4 * a) / 11) {760value = b * b - Math.pow((11 - 6 * a - 11 * p) / 4, 2);761break;762}763}764return value;765},766767Elastic: function(p, x){768return Math.pow(2, 10 * --p)769* Math.cos(20 * p * Math.PI * (x[0] || 1) / 3);770}771772};773774$.each(transitions, function(val, key){775Trans[key] = makeTrans(val);776});777778$.each( [779'Quad', 'Cubic', 'Quart', 'Quint'780], function(elem, i){781Trans[elem] = makeTrans(function(p){782return Math.pow(p, [783i + 2784]);785});786});787788})();789790/*791A Class that can perform animations for generic objects.792793If you are looking for animation transitions please take a look at the <Trans> object.794795Used by:796797<Graph.Plot>798799Based on:800801The Animation class is based in the MooTools Framework <http://mootools.net>. Copyright (c) 2006-2009 Valerio Proietti, <http://mad4milk.net/>. MIT license <http://mootools.net/license.txt>.802803*/804805var Animation = new Class( {806807initialize: function(options){808this.setOptions(options);809},810811setOptions: function(options){812var opt = {813duration: 2500,814fps: 40,815transition: Trans.Quart.easeInOut,816compute: $.empty,817complete: $.empty,818link: 'ignore'819};820this.opt = $.merge(opt, options || {});821return this;822},823824step: function(){825var time = $.time(), opt = this.opt;826if (time < this.time + opt.duration) {827var delta = opt.transition((time - this.time) / opt.duration);828opt.compute(delta);829} else {830this.timer = clearInterval(this.timer);831opt.compute(1);832opt.complete();833}834},835836start: function(){837if (!this.check())838return this;839this.time = 0;840this.startTimer();841return this;842},843844startTimer: function(){845var that = this, fps = this.opt.fps;846if (this.timer)847return false;848this.time = $.time() - this.time;849this.timer = setInterval((function(){850that.step();851}), Math.round(1000 / fps));852return true;853},854855pause: function(){856this.stopTimer();857return this;858},859860resume: function(){861this.startTimer();862return this;863},864865stopTimer: function(){866if (!this.timer)867return false;868this.time = $.time() - this.time;869this.timer = clearInterval(this.timer);870return true;871},872873check: function(){874if (!this.timer)875return true;876if (this.opt.link == 'cancel') {877this.stopTimer();878return true;879}880return false;881}882});883884885var Options = function() {886var args = arguments;887for(var i=0, l=args.length, ans={}; i<l; i++) {888var opt = Options[args[i]];889if(opt.$extend) {890$.extend(ans, opt);891} else {892ans[args[i]] = opt;893}894}895return ans;896};897898/*899* File: Options.AreaChart.js900*901*/902903/*904Object: Options.AreaChart905906<AreaChart> options.907Other options included in the AreaChart are <Options.Canvas>, <Options.Label>, <Options.Margin>, <Options.Tips> and <Options.Events>.908909Syntax:910911(start code js)912913Options.AreaChart = {914animate: true,915labelOffset: 3,916type: 'stacked',917selectOnHover: true,918showAggregates: true,919showLabels: true,920filterOnClick: false,921restoreOnRightClick: false922};923924(end code)925926Example:927928(start code js)929930var areaChart = new $jit.AreaChart({931animate: true,932type: 'stacked:gradient',933selectOnHover: true,934filterOnClick: true,935restoreOnRightClick: true936});937938(end code)939940Parameters:941942animate - (boolean) Default's *true*. Whether to add animated transitions when filtering/restoring stacks.943labelOffset - (number) Default's *3*. Adds margin between the label and the default place where it should be drawn.944type - (string) Default's *'stacked'*. Stack style. Posible values are 'stacked', 'stacked:gradient' to add gradients.945selectOnHover - (boolean) Default's *true*. If true, it will add a mark to the hovered stack.946showAggregates - (boolean, function) Default's *true*. Display the values of the stacks. Can also be a function that returns *true* or *false* to display or filter some values. That same function can also return a string with the formatted value.947showLabels - (boolean, function) Default's *true*. Display the name of the slots. Can also be a function that returns *true* or *false* to display or not each label.948filterOnClick - (boolean) Default's *true*. Select the clicked stack by hiding all other stacks.949restoreOnRightClick - (boolean) Default's *true*. Show all stacks by right clicking.950951*/952953Options.AreaChart = {954$extend: true,955956animate: true,957labelOffset: 3, // label offset958type: 'stacked', // gradient959Tips: {960enable: false,961onShow: $.empty,962onHide: $.empty963},964Events: {965enable: false,966onClick: $.empty967},968selectOnHover: true,969showAggregates: true,970showLabels: true,971filterOnClick: false,972restoreOnRightClick: false973};974975/*976* File: Options.Margin.js977*978*/979980/*981Object: Options.Margin982983Canvas drawing margins.984985Syntax:986987(start code js)988989Options.Margin = {990top: 0,991left: 0,992right: 0,993bottom: 0994};995996(end code)997998Example:9991000(start code js)10011002var viz = new $jit.Viz({1003Margin: {1004right: 10,1005bottom: 201006}1007});10081009(end code)10101011Parameters:10121013top - (number) Default's *0*. Top margin.1014left - (number) Default's *0*. Left margin.1015right - (number) Default's *0*. Right margin.1016bottom - (number) Default's *0*. Bottom margin.10171018*/10191020Options.Margin = {1021$extend: false,10221023top: 0,1024left: 0,1025right: 0,1026bottom: 01027};10281029/*1030* File: Options.Canvas.js1031*1032*/10331034/*1035Object: Options.Canvas10361037These are Canvas general options, like where to append it in the DOM, its dimensions, background,1038and other more advanced options.10391040Syntax:10411042(start code js)10431044Options.Canvas = {1045injectInto: 'id',1046type: '2D', //'3D'1047width: false,1048height: false,1049useCanvas: false,1050withLabels: true,1051background: false1052};1053(end code)10541055Example:10561057(start code js)1058var viz = new $jit.Viz({1059injectInto: 'someContainerId',1060width: 500,1061height: 7001062});1063(end code)10641065Parameters:10661067injectInto - *required* (string|element) The id of the DOM container for the visualization. It can also be an Element provided that it has an id.1068type - (string) Context type. Default's 2D but can be 3D for webGL enabled browsers.1069width - (number) Default's to the *container's offsetWidth*. The width of the canvas.1070height - (number) Default's to the *container's offsetHeight*. The height of the canvas.1071useCanvas - (boolean|object) Default's *false*. You can pass another <Canvas> instance to be used by the visualization.1072withLabels - (boolean) Default's *true*. Whether to use a label container for the visualization.1073background - (boolean|object) Default's *false*. An object containing information about the rendering of a background canvas.1074*/10751076Options.Canvas = {1077$extend: true,10781079injectInto: 'id',1080type: '2D',1081width: false,1082height: false,1083useCanvas: false,1084withLabels: true,1085background: false,10861087Scene: {1088Lighting: {1089enable: false,1090ambient: [1, 1, 1],1091directional: {1092direction: { x: -100, y: -100, z: -100 },1093color: [0.5, 0.3, 0.1]1094}1095}1096}1097};10981099/*1100* File: Options.Tree.js1101*1102*/11031104/*1105Object: Options.Tree11061107Options related to (strict) Tree layout algorithms. These options are used by the <ST> visualization.11081109Syntax:11101111(start code js)1112Options.Tree = {1113orientation: "left",1114subtreeOffset: 8,1115siblingOffset: 5,1116indent:10,1117multitree: false,1118align:"center"1119};1120(end code)11211122Example:11231124(start code js)1125var st = new $jit.ST({1126orientation: 'left',1127subtreeOffset: 1,1128siblingOFfset: 5,1129multitree: true1130});1131(end code)11321133Parameters:11341135subtreeOffset - (number) Default's 8. Separation offset between subtrees.1136siblingOffset - (number) Default's 5. Separation offset between siblings.1137orientation - (string) Default's 'left'. Tree orientation layout. Possible values are 'left', 'top', 'right', 'bottom'.1138align - (string) Default's *center*. Whether the tree alignment is 'left', 'center' or 'right'.1139indent - (number) Default's 10. Used when *align* is left or right and shows an indentation between parent and children.1140multitree - (boolean) Default's *false*. Used with the node $orn data property for creating multitrees.11411142*/1143Options.Tree = {1144$extend: true,11451146orientation: "left",1147subtreeOffset: 8,1148siblingOffset: 5,1149indent:10,1150multitree: false,1151align:"center"1152};115311541155/*1156* File: Options.Node.js1157*1158*/11591160/*1161Object: Options.Node11621163Provides Node rendering options for Tree and Graph based visualizations.11641165Syntax:11661167(start code js)1168Options.Node = {1169overridable: false,1170type: 'circle',1171color: '#ccb',1172alpha: 1,1173dim: 3,1174height: 20,1175width: 90,1176autoHeight: false,1177autoWidth: false,1178lineWidth: 1,1179transform: true,1180align: "center",1181angularWidth:1,1182span:1,1183CanvasStyles: {}1184};1185(end code)11861187Example:11881189(start code js)1190var viz = new $jit.Viz({1191Node: {1192overridable: true,1193width: 30,1194autoHeight: true,1195type: 'rectangle'1196}1197});1198(end code)11991200Parameters:12011202overridable - (boolean) Default's *false*. Determine whether or not general node properties can be overridden by a particular <Graph.Node>.1203type - (string) Default's *circle*. Node's shape. Node built-in types include 'circle', 'rectangle', 'square', 'ellipse', 'triangle', 'star'. The default Node type might vary in each visualization. You can also implement (non built-in) custom Node types into your visualizations.1204color - (string) Default's *#ccb*. Node color.1205alpha - (number) Default's *1*. The Node's alpha value. *1* is for full opacity.1206dim - (number) Default's *3*. An extra parameter used by 'circle', 'square', 'triangle' and 'star' node types. Depending on each shape, this parameter can set the radius of a circle, half the length of the side of a square, half the base and half the height of a triangle or the length of a side of a star (concave decagon).1207height - (number) Default's *20*. Used by 'rectangle' and 'ellipse' node types. The height of the node shape.1208width - (number) Default's *90*. Used by 'rectangle' and 'ellipse' node types. The width of the node shape.1209autoHeight - (boolean) Default's *false*. Whether to set an auto height for the node depending on the content of the Node's label.1210autoWidth - (boolean) Default's *false*. Whether to set an auto width for the node depending on the content of the Node's label.1211lineWidth - (number) Default's *1*. Used only by some Node shapes. The line width of the strokes of a node.1212transform - (boolean) Default's *true*. Only used by the <Hypertree> visualization. Whether to scale the nodes according to the moebius transformation.1213align - (string) Default's *center*. Possible values are 'center', 'left' or 'right'. Used only by the <ST> visualization, these parameters are used for aligning nodes when some of they dimensions vary.1214angularWidth - (number) Default's *1*. Used in radial layouts (like <RGraph> or <Sunburst> visualizations). The amount of relative 'space' set for a node.1215span - (number) Default's *1*. Used in radial layouts (like <RGraph> or <Sunburst> visualizations). The angle span amount set for a node.1216CanvasStyles - (object) Default's an empty object (i.e. {}). Attach any other canvas specific property that you'd set to the canvas context before plotting a Node.12171218*/1219Options.Node = {1220$extend: false,1221overridable: false,1222type: 'circle',1223color: '#fff000', // node Color1224alpha: 1,1225dim: 3,1226height: 20,1227width: 90,1228autoHeight: false,1229autoWidth: false,1230lineWidth: 1,1231transform: true,1232align: "center",1233angularWidth:1,1234span:1,1235//Raw canvas styles to be1236//applied to the context instance1237//before plotting a node1238CanvasStyles: {}1239};124012411242/*1243* File: Options.Edge.js1244*1245*/12461247/*1248Object: Options.Edge12491250Provides Edge rendering options for Tree and Graph based visualizations.12511252Syntax:12531254(start code js)1255Options.Edge = {1256overridable: false,1257type: 'line',1258color: '#ccb',1259lineWidth: 1,1260dim:15,1261alpha: 1,1262CanvasStyles: {}1263};1264(end code)12651266Example:12671268(start code js)1269var viz = new $jit.Viz({1270Edge: {1271overridable: true,1272type: 'line',1273color: '#fff',1274CanvasStyles: {1275shadowColor: '#ccc',1276shadowBlur: 101277}1278}1279});1280(end code)12811282Parameters:12831284overridable - (boolean) Default's *false*. Determine whether or not general edges properties can be overridden by a particular <Graph.Adjacence>.1285type - (string) Default's 'line'. Edge styles include 'line', 'hyperline', 'arrow'. The default Edge type might vary in each visualization. You can also implement custom Edge types.1286color - (string) Default's '#ccb'. Edge color.1287lineWidth - (number) Default's *1*. Line/Edge width.1288alpha - (number) Default's *1*. The Edge's alpha value. *1* is for full opacity.1289dim - (number) Default's *15*. An extra parameter used by other complex shapes such as quadratic, bezier or arrow, to determine the shape's diameter.1290epsilon - (number) Default's *7*. Only used when using *enableForEdges* in <Options.Events>. This dimension is used to create an area for the line where the contains method for the edge returns *true*.1291CanvasStyles - (object) Default's an empty object (i.e. {}). Attach any other canvas specific property that you'd set to the canvas context before plotting an Edge.12921293See also:12941295If you want to know more about how to customize Node/Edge data per element, in the JSON or programmatically, take a look at this article.1296*/1297Options.Edge = {1298$extend: false,12991300overridable: false,1301type: 'line',1302color: '#ccb',1303lineWidth: 1,1304dim:15,1305alpha: 1,1306epsilon: 7,13071308//Raw canvas styles to be1309//applied to the context instance1310//before plotting an edge1311CanvasStyles: {}1312};131313141315/*1316* File: Options.Fx.js1317*1318*/13191320/*1321Object: Options.Fx13221323Provides animation options like duration of the animations, frames per second and animation transitions.13241325Syntax:13261327(start code js)1328Options.Fx = {1329fps:40,1330duration: 2500,1331transition: $jit.Trans.Quart.easeInOut,1332clearCanvas: true1333};1334(end code)13351336Example:13371338(start code js)1339var viz = new $jit.Viz({1340duration: 1000,1341fps: 35,1342transition: $jit.Trans.linear1343});1344(end code)13451346Parameters:13471348clearCanvas - (boolean) Default's *true*. Whether to clear the frame/canvas when the viz is plotted or animated.1349duration - (number) Default's *2500*. Duration of the animation in milliseconds.1350fps - (number) Default's *40*. Frames per second.1351transition - (object) Default's *$jit.Trans.Quart.easeInOut*. The transition used for the animations. See below for a more detailed explanation.13521353Object: $jit.Trans13541355This object is used for specifying different animation transitions in all visualizations.13561357There are many different type of animation transitions.13581359linear:13601361Displays a linear transition13621363>Trans.linear13641365(see Linear.png)13661367Quad:13681369Displays a Quadratic transition.13701371>Trans.Quad.easeIn1372>Trans.Quad.easeOut1373>Trans.Quad.easeInOut13741375(see Quad.png)13761377Cubic:13781379Displays a Cubic transition.13801381>Trans.Cubic.easeIn1382>Trans.Cubic.easeOut1383>Trans.Cubic.easeInOut13841385(see Cubic.png)13861387Quart:13881389Displays a Quartetic transition.13901391>Trans.Quart.easeIn1392>Trans.Quart.easeOut1393>Trans.Quart.easeInOut13941395(see Quart.png)13961397Quint:13981399Displays a Quintic transition.14001401>Trans.Quint.easeIn1402>Trans.Quint.easeOut1403>Trans.Quint.easeInOut14041405(see Quint.png)14061407Expo:14081409Displays an Exponential transition.14101411>Trans.Expo.easeIn1412>Trans.Expo.easeOut1413>Trans.Expo.easeInOut14141415(see Expo.png)14161417Circ:14181419Displays a Circular transition.14201421>Trans.Circ.easeIn1422>Trans.Circ.easeOut1423>Trans.Circ.easeInOut14241425(see Circ.png)14261427Sine:14281429Displays a Sineousidal transition.14301431>Trans.Sine.easeIn1432>Trans.Sine.easeOut1433>Trans.Sine.easeInOut14341435(see Sine.png)14361437Back:14381439>Trans.Back.easeIn1440>Trans.Back.easeOut1441>Trans.Back.easeInOut14421443(see Back.png)14441445Bounce:14461447Bouncy transition.14481449>Trans.Bounce.easeIn1450>Trans.Bounce.easeOut1451>Trans.Bounce.easeInOut14521453(see Bounce.png)14541455Elastic:14561457Elastic curve.14581459>Trans.Elastic.easeIn1460>Trans.Elastic.easeOut1461>Trans.Elastic.easeInOut14621463(see Elastic.png)14641465Based on:14661467Easing and Transition animation methods are based in the MooTools Framework <http://mootools.net>. Copyright (c) 2006-2010 Valerio Proietti, <http://mad4milk.net/>. MIT license <http://mootools.net/license.txt>.146814691470*/1471Options.Fx = {1472$extend: true,14731474fps:40,1475duration: 2500,1476transition: $jit.Trans.Quart.easeInOut,1477clearCanvas: true1478};14791480/*1481* File: Options.Label.js1482*1483*/1484/*1485Object: Options.Label14861487Provides styling for Labels such as font size, family, etc. Also sets Node labels as HTML, SVG or Native canvas elements.14881489Syntax:14901491(start code js)1492Options.Label = {1493overridable: false,1494type: 'HTML', //'SVG', 'Native'1495style: ' ',1496size: 10,1497family: 'sans-serif',1498textAlign: 'center',1499textBaseline: 'alphabetic',1500color: '#fff'1501};1502(end code)15031504Example:15051506(start code js)1507var viz = new $jit.Viz({1508Label: {1509type: 'Native',1510size: 11,1511color: '#ccc'1512}1513});1514(end code)15151516Parameters:15171518overridable - (boolean) Default's *false*. Determine whether or not general label properties can be overridden by a particular <Graph.Node>.1519type - (string) Default's *HTML*. The type for the labels. Can be 'HTML', 'SVG' or 'Native' canvas labels.1520style - (string) Default's *empty string*. Can be 'italic' or 'bold'. This parameter is only taken into account when using 'Native' canvas labels. For DOM based labels the className *node* is added to the DOM element for styling via CSS. You can also use <Options.Controller> methods to style individual labels.1521size - (number) Default's *10*. The font's size. This parameter is only taken into account when using 'Native' canvas labels. For DOM based labels the className *node* is added to the DOM element for styling via CSS. You can also use <Options.Controller> methods to style individual labels.1522family - (string) Default's *sans-serif*. The font's family. This parameter is only taken into account when using 'Native' canvas labels. For DOM based labels the className *node* is added to the DOM element for styling via CSS. You can also use <Options.Controller> methods to style individual labels.1523color - (string) Default's *#fff*. The font's color. This parameter is only taken into account when using 'Native' canvas labels. For DOM based labels the className *node* is added to the DOM element for styling via CSS. You can also use <Options.Controller> methods to style individual labels.1524*/1525Options.Label = {1526$extend: false,1527overridable: false,1528type: 'HTML', //'SVG', 'Native'1529style: 'bold',1530size: 12,1531family: ' Courier, Courier New, monospace',1532textAlign: 'center',1533textBaseline: 'alphabetic',1534color: 'white'1535};153615371538/*1539* File: Options.Tips.js1540*1541*/15421543/*1544Object: Options.Tips15451546Tips options15471548Syntax:15491550(start code js)1551Options.Tips = {1552enable: false,1553type: 'auto',1554offsetX: 20,1555offsetY: 20,1556onShow: $.empty,1557onHide: $.empty1558};1559(end code)15601561Example:15621563(start code js)1564var viz = new $jit.Viz({1565Tips: {1566enable: true,1567type: 'Native',1568offsetX: 10,1569offsetY: 10,1570onShow: function(tip, node) {1571tip.innerHTML = node.name;1572}1573}1574});1575(end code)15761577Parameters:15781579enable - (boolean) Default's *false*. If *true*, a tooltip will be shown when a node is hovered. The tooltip is a div DOM element having "tip" as CSS class.1580type - (string) Default's *auto*. Defines where to attach the MouseEnter/Leave tooltip events. Possible values are 'Native' to attach them to the canvas or 'HTML' to attach them to DOM label elements (if defined). 'auto' sets this property to the value of <Options.Label>'s *type* property.1581offsetX - (number) Default's *20*. An offset added to the current tooltip x-position (which is the same as the current mouse position). Default's 20.1582offsetY - (number) Default's *20*. An offset added to the current tooltip y-position (which is the same as the current mouse position). Default's 20.1583onShow(tip, node) - This callack is used right before displaying a tooltip. The first formal parameter is the tip itself (which is a DivElement). The second parameter may be a <Graph.Node> for graph based visualizations or an object with label, value properties for charts.1584onHide() - This callack is used when hiding a tooltip.15851586*/1587Options.Tips = {1588$extend: false,15891590enable: false,1591type: 'auto',1592offsetX: 20,1593offsetY: 20,1594force: false,1595onShow: $.empty,1596onHide: $.empty1597};159815991600/*1601* File: Options.NodeStyles.js1602*1603*/16041605/*1606Object: Options.NodeStyles16071608Apply different styles when a node is hovered or selected.16091610Syntax:16111612(start code js)1613Options.NodeStyles = {1614enable: false,1615type: 'auto',1616stylesHover: false,1617stylesClick: false1618};1619(end code)16201621Example:16221623(start code js)1624var viz = new $jit.Viz({1625NodeStyles: {1626enable: true,1627type: 'Native',1628stylesHover: {1629dim: 30,1630color: '#fcc'1631},1632duration: 6001633}1634});1635(end code)16361637Parameters:16381639enable - (boolean) Default's *false*. Whether to enable this option.1640type - (string) Default's *auto*. Use this to attach the hover/click events in the nodes or the nodes labels (if they have been defined as DOM elements: 'HTML' or 'SVG', see <Options.Label> for more details). The default 'auto' value will set NodeStyles to the same type defined for <Options.Label>.1641stylesHover - (boolean|object) Default's *false*. An object with node styles just like the ones defined for <Options.Node> or *false* otherwise.1642stylesClick - (boolean|object) Default's *false*. An object with node styles just like the ones defined for <Options.Node> or *false* otherwise.1643*/16441645Options.NodeStyles = {1646$extend: false,16471648enable: false,1649type: 'auto',1650stylesHover: false,1651stylesClick: false1652};165316541655/*1656* File: Options.Events.js1657*1658*/16591660/*1661Object: Options.Events16621663Configuration for adding mouse/touch event handlers to Nodes.16641665Syntax:16661667(start code js)1668Options.Events = {1669enable: false,1670enableForEdges: false,1671type: 'auto',1672onClick: $.empty,1673onRightClick: $.empty,1674onMouseMove: $.empty,1675onMouseEnter: $.empty,1676onMouseLeave: $.empty,1677onDragStart: $.empty,1678onDragMove: $.empty,1679onDragCancel: $.empty,1680onDragEnd: $.empty,1681onTouchStart: $.empty,1682onTouchMove: $.empty,1683onTouchEnd: $.empty,1684onTouchCancel: $.empty,1685onMouseWheel: $.empty1686};1687(end code)16881689Example:16901691(start code js)1692var viz = new $jit.Viz({1693Events: {1694enable: true,1695onClick: function(node, eventInfo, e) {1696viz.doSomething();1697},1698onMouseEnter: function(node, eventInfo, e) {1699viz.canvas.getElement().style.cursor = 'pointer';1700},1701onMouseLeave: function(node, eventInfo, e) {1702viz.canvas.getElement().style.cursor = '';1703}1704}1705});1706(end code)17071708Parameters:17091710enable - (boolean) Default's *false*. Whether to enable the Event system.1711enableForEdges - (boolean) Default's *false*. Whether to track events also in arcs. If *true* the same callbacks -described below- are used for nodes *and* edges. A simple duck type check for edges is to check for *node.nodeFrom*.1712type - (string) Default's 'auto'. Whether to attach the events onto the HTML labels (via event delegation) or to use the custom 'Native' canvas Event System of the library. 'auto' is set when you let the <Options.Label> *type* parameter decide this.1713onClick(node, eventInfo, e) - Triggered when a user performs a click in the canvas. *node* is the <Graph.Node> clicked or false if no node has been clicked. *e* is the grabbed event (should return the native event in a cross-browser manner). *eventInfo* is an object containing useful methods like *getPos* to get the mouse position relative to the canvas.1714onRightClick(node, eventInfo, e) - Triggered when a user performs a right click in the canvas. *node* is the <Graph.Node> right clicked or false if no node has been clicked. *e* is the grabbed event (should return the native event in a cross-browser manner). *eventInfo* is an object containing useful methods like *getPos* to get the mouse position relative to the canvas.1715onMouseMove(node, eventInfo, e) - Triggered when the user moves the mouse. *node* is the <Graph.Node> under the cursor as it's moving over the canvas or false if no node has been clicked. *e* is the grabbed event (should return the native event in a cross-browser manner). *eventInfo* is an object containing useful methods like *getPos* to get the mouse position relative to the canvas.1716onMouseEnter(node, eventInfo, e) - Triggered when a user moves the mouse over a node. *node* is the <Graph.Node> that the mouse just entered. *e* is the grabbed event (should return the native event in a cross-browser manner). *eventInfo* is an object containing useful methods like *getPos* to get the mouse position relative to the canvas.1717onMouseLeave(node, eventInfo, e) - Triggered when the user mouse-outs a node. *node* is the <Graph.Node> 'mouse-outed'. *e* is the grabbed event (should return the native event in a cross-browser manner). *eventInfo* is an object containing useful methods like *getPos* to get the mouse position relative to the canvas.1718onDragStart(node, eventInfo, e) - Triggered when the user mouse-downs over a node. *node* is the <Graph.Node> being pressed. *e* is the grabbed event (should return the native event in a cross-browser manner). *eventInfo* is an object containing useful methods like *getPos* to get the mouse position relative to the canvas.1719onDragMove(node, eventInfo, e) - Triggered when a user, after pressing the mouse button over a node, moves the mouse around. *node* is the <Graph.Node> being dragged. *e* is the grabbed event (should return the native event in a cross-browser manner). *eventInfo* is an object containing useful methods like *getPos* to get the mouse position relative to the canvas.1720onDragEnd(node, eventInfo, e) - Triggered when a user finished dragging a node. *node* is the <Graph.Node> being dragged. *e* is the grabbed event (should return the native event in a cross-browser manner). *eventInfo* is an object containing useful methods like *getPos* to get the mouse position relative to the canvas.1721onDragCancel(node, eventInfo, e) - Triggered when the user releases the mouse button over a <Graph.Node> that wasn't dragged (i.e. the user didn't perform any mouse movement after pressing the mouse button). *node* is the <Graph.Node> being dragged. *e* is the grabbed event (should return the native event in a cross-browser manner). *eventInfo* is an object containing useful methods like *getPos* to get the mouse position relative to the canvas.1722onTouchStart(node, eventInfo, e) - Behaves just like onDragStart.1723onTouchMove(node, eventInfo, e) - Behaves just like onDragMove.1724onTouchEnd(node, eventInfo, e) - Behaves just like onDragEnd.1725onTouchCancel(node, eventInfo, e) - Behaves just like onDragCancel.1726onMouseWheel(delta, e) - Triggered when the user uses the mouse scroll over the canvas. *delta* is 1 or -1 depending on the sense of the mouse scroll.1727*/17281729Options.Events = {1730$extend: false,17311732enable: false,1733enableForEdges: false,1734type: 'auto',1735onClick: $.empty,1736onRightClick: $.empty,1737onMouseMove: $.empty,1738onMouseEnter: $.empty,1739onMouseLeave: $.empty,1740onDragStart: $.empty,1741onDragMove: $.empty,1742onDragCancel: $.empty,1743onDragEnd: $.empty,1744onTouchStart: $.empty,1745onTouchMove: $.empty,1746onTouchEnd: $.empty,1747onMouseWheel: $.empty1748};17491750/*1751* File: Options.Navigation.js1752*1753*/17541755/*1756Object: Options.Navigation17571758Panning and zooming options for Graph/Tree based visualizations. These options are implemented1759by all visualizations except charts (<AreaChart>, <BarChart> and <PieChart>).17601761Syntax:17621763(start code js)17641765Options.Navigation = {1766enable: false,1767type: 'auto',1768panning: false, //true, 'avoid nodes'1769zooming: false1770};17711772(end code)17731774Example:17751776(start code js)1777var viz = new $jit.Viz({1778Navigation: {1779enable: true,1780panning: 'avoid nodes',1781zooming: 201782}1783});1784(end code)17851786Parameters:17871788enable - (boolean) Default's *false*. Whether to enable Navigation capabilities.1789type - (string) Default's 'auto'. Whether to attach the navigation events onto the HTML labels (via event delegation) or to use the custom 'Native' canvas Event System of the library. When 'auto' set when you let the <Options.Label> *type* parameter decide this.1790panning - (boolean|string) Default's *false*. Set this property to *true* if you want to add Drag and Drop panning support to the visualization. You can also set this parameter to 'avoid nodes' to enable DnD panning but disable it if the DnD is taking place over a node. This is useful when some other events like Drag & Drop for nodes are added to <Graph.Nodes>.1791zooming - (boolean|number) Default's *false*. Set this property to a numeric value to turn mouse-scroll zooming on. The number will be proportional to the mouse-scroll sensitivity.17921793*/17941795Options.Navigation = {1796$extend: false,17971798enable: false,1799type: 'auto',1800panning: false, //true | 'avoid nodes'1801zooming: false1802};18031804/*1805* File: Options.Controller.js1806*1807*/18081809/*1810Object: Options.Controller18111812Provides controller methods. Controller methods are callback functions that get called at different stages1813of the animation, computing or plotting of the visualization.18141815Implemented by:18161817All visualizations except charts (<AreaChart>, <BarChart> and <PieChart>).18181819Syntax:18201821(start code js)18221823Options.Controller = {1824onBeforeCompute: $.empty,1825onAfterCompute: $.empty,1826onCreateLabel: $.empty,1827onPlaceLabel: $.empty,1828onComplete: $.empty,1829onBeforePlotLine:$.empty,1830onAfterPlotLine: $.empty,1831onBeforePlotNode:$.empty,1832onAfterPlotNode: $.empty,1833request: false1834};18351836(end code)18371838Example:18391840(start code js)1841var viz = new $jit.Viz({1842onBeforePlotNode: function(node) {1843if(node.selected) {1844node.setData('color', '#ffc');1845} else {1846node.removeData('color');1847}1848},1849onBeforePlotLine: function(adj) {1850if(adj.nodeFrom.selected && adj.nodeTo.selected) {1851adj.setData('color', '#ffc');1852} else {1853adj.removeData('color');1854}1855},1856onAfterCompute: function() {1857alert("computed!");1858}1859});1860(end code)18611862Parameters:18631864onBeforeCompute(node) - This method is called right before performing all computations and animations. The selected <Graph.Node> is passed as parameter.1865onAfterCompute() - This method is triggered after all animations or computations ended.1866onCreateLabel(domElement, node) - This method receives a new label DIV element as first parameter, and the corresponding <Graph.Node> as second parameter. This method will only be called once for each label. This method is useful when adding events or styles to the labels used by the JIT.1867onPlaceLabel(domElement, node) - This method receives a label DIV element as first parameter and the corresponding <Graph.Node> as second parameter. This method is called each time a label has been placed in the visualization, for example at each step of an animation, 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. Width and height properties are not set however.1868onBeforePlotNode(node) - This method is triggered right before plotting each <Graph.Node>. This method is useful for changing a node style right before plotting it.1869onAfterPlotNode(node) - This method is triggered right after plotting each <Graph.Node>.1870onBeforePlotLine(adj) - This method is triggered right before plotting a <Graph.Adjacence>. This method is useful for adding some styles to a particular edge before being plotted.1871onAfterPlotLine(adj) - This method is triggered right after plotting a <Graph.Adjacence>.18721873*Used in <ST>, <TM.Base> and <Icicle> visualizations*18741875request(nodeId, level, onComplete) - This method is used for buffering information into the visualization. When clicking on an empty node, 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 callback should be called with the given result. This is useful to provide on-demand information into the visualizations withought having to load the entire information from start. The parameters used by this method are _nodeId_, which is the id of the root of the subtree to request, _level_ which is the depth of the subtree to be requested (0 would mean just the root node). _onComplete_ is an object having the callback method _onComplete.onComplete(json)_ that should be called once the json has been retrieved.18761877*/1878Options.Controller = {1879$extend: true,18801881onBeforeCompute: $.empty,1882onAfterCompute: $.empty,1883onCreateLabel: $.empty,1884onPlaceLabel: $.empty,1885onComplete: $.empty,1886onBeforePlotLine:$.empty,1887onAfterPlotLine: $.empty,1888onBeforePlotNode:$.empty,1889onAfterPlotNode: $.empty,1890request: false1891};189218931894/*1895* File: Extras.js1896*1897* Provides Extras such as Tips and Style Effects.1898*1899* Description:1900*1901* Provides the <Tips> and <NodeStyles> classes and functions.1902*1903*/19041905/*1906* Manager for mouse events (clicking and mouse moving).1907*1908* This class is used for registering objects implementing onClick1909* and onMousemove methods. These methods are called when clicking or1910* moving the mouse around the Canvas.1911* For now, <Tips> and <NodeStyles> are classes implementing these methods.1912*1913*/1914var ExtrasInitializer = {1915initialize: function(className, viz) {1916this.viz = viz;1917this.canvas = viz.canvas;1918this.config = viz.config[className];1919this.nodeTypes = viz.fx.nodeTypes;1920var type = this.config.type;1921this.dom = type == 'auto'? (viz.config.Label.type != 'Native') : (type != 'Native');1922this.labelContainer = this.dom && viz.labels.getLabelContainer();1923this.isEnabled() && this.initializePost();1924},1925initializePost: $.empty,1926setAsProperty: $.lambda(false),1927isEnabled: function() {1928return this.config.enable;1929},1930isLabel: function(e, win, group) {1931e = $.event.get(e, win);1932var labelContainer = this.labelContainer,1933target = e.target || e.srcElement,1934related = e.relatedTarget;1935if(group) {1936return related && related == this.viz.canvas.getCtx().canvas1937&& !!target && this.isDescendantOf(target, labelContainer);1938} else {1939return this.isDescendantOf(target, labelContainer);1940}1941},1942isDescendantOf: function(elem, par) {1943while(elem && elem.parentNode) {1944if(elem.parentNode == par)1945return elem;1946elem = elem.parentNode;1947}1948return false;1949}1950};19511952var EventsInterface = {1953onMouseUp: $.empty,1954onMouseDown: $.empty,1955onMouseMove: $.empty,1956onMouseOver: $.empty,1957onMouseOut: $.empty,1958onMouseWheel: $.empty,1959onTouchStart: $.empty,1960onTouchMove: $.empty,1961onTouchEnd: $.empty,1962onTouchCancel: $.empty1963};19641965var MouseEventsManager = new Class({1966initialize: function(viz) {1967this.viz = viz;1968this.canvas = viz.canvas;1969this.node = false;1970this.edge = false;1971this.registeredObjects = [];1972this.attachEvents();1973},19741975attachEvents: function() {1976var htmlCanvas = this.canvas.getElement(),1977that = this;1978htmlCanvas.oncontextmenu = $.lambda(false);1979$.addEvents(htmlCanvas, {1980'mouseup': function(e, win) {1981var event = $.event.get(e, win);1982that.handleEvent('MouseUp', e, win,1983that.makeEventObject(e, win),1984$.event.isRightClick(event));1985},1986'mousedown': function(e, win) {1987var event = $.event.get(e, win);1988that.handleEvent('MouseDown', e, win, that.makeEventObject(e, win),1989$.event.isRightClick(event));1990},1991'mousemove': function(e, win) {1992that.handleEvent('MouseMove', e, win, that.makeEventObject(e, win));1993},1994'mouseover': function(e, win) {1995that.handleEvent('MouseOver', e, win, that.makeEventObject(e, win));1996},1997'mouseout': function(e, win) {1998that.handleEvent('MouseOut', e, win, that.makeEventObject(e, win));1999},2000'touchstart': function(e, win) {2001that.handleEvent('TouchStart', e, win, that.makeEventObject(e, win));2002},2003'touchmove': function(e, win) {2004that.handleEvent('TouchMove', e, win, that.makeEventObject(e, win));2005},2006'touchend': function(e, win) {2007that.handleEvent('TouchEnd', e, win, that.makeEventObject(e, win));2008}2009});2010//attach mousewheel event2011var handleMouseWheel = function(e, win) {2012var event = $.event.get(e, win);2013var wheel = $.event.getWheel(event);2014that.handleEvent('MouseWheel', e, win, wheel);2015};2016//this is a horrible check for non-gecko browsers!2017if(!document.getBoxObjectFor && window.mozInnerScreenX == null) {2018$.addEvent(htmlCanvas, 'mousewheel', handleMouseWheel);2019} else {2020htmlCanvas.addEventListener('DOMMouseScroll', handleMouseWheel, false);2021}2022},20232024register: function(obj) {2025this.registeredObjects.push(obj);2026},20272028handleEvent: function() {2029var args = Array.prototype.slice.call(arguments),2030type = args.shift();2031for(var i=0, regs=this.registeredObjects, l=regs.length; i<l; i++) {2032regs[i]['on' + type].apply(regs[i], args);2033}2034},20352036makeEventObject: function(e, win) {2037var that = this,2038graph = this.viz.graph,2039fx = this.viz.fx,2040ntypes = fx.nodeTypes,2041etypes = fx.edgeTypes;2042return {2043pos: false,2044node: false,2045edge: false,2046contains: false,2047getNodeCalled: false,2048getEdgeCalled: false,2049getPos: function() {2050//check why this can't be cache anymore when using edge detection2051//if(this.pos) return this.pos;2052var canvas = that.viz.canvas,2053s = canvas.getSize(),2054p = canvas.getPos(),2055ox = canvas.translateOffsetX,2056oy = canvas.translateOffsetY,2057sx = canvas.scaleOffsetX,2058sy = canvas.scaleOffsetY,2059pos = $.event.getPos(e, win);2060this.pos = {2061x: (pos.x - p.x - s.width/2 - ox) * 1/sx,2062y: (pos.y - p.y - s.height/2 - oy) * 1/sy2063};2064return this.pos;2065},2066getNode: function() {2067if(this.getNodeCalled) return this.node;2068this.getNodeCalled = true;2069for(var id in graph.nodes) {2070var n = graph.nodes[id],2071geom = n && ntypes[n.getData('type')],2072contains = geom && geom.contains && geom.contains.call(fx, n, this.getPos());2073if(contains) {2074this.contains = contains;2075return that.node = this.node = n;2076}2077}2078return that.node = this.node = false;2079},2080getEdge: function() {2081if(this.getEdgeCalled) return this.edge;2082this.getEdgeCalled = true;2083var hashset = {};2084for(var id in graph.edges) {2085var edgeFrom = graph.edges[id];2086hashset[id] = true;2087for(var edgeId in edgeFrom) {2088if(edgeId in hashset) continue;2089var e = edgeFrom[edgeId],2090geom = e && etypes[e.getData('type')],2091contains = geom && geom.contains && geom.contains.call(fx, e, this.getPos());2092if(contains) {2093this.contains = contains;2094return that.edge = this.edge = e;2095}2096}2097}2098return that.edge = this.edge = false;2099},2100getContains: function() {2101if(this.getNodeCalled) return this.contains;2102this.getNode();2103return this.contains;2104}2105};2106}2107});21082109/*2110* Provides the initialization function for <NodeStyles> and <Tips> implemented2111* by all main visualizations.2112*2113*/2114var Extras = {2115initializeExtras: function() {2116var mem = new MouseEventsManager(this), that = this;2117$.each(['NodeStyles', 'Tips', 'Navigation', 'Events'], function(k) {2118var obj = new Extras.Classes[k](k, that);2119if(obj.isEnabled()) {2120mem.register(obj);2121}2122if(obj.setAsProperty()) {2123that[k.toLowerCase()] = obj;2124}2125});2126}2127};21282129Extras.Classes = {};2130/*2131Class: Events21322133This class defines an Event API to be accessed by the user.2134The methods implemented are the ones defined in the <Options.Events> object.2135*/21362137Extras.Classes.Events = new Class({2138Implements: [ExtrasInitializer, EventsInterface],21392140initializePost: function() {2141this.fx = this.viz.fx;2142this.ntypes = this.viz.fx.nodeTypes;2143this.etypes = this.viz.fx.edgeTypes;21442145this.hovered = false;2146this.pressed = false;2147this.touched = false;21482149this.touchMoved = false;2150this.moved = false;21512152},21532154setAsProperty: $.lambda(true),21552156onMouseUp: function(e, win, event, isRightClick) {2157var evt = $.event.get(e, win);2158if(!this.moved) {2159if(isRightClick) {2160this.config.onRightClick(this.hovered, event, evt);2161} else {2162this.config.onClick(this.pressed, event, evt);2163}2164}2165if(this.pressed) {2166if(this.moved) {2167this.config.onDragEnd(this.pressed, event, evt);2168} else {2169this.config.onDragCancel(this.pressed, event, evt);2170}2171this.pressed = this.moved = false;2172}2173},21742175onMouseOut: function(e, win, event) {2176//mouseout a label2177var evt = $.event.get(e, win), label;2178if(this.dom && (label = this.isLabel(e, win, true))) {2179this.config.onMouseLeave(this.viz.graph.getNode(label.id),2180event, evt);2181this.hovered = false;2182return;2183}2184//mouseout canvas2185var rt = evt.relatedTarget,2186canvasWidget = this.canvas.getElement();2187while(rt && rt.parentNode) {2188if(canvasWidget == rt.parentNode) return;2189rt = rt.parentNode;2190}2191if(this.hovered) {2192this.config.onMouseLeave(this.hovered,2193event, evt);2194this.hovered = false;2195}2196},21972198onMouseOver: function(e, win, event) {2199//mouseover a label2200var evt = $.event.get(e, win), label;2201if(this.dom && (label = this.isLabel(e, win, true))) {2202this.hovered = this.viz.graph.getNode(label.id);2203this.config.onMouseEnter(this.hovered,2204event, evt);2205}2206},22072208onMouseMove: function(e, win, event) {2209var label, evt = $.event.get(e, win);2210if(this.pressed) {2211this.moved = true;2212this.config.onDragMove(this.pressed, event, evt);2213return;2214}2215if(this.dom) {2216this.config.onMouseMove(this.hovered,2217event, evt);2218} else {2219if(this.hovered) {2220var hn = this.hovered;2221var geom = hn.nodeFrom? this.etypes[hn.getData('type')] : this.ntypes[hn.getData('type')];2222var contains = geom && geom.contains2223&& geom.contains.call(this.fx, hn, event.getPos());2224if(contains) {2225this.config.onMouseMove(hn, event, evt);2226return;2227} else {2228this.config.onMouseLeave(hn, event, evt);2229this.hovered = false;2230}2231}2232if(this.hovered = (event.getNode() || (this.config.enableForEdges && event.getEdge()))) {2233this.config.onMouseEnter(this.hovered, event, evt);2234} else {2235this.config.onMouseMove(false, event, evt);2236}2237}2238},22392240onMouseWheel: function(e, win, delta) {2241this.config.onMouseWheel(delta, $.event.get(e, win));2242},22432244onMouseDown: function(e, win, event) {2245var evt = $.event.get(e, win), label;2246if(this.dom) {2247if(label = this.isLabel(e, win)) {2248this.pressed = this.viz.graph.getNode(label.id);2249}2250} else {2251this.pressed = event.getNode() || (this.config.enableForEdges && event.getEdge());2252}2253this.pressed && this.config.onDragStart(this.pressed, event, evt);2254},22552256onTouchStart: function(e, win, event) {2257var evt = $.event.get(e, win), label;2258if(this.dom && (label = this.isLabel(e, win))) {2259this.touched = this.viz.graph.getNode(label.id);2260} else {2261this.touched = event.getNode() || (this.config.enableForEdges && event.getEdge());2262}2263this.touched && this.config.onTouchStart(this.touched, event, evt);2264},22652266onTouchMove: function(e, win, event) {2267var evt = $.event.get(e, win);2268if(this.touched) {2269this.touchMoved = true;2270this.config.onTouchMove(this.touched, event, evt);2271}2272},22732274onTouchEnd: function(e, win, event) {2275var evt = $.event.get(e, win);2276if(this.touched) {2277if(this.touchMoved) {2278this.config.onTouchEnd(this.touched, event, evt);2279} else {2280this.config.onTouchCancel(this.touched, event, evt);2281}2282this.touched = this.touchMoved = false;2283}2284}2285});22862287/*2288Class: Tips22892290A class containing tip related functions. This class is used internally.22912292Used by:22932294<ST>, <Sunburst>, <Hypertree>, <RGraph>, <TM>, <ForceDirected>, <Icicle>22952296See also:22972298<Options.Tips>2299*/23002301Extras.Classes.Tips = new Class({2302Implements: [ExtrasInitializer, EventsInterface],23032304initializePost: function() {2305//add DOM tooltip2306if(document.body) {2307var tip = $('_tooltip') || document.createElement('div');2308tip.id = '_tooltip';2309tip.className = 'tip';2310$.extend(tip.style, {2311position: 'absolute',2312display: 'none',2313zIndex: 130002314});2315document.body.appendChild(tip);2316this.tip = tip;2317this.node = false;2318}2319},23202321setAsProperty: $.lambda(true),23222323onMouseOut: function(e, win) {2324//mouseout a label2325var evt = $.event.get(e, win);2326if(this.dom && this.isLabel(e, win, true)) {2327this.hide(true);2328return;2329}2330//mouseout canvas2331var rt = e.relatedTarget,2332canvasWidget = this.canvas.getElement();2333while(rt && rt.parentNode) {2334if(canvasWidget == rt.parentNode) return;2335rt = rt.parentNode;2336}2337this.hide(false);2338},23392340onMouseOver: function(e, win) {2341//mouseover a label2342var label;2343if(this.dom && (label = this.isLabel(e, win, false))) {2344this.node = this.viz.graph.getNode(label.id);2345this.config.onShow(this.tip, this.node, label);2346}2347},23482349onMouseMove: function(e, win, opt) {2350if(this.dom && this.isLabel(e, win)) {2351this.setTooltipPosition($.event.getPos(e, win));2352}2353if(!this.dom) {2354var node = opt.getNode();2355if(!node) {2356this.hide(true);2357return;2358}2359if(this.config.force || !this.node || this.node.id != node.id) {2360this.node = node;2361this.config.onShow(this.tip, node, opt.getContains());2362}2363this.setTooltipPosition($.event.getPos(e, win));2364}2365},23662367setTooltipPosition: function(pos) {2368var tip = this.tip,2369style = tip.style,2370cont = this.config;2371style.display = '';2372//get window dimensions2373var win = {2374'height': document.body.clientHeight,2375'width': document.body.clientWidth2376};2377//get tooltip dimensions2378var obj = {2379'width': tip.offsetWidth,2380'height': tip.offsetHeight2381};2382//set tooltip position2383var x = cont.offsetX, y = cont.offsetY;2384style.top = ((pos.y + y + obj.height > win.height)?2385(pos.y - obj.height - y) : pos.y + y) + 'px';2386style.left = ((pos.x + obj.width + x > win.width)?2387(pos.x - obj.width - x) : pos.x + x) + 'px';2388},23892390hide: function(triggerCallback) {2391this.tip.style.display = 'none';2392triggerCallback && this.config.onHide();2393}2394});23952396/*2397Class: NodeStyles23982399Change node styles when clicking or hovering a node. This class is used internally.24002401Used by:24022403<ST>, <Sunburst>, <Hypertree>, <RGraph>, <TM>, <ForceDirected>, <Icicle>24042405See also:24062407<Options.NodeStyles>2408*/2409Extras.Classes.NodeStyles = new Class({2410Implements: [ExtrasInitializer, EventsInterface],24112412initializePost: function() {2413this.fx = this.viz.fx;2414this.types = this.viz.fx.nodeTypes;2415this.nStyles = this.config;2416this.nodeStylesOnHover = this.nStyles.stylesHover;2417this.nodeStylesOnClick = this.nStyles.stylesClick;2418this.hoveredNode = false;2419this.fx.nodeFxAnimation = new Animation();24202421this.down = false;2422this.move = false;2423},24242425onMouseOut: function(e, win) {2426this.down = this.move = false;2427if(!this.hoveredNode) return;2428//mouseout a label2429if(this.dom && this.isLabel(e, win, true)) {2430this.toggleStylesOnHover(this.hoveredNode, false);2431}2432//mouseout canvas2433var rt = e.relatedTarget,2434canvasWidget = this.canvas.getElement();2435while(rt && rt.parentNode) {2436if(canvasWidget == rt.parentNode) return;2437rt = rt.parentNode;2438}2439this.toggleStylesOnHover(this.hoveredNode, false);2440this.hoveredNode = false;2441},24422443onMouseOver: function(e, win) {2444//mouseover a label2445var label;2446if(this.dom && (label = this.isLabel(e, win, true))) {2447var node = this.viz.graph.getNode(label.id);2448if(node.selected) return;2449this.hoveredNode = node;2450this.toggleStylesOnHover(this.hoveredNode, true);2451}2452},24532454onMouseDown: function(e, win, event, isRightClick) {2455if(isRightClick) return;2456var label;2457if(this.dom && (label = this.isLabel(e, win))) {2458this.down = this.viz.graph.getNode(label.id);2459} else if(!this.dom) {2460this.down = event.getNode();2461}2462this.move = false;2463},24642465onMouseUp: function(e, win, event, isRightClick) {2466if(isRightClick) return;2467if(!this.move) {2468this.onClick(event.getNode());2469}2470this.down = this.move = false;2471},24722473getRestoredStyles: function(node, type) {2474var restoredStyles = {},2475nStyles = this['nodeStylesOn' + type];2476for(var prop in nStyles) {2477restoredStyles[prop] = node.styles['$' + prop];2478}2479return restoredStyles;2480},24812482toggleStylesOnHover: function(node, set) {2483if(this.nodeStylesOnHover) {2484this.toggleStylesOn('Hover', node, set);2485}2486},24872488toggleStylesOnClick: function(node, set) {2489if(this.nodeStylesOnClick) {2490this.toggleStylesOn('Click', node, set);2491}2492},24932494toggleStylesOn: function(type, node, set) {2495var viz = this.viz;2496var nStyles = this.nStyles;2497if(set) {2498var that = this;2499if(!node.styles) {2500node.styles = $.merge(node.data, {});2501}2502for(var s in this['nodeStylesOn' + type]) {2503var $s = '$' + s;2504if(!($s in node.styles)) {2505node.styles[$s] = node.getData(s);2506}2507}2508viz.fx.nodeFx($.extend({2509'elements': {2510'id': node.id,2511'properties': that['nodeStylesOn' + type]2512},2513transition: Trans.Quart.easeOut,2514duration:300,2515fps:402516}, this.config));2517} else {2518var restoredStyles = this.getRestoredStyles(node, type);2519viz.fx.nodeFx($.extend({2520'elements': {2521'id': node.id,2522'properties': restoredStyles2523},2524transition: Trans.Quart.easeOut,2525duration:300,2526fps:402527}, this.config));2528}2529},25302531onClick: function(node) {2532if(!node) return;2533var nStyles = this.nodeStylesOnClick;2534if(!nStyles) return;2535//if the node is selected then unselect it2536if(node.selected) {2537this.toggleStylesOnClick(node, false);2538delete node.selected;2539} else {2540//unselect all selected nodes...2541this.viz.graph.eachNode(function(n) {2542if(n.selected) {2543for(var s in nStyles) {2544n.setData(s, n.styles['$' + s], 'end');2545}2546delete n.selected;2547}2548});2549//select clicked node2550this.toggleStylesOnClick(node, true);2551node.selected = true;2552delete node.hovered;2553this.hoveredNode = false;2554}2555},25562557onMouseMove: function(e, win, event) {2558//if mouse button is down and moving set move=true2559if(this.down) this.move = true;2560//already handled by mouseover/out2561if(this.dom && this.isLabel(e, win)) return;2562var nStyles = this.nodeStylesOnHover;2563if(!nStyles) return;25642565if(!this.dom) {2566if(this.hoveredNode) {2567var geom = this.types[this.hoveredNode.getData('type')];2568var contains = geom && geom.contains && geom.contains.call(this.fx,2569this.hoveredNode, event.getPos());2570if(contains) return;2571}2572var node = event.getNode();2573//if no node is being hovered then just exit2574if(!this.hoveredNode && !node) return;2575//if the node is hovered then exit2576if(node.hovered) return;2577//select hovered node2578if(node && !node.selected) {2579//check if an animation is running and exit it2580this.fx.nodeFxAnimation.stopTimer();2581//unselect all hovered nodes...2582this.viz.graph.eachNode(function(n) {2583if(n.hovered && !n.selected) {2584for(var s in nStyles) {2585n.setData(s, n.styles['$' + s], 'end');2586}2587delete n.hovered;2588}2589});2590//select hovered node2591node.hovered = true;2592this.hoveredNode = node;2593this.toggleStylesOnHover(node, true);2594} else if(this.hoveredNode && !this.hoveredNode.selected) {2595//check if an animation is running and exit it2596this.fx.nodeFxAnimation.stopTimer();2597//unselect hovered node2598this.toggleStylesOnHover(this.hoveredNode, false);2599delete this.hoveredNode.hovered;2600this.hoveredNode = false;2601}2602}2603}2604});26052606Extras.Classes.Navigation = new Class({2607Implements: [ExtrasInitializer, EventsInterface],26082609initializePost: function() {2610this.pos = false;2611this.pressed = false;2612},26132614onMouseWheel: function(e, win, scroll) {2615if(!this.config.zooming) return;2616$.event.stop($.event.get(e, win));2617var val = this.config.zooming / 1000,2618ans = 1 + scroll * val;2619this.canvas.scale(ans, ans);2620},26212622onMouseDown: function(e, win, eventInfo) {2623if(!this.config.panning) return;2624if(this.config.panning == 'avoid nodes' && (this.dom? this.isLabel(e, win) : eventInfo.getNode())) return;2625this.pressed = true;2626this.pos = eventInfo.getPos();2627var canvas = this.canvas,2628ox = canvas.translateOffsetX,2629oy = canvas.translateOffsetY,2630sx = canvas.scaleOffsetX,2631sy = canvas.scaleOffsetY;2632this.pos.x *= sx;2633this.pos.x += ox;2634this.pos.y *= sy;2635this.pos.y += oy;2636},26372638onMouseMove: function(e, win, eventInfo) {2639if(!this.config.panning) return;2640if(!this.pressed) return;2641if(this.config.panning == 'avoid nodes' && (this.dom? this.isLabel(e, win) : eventInfo.getNode())) return;2642var thispos = this.pos,2643currentPos = eventInfo.getPos(),2644canvas = this.canvas,2645ox = canvas.translateOffsetX,2646oy = canvas.translateOffsetY,2647sx = canvas.scaleOffsetX,2648sy = canvas.scaleOffsetY;2649currentPos.x *= sx;2650currentPos.y *= sy;2651currentPos.x += ox;2652currentPos.y += oy;2653var x = currentPos.x - thispos.x,2654y = currentPos.y - thispos.y;2655this.pos = currentPos;2656this.canvas.translate(x * 1/sx, y * 1/sy);2657},26582659onMouseUp: function(e, win, eventInfo, isRightClick) {2660if(!this.config.panning) return;2661this.pressed = false;2662}2663});266426652666/*2667* File: Canvas.js2668*2669*/26702671/*2672Class: Canvas26732674A canvas widget used by all visualizations. The canvas object can be accessed by doing *viz.canvas*. If you want to2675know more about <Canvas> options take a look at <Options.Canvas>.26762677A canvas widget is a set of DOM elements that wrap the native canvas DOM Element providing a consistent API and behavior2678across all browsers. It can also include Elements to add DOM (SVG or HTML) label support to all visualizations.26792680Example:26812682Suppose we have this HTML26832684(start code xml)2685<div id="infovis"></div>2686(end code)26872688Now we create a new Visualization26892690(start code js)2691var viz = new $jit.Viz({2692//Where to inject the canvas. Any div container will do.2693'injectInto':'infovis',2694//width and height for canvas.2695//Default's to the container offsetWidth and Height.2696'width': 900,2697'height':5002698});2699(end code)27002701The generated HTML will look like this27022703(start code xml)2704<div id="infovis">2705<div id="infovis-canvaswidget" style="position:relative;">2706<canvas id="infovis-canvas" width=900 height=5002707style="position:absolute; top:0; left:0; width:900px; height:500px;" />2708<div id="infovis-label"2709style="overflow:visible; position:absolute; top:0; left:0; width:900px; height:0px">2710</div>2711</div>2712</div>2713(end code)27142715As you can see, the generated HTML consists of a canvas DOM Element of id *infovis-canvas* and a div label container2716of id *infovis-label*, wrapped in a main div container of id *infovis-canvaswidget*.2717*/27182719var Canvas;2720(function() {2721//check for native canvas support2722var canvasType = typeof HTMLCanvasElement,2723supportsCanvas = (canvasType == 'object' || canvasType == 'function');2724//create element function2725function $E(tag, props) {2726var elem = document.createElement(tag);2727for(var p in props) {2728if(typeof props[p] == "object") {2729$.extend(elem[p], props[p]);2730} else {2731elem[p] = props[p];2732}2733}2734if (tag == "canvas" && !supportsCanvas && G_vmlCanvasManager) {2735elem = G_vmlCanvasManager.initElement(document.body.appendChild(elem));2736}2737return elem;2738}2739//canvas widget which we will call just Canvas2740$jit.Canvas = Canvas = new Class({2741canvases: [],2742pos: false,2743element: false,2744labelContainer: false,2745translateOffsetX: 0,2746translateOffsetY: 0,2747scaleOffsetX: 1,2748scaleOffsetY: 1,27492750initialize: function(viz, opt) {2751this.viz = viz;2752this.opt = this.config = opt;2753var id = $.type(opt.injectInto) == 'string'?2754opt.injectInto:opt.injectInto.id,2755type = opt.type,2756idLabel = id + "-label",2757wrapper = $(id),2758width = opt.width || wrapper.offsetWidth,2759height = opt.height || wrapper.offsetHeight;2760this.id = id;2761//canvas options2762var canvasOptions = {2763injectInto: id,2764width: width,2765height: height2766};2767//create main wrapper2768this.element = $E('div', {2769'id': id + '-canvaswidget',2770'style': {2771'position': 'relative',2772'width': width + 'px',2773'height': height + 'px'2774}2775});2776//create label container2777this.labelContainer = this.createLabelContainer(opt.Label.type,2778idLabel, canvasOptions);2779//create primary canvas2780this.canvases.push(new Canvas.Base[type]({2781config: $.extend({idSuffix: '-canvas'}, canvasOptions),2782plot: function(base) {2783viz.fx.plot();2784},2785resize: function() {2786viz.refresh();2787}2788}));2789//create secondary canvas2790var back = opt.background;2791if(back) {2792var backCanvas = new Canvas.Background[back.type](viz, $.extend(back, canvasOptions));2793this.canvases.push(new Canvas.Base[type](backCanvas));2794}2795//insert canvases2796var len = this.canvases.length;2797while(len--) {2798this.element.appendChild(this.canvases[len].canvas);2799if(len > 0) {2800this.canvases[len].plot();2801}2802}2803this.element.appendChild(this.labelContainer);2804wrapper.appendChild(this.element);2805//Update canvas position when the page is scrolled.2806var timer = null, that = this;2807$.addEvent(window, 'scroll', function() {2808clearTimeout(timer);2809timer = setTimeout(function() {2810that.getPos(true); //update canvas position2811}, 500);2812});2813},2814/*2815Method: getCtx28162817Returns the main canvas context object28182819Example:28202821(start code js)2822var ctx = canvas.getCtx();2823//Now I can use the native canvas context2824//and for example change some canvas styles2825ctx.globalAlpha = 1;2826(end code)2827*/2828getCtx: function(i) {2829return this.canvases[i || 0].getCtx();2830},2831/*2832Method: getConfig28332834Returns the current Configuration for this Canvas Widget.28352836Example:28372838(start code js)2839var config = canvas.getConfig();2840(end code)2841*/2842getConfig: function() {2843return this.opt;2844},2845/*2846Method: getElement28472848Returns the main Canvas DOM wrapper28492850Example:28512852(start code js)2853var wrapper = canvas.getElement();2854//Returns <div id="infovis-canvaswidget" ... >...</div> as element2855(end code)2856*/2857getElement: function() {2858return this.element;2859},2860/*2861Method: getSize28622863Returns canvas dimensions.28642865Returns:28662867An object with *width* and *height* properties.28682869Example:2870(start code js)2871canvas.getSize(); //returns { width: 900, height: 500 }2872(end code)2873*/2874getSize: function(i) {2875return this.canvases[i || 0].getSize();2876},2877/*2878Method: resize28792880Resizes the canvas.28812882Parameters:28832884width - New canvas width.2885height - New canvas height.28862887Example:28882889(start code js)2890canvas.resize(width, height);2891(end code)28922893*/2894resize: function(width, height) {2895this.getPos(true);2896this.translateOffsetX = this.translateOffsetY = 0;2897this.scaleOffsetX = this.scaleOffsetY = 1;2898for(var i=0, l=this.canvases.length; i<l; i++) {2899this.canvases[i].resize(width, height);2900}2901var style = this.element.style;2902style.width = width + 'px';2903style.height = height + 'px';2904if(this.labelContainer)2905this.labelContainer.style.width = width + 'px';2906},2907/*2908Method: translate29092910Applies a translation to the canvas.29112912Parameters:29132914x - (number) x offset.2915y - (number) y offset.2916disablePlot - (boolean) Default's *false*. Set this to *true* if you don't want to refresh the visualization.29172918Example:29192920(start code js)2921canvas.translate(30, 30);2922(end code)29232924*/2925translate: function(x, y, disablePlot) {2926this.translateOffsetX += x*this.scaleOffsetX;2927this.translateOffsetY += y*this.scaleOffsetY;2928for(var i=0, l=this.canvases.length; i<l; i++) {2929this.canvases[i].translate(x, y, disablePlot);2930}2931},2932/*2933Method: scale29342935Scales the canvas.29362937Parameters:29382939x - (number) scale value.2940y - (number) scale value.2941disablePlot - (boolean) Default's *false*. Set this to *true* if you don't want to refresh the visualization.29422943Example:29442945(start code js)2946canvas.scale(0.5, 0.5);2947(end code)29482949*/2950scale: function(x, y, disablePlot) {2951var px = this.scaleOffsetX * x,2952py = this.scaleOffsetY * y;2953var dx = this.translateOffsetX * (x -1) / px,2954dy = this.translateOffsetY * (y -1) / py;2955this.scaleOffsetX = px;2956this.scaleOffsetY = py;2957for(var i=0, l=this.canvases.length; i<l; i++) {2958this.canvases[i].scale(x, y, true);2959}2960this.translate(dx, dy, false);2961},2962/*2963Method: getPos29642965Returns the canvas position as an *x, y* object.29662967Parameters:29682969force - (boolean) Default's *false*. Set this to *true* if you want to recalculate the position without using any cache information.29702971Returns:29722973An object with *x* and *y* properties.29742975Example:2976(start code js)2977canvas.getPos(true); //returns { x: 900, y: 500 }2978(end code)2979*/2980getPos: function(force){2981if(force || !this.pos) {2982return this.pos = $.getPos(this.getElement());2983}2984return this.pos;2985},2986/*2987Method: clear29882989Clears the canvas.2990*/2991clear: function(i){2992this.canvases[i||0].clear();2993},29942995path: function(type, action){2996var ctx = this.canvases[0].getCtx();2997ctx.beginPath();2998action(ctx);2999ctx[type]();3000ctx.closePath();3001},30023003createLabelContainer: function(type, idLabel, dim) {3004var NS = 'http://www.w3.org/2000/svg';3005if(type == 'HTML' || type == 'Native') {3006return $E('div', {3007'id': idLabel,3008'style': {3009'overflow': 'visible',3010'position': 'absolute',3011'top': 0,3012'left': 0,3013'width': dim.width + 'px',3014'height': 03015}3016});3017} else if(type == 'SVG') {3018var svgContainer = document.createElementNS(NS, 'svg:svg');3019svgContainer.setAttribute("width", dim.width);3020svgContainer.setAttribute('height', dim.height);3021var style = svgContainer.style;3022style.position = 'absolute';3023style.left = style.top = '0px';3024var labelContainer = document.createElementNS(NS, 'svg:g');3025labelContainer.setAttribute('width', dim.width);3026labelContainer.setAttribute('height', dim.height);3027labelContainer.setAttribute('x', 0);3028labelContainer.setAttribute('y', 0);3029labelContainer.setAttribute('id', idLabel);3030svgContainer.appendChild(labelContainer);3031return svgContainer;3032}3033}3034});3035//base canvas wrapper3036Canvas.Base = {};3037Canvas.Base['2D'] = new Class({3038translateOffsetX: 0,3039translateOffsetY: 0,3040scaleOffsetX: 1,3041scaleOffsetY: 1,30423043initialize: function(viz) {3044this.viz = viz;3045this.opt = viz.config;3046this.size = false;3047this.createCanvas();3048this.translateToCenter();3049},3050createCanvas: function() {3051var opt = this.opt,3052width = opt.width,3053height = opt.height;3054this.canvas = $E('canvas', {3055'id': opt.injectInto + opt.idSuffix,3056'width': width,3057'height': height,3058'style': {3059'position': 'absolute',3060'top': 0,3061'left': 0,3062'width': width + 'px',3063'height': height + 'px'3064}3065});3066},3067getCtx: function() {3068if(!this.ctx)3069return this.ctx = this.canvas.getContext('2d');3070return this.ctx;3071},3072getSize: function() {3073if(this.size) return this.size;3074var canvas = this.canvas;3075return this.size = {3076width: canvas.width,3077height: canvas.height3078};3079},3080translateToCenter: function(ps) {3081var size = this.getSize(),3082width = ps? (size.width - ps.width - this.translateOffsetX*2) : size.width;3083height = ps? (size.height - ps.height - this.translateOffsetY*2) : size.height;3084var ctx = this.getCtx();3085ps && ctx.scale(1/this.scaleOffsetX, 1/this.scaleOffsetY);3086ctx.translate(width/2, height/2);3087},3088resize: function(width, height) {3089var size = this.getSize(),3090canvas = this.canvas,3091styles = canvas.style;3092this.size = false;3093canvas.width = width;3094canvas.height = height;3095styles.width = width + "px";3096styles.height = height + "px";3097//small ExCanvas fix3098if(!supportsCanvas) {3099this.translateToCenter(size);3100} else {3101this.translateToCenter();3102}3103this.translateOffsetX =3104this.translateOffsetY = 0;3105this.scaleOffsetX =3106this.scaleOffsetY = 1;3107this.clear();3108this.viz.resize(width, height, this);3109},3110translate: function(x, y, disablePlot) {3111var sx = this.scaleOffsetX,3112sy = this.scaleOffsetY;3113this.translateOffsetX += x*sx;3114this.translateOffsetY += y*sy;3115this.getCtx().translate(x, y);3116!disablePlot && this.plot();3117},3118scale: function(x, y, disablePlot) {3119this.scaleOffsetX *= x;3120this.scaleOffsetY *= y;3121this.getCtx().scale(x, y);3122!disablePlot && this.plot();3123},3124clear: function(){3125var size = this.getSize(),3126ox = this.translateOffsetX,3127oy = this.translateOffsetY,3128sx = this.scaleOffsetX,3129sy = this.scaleOffsetY;3130this.getCtx().clearRect((-size.width / 2 - ox) * 1/sx,3131(-size.height / 2 - oy) * 1/sy,3132size.width * 1/sx, size.height * 1/sy);3133},3134plot: function() {3135this.clear();3136this.viz.plot(this);3137}3138});3139//background canvases3140//document this!3141Canvas.Background = {};3142Canvas.Background.Circles = new Class({3143initialize: function(viz, options) {3144this.viz = viz;3145this.config = $.merge({3146idSuffix: '-bkcanvas',3147levelDistance: 100,3148numberOfCircles: 6,3149CanvasStyles: {},3150offset: 03151}, options);3152},3153resize: function(width, height, base) {3154this.plot(base);3155},3156plot: function(base) {3157var canvas = base.canvas,3158ctx = base.getCtx(),3159conf = this.config,3160styles = conf.CanvasStyles;3161//set canvas styles3162for(var s in styles) ctx[s] = styles[s];3163var n = conf.numberOfCircles,3164rho = conf.levelDistance;3165for(var i=1; i<=n; i++) {3166ctx.beginPath();3167ctx.arc(0, 0, rho * i, 0, 2 * Math.PI, false);3168ctx.stroke();3169ctx.closePath();3170}3171//print labels too!3172}3173});3174})();317531763177/*3178* File: Polar.js3179*3180* Defines the <Polar> class.3181*3182* Description:3183*3184* The <Polar> class, just like the <Complex> class, is used by the <Hypertree>, <ST> and <RGraph> as a 2D point representation.3185*3186* See also:3187*3188* <http://en.wikipedia.org/wiki/Polar_coordinates>3189*3190*/31913192/*3193Class: Polar31943195A multi purpose polar representation.31963197Description:31983199The <Polar> class, just like the <Complex> class, is used by the <Hypertree>, <ST> and <RGraph> as a 2D point representation.32003201See also:32023203<http://en.wikipedia.org/wiki/Polar_coordinates>32043205Parameters:32063207theta - An angle.3208rho - The norm.3209*/32103211var Polar = function(theta, rho) {3212this.theta = theta || 0;3213this.rho = rho || 0;3214};32153216$jit.Polar = Polar;32173218Polar.prototype = {3219/*3220Method: getc32213222Returns a complex number.32233224Parameters:32253226simple - _optional_ If *true*, this method will return only an object holding x and y properties and not a <Complex> instance. Default's *false*.32273228Returns:32293230A complex number.3231*/3232getc: function(simple) {3233return this.toComplex(simple);3234},32353236/*3237Method: getp32383239Returns a <Polar> representation.32403241Returns:32423243A variable in polar coordinates.3244*/3245getp: function() {3246return this;3247},324832493250/*3251Method: set32523253Sets a number.32543255Parameters:32563257v - A <Complex> or <Polar> instance.32583259*/3260set: function(v) {3261v = v.getp();3262this.theta = v.theta; this.rho = v.rho;3263},32643265/*3266Method: setc32673268Sets a <Complex> number.32693270Parameters:32713272x - A <Complex> number real part.3273y - A <Complex> number imaginary part.32743275*/3276setc: function(x, y) {3277this.rho = Math.sqrt(x * x + y * y);3278this.theta = Math.atan2(y, x);3279if(this.theta < 0) this.theta += Math.PI * 2;3280},32813282/*3283Method: setp32843285Sets a polar number.32863287Parameters:32883289theta - A <Polar> number angle property.3290rho - A <Polar> number rho property.32913292*/3293setp: function(theta, rho) {3294this.theta = theta;3295this.rho = rho;3296},32973298/*3299Method: clone33003301Returns a copy of the current object.33023303Returns:33043305A copy of the real object.3306*/3307clone: function() {3308return new Polar(this.theta, this.rho);3309},33103311/*3312Method: toComplex33133314Translates from polar to cartesian coordinates and returns a new <Complex> instance.33153316Parameters:33173318simple - _optional_ If *true* this method will only return an object with x and y properties (and not the whole <Complex> instance). Default's *false*.33193320Returns:33213322A new <Complex> instance.3323*/3324toComplex: function(simple) {3325var x = Math.cos(this.theta) * this.rho;3326var y = Math.sin(this.theta) * this.rho;3327if(simple) return { 'x': x, 'y': y};3328return new Complex(x, y);3329},33303331/*3332Method: add33333334Adds two <Polar> instances.33353336Parameters:33373338polar - A <Polar> number.33393340Returns:33413342A new Polar instance.3343*/3344add: function(polar) {3345return new Polar(this.theta + polar.theta, this.rho + polar.rho);3346},33473348/*3349Method: scale33503351Scales a polar norm.33523353Parameters:33543355number - A scale factor.33563357Returns:33583359A new Polar instance.3360*/3361scale: function(number) {3362return new Polar(this.theta, this.rho * number);3363},33643365/*3366Method: equals33673368Comparison method.33693370Returns *true* if the theta and rho properties are equal.33713372Parameters:33733374c - A <Polar> number.33753376Returns:33773378*true* if the theta and rho parameters for these objects are equal. *false* otherwise.3379*/3380equals: function(c) {3381return this.theta == c.theta && this.rho == c.rho;3382},33833384/*3385Method: $add33863387Adds two <Polar> instances affecting the current object.33883389Paramters:33903391polar - A <Polar> instance.33923393Returns:33943395The changed object.3396*/3397$add: function(polar) {3398this.theta = this.theta + polar.theta; this.rho += polar.rho;3399return this;3400},34013402/*3403Method: $madd34043405Adds two <Polar> instances affecting the current object. The resulting theta angle is modulo 2pi.34063407Parameters:34083409polar - A <Polar> instance.34103411Returns:34123413The changed object.3414*/3415$madd: function(polar) {3416this.theta = (this.theta + polar.theta) % (Math.PI * 2); this.rho += polar.rho;3417return this;3418},341934203421/*3422Method: $scale34233424Scales a polar instance affecting the object.34253426Parameters:34273428number - A scaling factor.34293430Returns:34313432The changed object.3433*/3434$scale: function(number) {3435this.rho *= number;3436return this;3437},34383439/*3440Method: isZero34413442Returns *true* if the number is zero.34433444*/3445isZero: function () {3446var almostZero = 0.0001, abs = Math.abs;3447return abs(this.theta) < almostZero && abs(this.rho) < almostZero;3448},34493450/*3451Method: interpolate34523453Calculates a polar interpolation between two points at a given delta moment.34543455Parameters:34563457elem - A <Polar> instance.3458delta - A delta factor ranging [0, 1].34593460Returns:34613462A new <Polar> instance representing an interpolation between _this_ and _elem_3463*/3464interpolate: function(elem, delta) {3465var pi = Math.PI, pi2 = pi * 2;3466var ch = function(t) {3467var a = (t < 0)? (t % pi2) + pi2 : t % pi2;3468return a;3469};3470var tt = this.theta, et = elem.theta;3471var sum, diff = Math.abs(tt - et);3472if(diff == pi) {3473if(tt > et) {3474sum = ch((et + ((tt - pi2) - et) * delta)) ;3475} else {3476sum = ch((et - pi2 + (tt - (et)) * delta));3477}3478} else if(diff >= pi) {3479if(tt > et) {3480sum = ch((et + ((tt - pi2) - et) * delta)) ;3481} else {3482sum = ch((et - pi2 + (tt - (et - pi2)) * delta));3483}3484} else {3485sum = ch((et + (tt - et) * delta)) ;3486}3487var r = (this.rho - elem.rho) * delta + elem.rho;3488return {3489'theta': sum,3490'rho': r3491};3492}3493};349434953496var $P = function(a, b) { return new Polar(a, b); };34973498Polar.KER = $P(0, 0);3499350035013502/*3503* File: Complex.js3504*3505* Defines the <Complex> class.3506*3507* Description:3508*3509* The <Complex> class, just like the <Polar> class, is used by the <Hypertree>, <ST> and <RGraph> as a 2D point representation.3510*3511* See also:3512*3513* <http://en.wikipedia.org/wiki/Complex_number>3514*3515*/35163517/*3518Class: Complex35193520A multi-purpose Complex Class with common methods.35213522Description:35233524The <Complex> class, just like the <Polar> class, is used by the <Hypertree>, <ST> and <RGraph> as a 2D point representation.35253526See also:35273528<http://en.wikipedia.org/wiki/Complex_number>35293530Parameters:35313532x - _optional_ A Complex number real part.3533y - _optional_ A Complex number imaginary part.35343535*/35363537var Complex = function(x, y) {3538this.x = x || 0;3539this.y = y || 0;3540};35413542$jit.Complex = Complex;35433544Complex.prototype = {3545/*3546Method: getc35473548Returns a complex number.35493550Returns:35513552A complex number.3553*/3554getc: function() {3555return this;3556},35573558/*3559Method: getp35603561Returns a <Polar> representation of this number.35623563Parameters:35643565simple - _optional_ If *true*, this method will return only an object holding theta and rho properties and not a <Polar> instance. Default's *false*.35663567Returns:35683569A variable in <Polar> coordinates.3570*/3571getp: function(simple) {3572return this.toPolar(simple);3573},357435753576/*3577Method: set35783579Sets a number.35803581Parameters:35823583c - A <Complex> or <Polar> instance.35843585*/3586set: function(c) {3587c = c.getc(true);3588this.x = c.x;3589this.y = c.y;3590},35913592/*3593Method: setc35943595Sets a complex number.35963597Parameters:35983599x - A <Complex> number Real part.3600y - A <Complex> number Imaginary part.36013602*/3603setc: function(x, y) {3604this.x = x;3605this.y = y;3606},36073608/*3609Method: setp36103611Sets a polar number.36123613Parameters:36143615theta - A <Polar> number theta property.3616rho - A <Polar> number rho property.36173618*/3619setp: function(theta, rho) {3620this.x = Math.cos(theta) * rho;3621this.y = Math.sin(theta) * rho;3622},36233624/*3625Method: clone36263627Returns a copy of the current object.36283629Returns:36303631A copy of the real object.3632*/3633clone: function() {3634return new Complex(this.x, this.y);3635},36363637/*3638Method: toPolar36393640Transforms cartesian to polar coordinates.36413642Parameters:36433644simple - _optional_ If *true* this method will only return an object with theta and rho properties (and not the whole <Polar> instance). Default's *false*.36453646Returns:36473648A new <Polar> instance.3649*/36503651toPolar: function(simple) {3652var rho = this.norm();3653var atan = Math.atan2(this.y, this.x);3654if(atan < 0) atan += Math.PI * 2;3655if(simple) return { 'theta': atan, 'rho': rho };3656return new Polar(atan, rho);3657},3658/*3659Method: norm36603661Calculates a <Complex> number norm.36623663Returns:36643665A real number representing the complex norm.3666*/3667norm: function () {3668return Math.sqrt(this.squaredNorm());3669},36703671/*3672Method: squaredNorm36733674Calculates a <Complex> number squared norm.36753676Returns:36773678A real number representing the complex squared norm.3679*/3680squaredNorm: function () {3681return this.x*this.x + this.y*this.y;3682},36833684/*3685Method: add36863687Returns the result of adding two complex numbers.36883689Does not alter the original object.36903691Parameters:36923693pos - A <Complex> instance.36943695Returns:36963697The result of adding two complex numbers.3698*/3699add: function(pos) {3700return new Complex(this.x + pos.x, this.y + pos.y);3701},37023703/*3704Method: prod37053706Returns the result of multiplying two <Complex> numbers.37073708Does not alter the original object.37093710Parameters:37113712pos - A <Complex> instance.37133714Returns:37153716The result of multiplying two complex numbers.3717*/3718prod: function(pos) {3719return new Complex(this.x*pos.x - this.y*pos.y, this.y*pos.x + this.x*pos.y);3720},37213722/*3723Method: conjugate37243725Returns the conjugate of this <Complex> number.37263727Does not alter the original object.37283729Returns:37303731The conjugate of this <Complex> number.3732*/3733conjugate: function() {3734return new Complex(this.x, -this.y);3735},373637373738/*3739Method: scale37403741Returns the result of scaling a <Complex> instance.37423743Does not alter the original object.37443745Parameters:37463747factor - A scale factor.37483749Returns:37503751The result of scaling this complex to a factor.3752*/3753scale: function(factor) {3754return new Complex(this.x * factor, this.y * factor);3755},37563757/*3758Method: equals37593760Comparison method.37613762Returns *true* if both real and imaginary parts are equal.37633764Parameters:37653766c - A <Complex> instance.37673768Returns:37693770A boolean instance indicating if both <Complex> numbers are equal.3771*/3772equals: function(c) {3773return this.x == c.x && this.y == c.y;3774},37753776/*3777Method: $add37783779Returns the result of adding two <Complex> numbers.37803781Alters the original object.37823783Parameters:37843785pos - A <Complex> instance.37863787Returns:37883789The result of adding two complex numbers.3790*/3791$add: function(pos) {3792this.x += pos.x; this.y += pos.y;3793return this;3794},37953796/*3797Method: $prod37983799Returns the result of multiplying two <Complex> numbers.38003801Alters the original object.38023803Parameters:38043805pos - A <Complex> instance.38063807Returns:38083809The result of multiplying two complex numbers.3810*/3811$prod:function(pos) {3812var x = this.x, y = this.y;3813this.x = x*pos.x - y*pos.y;3814this.y = y*pos.x + x*pos.y;3815return this;3816},38173818/*3819Method: $conjugate38203821Returns the conjugate for this <Complex>.38223823Alters the original object.38243825Returns:38263827The conjugate for this complex.3828*/3829$conjugate: function() {3830this.y = -this.y;3831return this;3832},38333834/*3835Method: $scale38363837Returns the result of scaling a <Complex> instance.38383839Alters the original object.38403841Parameters:38423843factor - A scale factor.38443845Returns:38463847The result of scaling this complex to a factor.3848*/3849$scale: function(factor) {3850this.x *= factor; this.y *= factor;3851return this;3852},38533854/*3855Method: $div38563857Returns the division of two <Complex> numbers.38583859Alters the original object.38603861Parameters:38623863pos - A <Complex> number.38643865Returns:38663867The result of scaling this complex to a factor.3868*/3869$div: function(pos) {3870var x = this.x, y = this.y;3871var sq = pos.squaredNorm();3872this.x = x * pos.x + y * pos.y; this.y = y * pos.x - x * pos.y;3873return this.$scale(1 / sq);3874},38753876/*3877Method: isZero38783879Returns *true* if the number is zero.38803881*/3882isZero: function () {3883var almostZero = 0.0001, abs = Math.abs;3884return abs(this.x) < almostZero && abs(this.y) < almostZero;3885}3886};38873888var $C = function(a, b) { return new Complex(a, b); };38893890Complex.KER = $C(0, 0);3891389238933894/*3895* File: Graph.js3896*3897*/38983899/*3900Class: Graph39013902A Graph Class that provides useful manipulation functions. You can find more manipulation methods in the <Graph.Util> object.39033904An instance of this class can be accessed by using the *graph* parameter of any tree or graph visualization.39053906Example:39073908(start code js)3909//create new visualization3910var viz = new $jit.Viz(options);3911//load JSON data3912viz.loadJSON(json);3913//access model3914viz.graph; //<Graph> instance3915(end code)39163917Implements:39183919The following <Graph.Util> methods are implemented in <Graph>39203921- <Graph.Util.getNode>3922- <Graph.Util.eachNode>3923- <Graph.Util.computeLevels>3924- <Graph.Util.eachBFS>3925- <Graph.Util.clean>3926- <Graph.Util.getClosestNodeToPos>3927- <Graph.Util.getClosestNodeToOrigin>39283929*/39303931$jit.Graph = new Class({39323933initialize: function(opt, Node, Edge, Label) {3934var innerOptions = {3935'klass': Complex,3936'Node': {}3937};3938this.Node = Node;3939this.Edge = Edge;3940this.Label = Label;3941this.opt = $.merge(innerOptions, opt || {});3942this.nodes = {};3943this.edges = {};39443945//add nodeList methods3946var that = this;3947this.nodeList = {};3948for(var p in Accessors) {3949that.nodeList[p] = (function(p) {3950return function() {3951var args = Array.prototype.slice.call(arguments);3952that.eachNode(function(n) {3953n[p].apply(n, args);3954});3955};3956})(p);3957}39583959},39603961/*3962Method: getNode39633964Returns a <Graph.Node> by *id*.39653966Parameters:39673968id - (string) A <Graph.Node> id.39693970Example:39713972(start code js)3973var node = graph.getNode('nodeId');3974(end code)3975*/3976getNode: function(id) {3977if(this.hasNode(id)) return this.nodes[id];3978return false;3979},39803981/*3982Method: get39833984An alias for <Graph.Util.getNode>. Returns a node by *id*.39853986Parameters:39873988id - (string) A <Graph.Node> id.39893990Example:39913992(start code js)3993var node = graph.get('nodeId');3994(end code)3995*/3996get: function(id) {3997return this.getNode(id);3998},39994000/*4001Method: getByName40024003Returns a <Graph.Node> by *name*.40044005Parameters:40064007name - (string) A <Graph.Node> name.40084009Example:40104011(start code js)4012var node = graph.getByName('someName');4013(end code)4014*/4015getByName: function(name) {4016for(var id in this.nodes) {4017var n = this.nodes[id];4018if(n.name == name) return n;4019}4020return false;4021},40224023/*4024Method: getAdjacence40254026Returns a <Graph.Adjacence> object connecting nodes with ids *id* and *id2*.40274028Parameters:40294030id - (string) A <Graph.Node> id.4031id2 - (string) A <Graph.Node> id.4032*/4033getAdjacence: function (id, id2) {4034if(id in this.edges) {4035return this.edges[id][id2];4036}4037return false;4038},40394040/*4041Method: addNode40424043Adds a node.40444045Parameters:40464047obj - An object with the properties described below40484049id - (string) A node id4050name - (string) A node's name4051data - (object) A node's data hash40524053See also:4054<Graph.Node>40554056*/4057addNode: function(obj) {4058if(!this.nodes[obj.id]) {4059var edges = this.edges[obj.id] = {};4060this.nodes[obj.id] = new Graph.Node($.extend({4061'id': obj.id,4062'name': obj.name,4063'data': $.merge(obj.data || {}, {}),4064'adjacencies': edges4065}, this.opt.Node),4066this.opt.klass,4067this.Node,4068this.Edge,4069this.Label);4070}4071return this.nodes[obj.id];4072},40734074/*4075Method: addAdjacence40764077Connects nodes specified by *obj* and *obj2*. If not found, nodes are created.40784079Parameters:40804081obj - (object) A <Graph.Node> object.4082obj2 - (object) Another <Graph.Node> object.4083data - (object) A data object. Used to store some extra information in the <Graph.Adjacence> object created.40844085See also:40864087<Graph.Node>, <Graph.Adjacence>4088*/4089addAdjacence: function (obj, obj2, data) {4090if(!this.hasNode(obj.id)) { this.addNode(obj); }4091if(!this.hasNode(obj2.id)) { this.addNode(obj2); }4092obj = this.nodes[obj.id]; obj2 = this.nodes[obj2.id];4093if(!obj.adjacentTo(obj2)) {4094var adjsObj = this.edges[obj.id] = this.edges[obj.id] || {};4095var adjsObj2 = this.edges[obj2.id] = this.edges[obj2.id] || {};4096adjsObj[obj2.id] = adjsObj2[obj.id] = new Graph.Adjacence(obj, obj2, data, this.Edge, this.Label);4097return adjsObj[obj2.id];4098}4099return this.edges[obj.id][obj2.id];4100},41014102/*4103Method: removeNode41044105Removes a <Graph.Node> matching the specified *id*.41064107Parameters:41084109id - (string) A node's id.41104111*/4112removeNode: function(id) {4113if(this.hasNode(id)) {4114delete this.nodes[id];4115var adjs = this.edges[id];4116for(var to in adjs) {4117delete this.edges[to][id];4118}4119delete this.edges[id];4120}4121},41224123/*4124Method: removeAdjacence41254126Removes a <Graph.Adjacence> matching *id1* and *id2*.41274128Parameters:41294130id1 - (string) A <Graph.Node> id.4131id2 - (string) A <Graph.Node> id.4132*/4133removeAdjacence: function(id1, id2) {4134delete this.edges[id1][id2];4135delete this.edges[id2][id1];4136},41374138/*4139Method: hasNode41404141Returns a boolean indicating if the node belongs to the <Graph> or not.41424143Parameters:41444145id - (string) Node id.4146*/4147hasNode: function(id) {4148return id in this.nodes;4149},41504151/*4152Method: empty41534154Empties the Graph41554156*/4157empty: function() { this.nodes = {}; this.edges = {};}41584159});41604161var Graph = $jit.Graph;41624163/*4164Object: Accessors41654166Defines a set of methods for data, canvas and label styles manipulation implemented by <Graph.Node> and <Graph.Adjacence> instances.41674168*/4169var Accessors;41704171(function () {4172var getDataInternal = function(prefix, prop, type, force, prefixConfig) {4173var data;4174type = type || 'current';4175prefix = "$" + (prefix ? prefix + "-" : "");41764177if(type == 'current') {4178data = this.data;4179} else if(type == 'start') {4180data = this.startData;4181} else if(type == 'end') {4182data = this.endData;4183}41844185var dollar = prefix + prop;41864187if(force) {4188return data[dollar];4189}41904191if(!this.Config.overridable)4192return prefixConfig[prop] || 0;41934194return (dollar in data) ?4195data[dollar] : ((dollar in this.data) ? this.data[dollar] : (prefixConfig[prop] || 0));4196}41974198var setDataInternal = function(prefix, prop, value, type) {4199type = type || 'current';4200prefix = '$' + (prefix ? prefix + '-' : '');42014202var data;42034204if(type == 'current') {4205data = this.data;4206} else if(type == 'start') {4207data = this.startData;4208} else if(type == 'end') {4209data = this.endData;4210}42114212data[prefix + prop] = value;4213}42144215var removeDataInternal = function(prefix, properties) {4216prefix = '$' + (prefix ? prefix + '-' : '');4217var that = this;4218$.each(properties, function(prop) {4219var pref = prefix + prop;4220delete that.data[pref];4221delete that.endData[pref];4222delete that.startData[pref];4223});4224}42254226Accessors = {4227/*4228Method: getData42294230Returns the specified data value property.4231This is useful for querying special/reserved <Graph.Node> data properties4232(i.e dollar prefixed properties).42334234Parameters:42354236prop - (string) The name of the property. The dollar sign is not needed. For4237example *getData(width)* will return *data.$width*.4238type - (string) The type of the data property queried. Default's "current". You can access *start* and *end*4239data properties also. These properties are used when making animations.4240force - (boolean) Whether to obtain the true value of the property (equivalent to4241*data.$prop*) or to check for *node.overridable = true* first.42424243Returns:42444245The value of the dollar prefixed property or the global Node/Edge property4246value if *overridable=false*42474248Example:4249(start code js)4250node.getData('width'); //will return node.data.$width if Node.overridable=true;4251(end code)4252*/4253getData: function(prop, type, force) {4254return getDataInternal.call(this, "", prop, type, force, this.Config);4255},425642574258/*4259Method: setData42604261Sets the current data property with some specific value.4262This method is only useful for reserved (dollar prefixed) properties.42634264Parameters:42654266prop - (string) The name of the property. The dollar sign is not necessary. For4267example *setData(width)* will set *data.$width*.4268value - (mixed) The value to store.4269type - (string) The type of the data property to store. Default's "current" but4270can also be "start" or "end".42714272Example:42734274(start code js)4275node.setData('width', 30);4276(end code)42774278If we were to make an animation of a node/edge width then we could do42794280(start code js)4281var node = viz.getNode('nodeId');4282//set start and end values4283node.setData('width', 10, 'start');4284node.setData('width', 30, 'end');4285//will animate nodes width property4286viz.fx.animate({4287modes: ['node-property:width'],4288duration: 10004289});4290(end code)4291*/4292setData: function(prop, value, type) {4293setDataInternal.call(this, "", prop, value, type);4294},42954296/*4297Method: setDataset42984299Convenience method to set multiple data values at once.43004301Parameters:43024303types - (array|string) A set of 'current', 'end' or 'start' values.4304obj - (object) A hash containing the names and values of the properties to be altered.43054306Example:4307(start code js)4308node.setDataset(['current', 'end'], {4309'width': [100, 5],4310'color': ['#fff', '#ccc']4311});4312//...or also4313node.setDataset('end', {4314'width': 5,4315'color': '#ccc'4316});4317(end code)43184319See also:43204321<Accessors.setData>43224323*/4324setDataset: function(types, obj) {4325types = $.splat(types);4326for(var attr in obj) {4327for(var i=0, val = $.splat(obj[attr]), l=types.length; i<l; i++) {4328this.setData(attr, val[i], types[i]);4329}4330}4331},43324333/*4334Method: removeData43354336Remove data properties.43374338Parameters:43394340One or more property names as arguments. The dollar sign is not needed.43414342Example:4343(start code js)4344node.removeData('width'); //now the default width value is returned4345(end code)4346*/4347removeData: function() {4348removeDataInternal.call(this, "", Array.prototype.slice.call(arguments));4349},43504351/*4352Method: getCanvasStyle43534354Returns the specified canvas style data value property. This is useful for4355querying special/reserved <Graph.Node> canvas style data properties (i.e.4356dollar prefixed properties that match with $canvas-<name of canvas style>).43574358Parameters:43594360prop - (string) The name of the property. The dollar sign is not needed. For4361example *getCanvasStyle(shadowBlur)* will return *data[$canvas-shadowBlur]*.4362type - (string) The type of the data property queried. Default's *current*. You can access *start* and *end*4363data properties also.43644365Example:4366(start code js)4367node.getCanvasStyle('shadowBlur');4368(end code)43694370See also:43714372<Accessors.getData>4373*/4374getCanvasStyle: function(prop, type, force) {4375return getDataInternal.call(4376this, 'canvas', prop, type, force, this.Config.CanvasStyles);4377},43784379/*4380Method: setCanvasStyle43814382Sets the canvas style data property with some specific value.4383This method is only useful for reserved (dollar prefixed) properties.43844385Parameters:43864387prop - (string) Name of the property. Can be any canvas property like 'shadowBlur', 'shadowColor', 'strokeStyle', etc.4388value - (mixed) The value to set to the property.4389type - (string) Default's *current*. Whether to set *start*, *current* or *end* type properties.43904391Example:43924393(start code js)4394node.setCanvasStyle('shadowBlur', 30);4395(end code)43964397If we were to make an animation of a node/edge shadowBlur canvas style then we could do43984399(start code js)4400var node = viz.getNode('nodeId');4401//set start and end values4402node.setCanvasStyle('shadowBlur', 10, 'start');4403node.setCanvasStyle('shadowBlur', 30, 'end');4404//will animate nodes canvas style property for nodes4405viz.fx.animate({4406modes: ['node-style:shadowBlur'],4407duration: 10004408});4409(end code)44104411See also:44124413<Accessors.setData>.4414*/4415setCanvasStyle: function(prop, value, type) {4416setDataInternal.call(this, 'canvas', prop, value, type);4417},44184419/*4420Method: setCanvasStyles44214422Convenience method to set multiple styles at once.44234424Parameters:44254426types - (array|string) A set of 'current', 'end' or 'start' values.4427obj - (object) A hash containing the names and values of the properties to be altered.44284429See also:44304431<Accessors.setDataset>.4432*/4433setCanvasStyles: function(types, obj) {4434types = $.splat(types);4435for(var attr in obj) {4436for(var i=0, val = $.splat(obj[attr]), l=types.length; i<l; i++) {4437this.setCanvasStyle(attr, val[i], types[i]);4438}4439}4440},44414442/*4443Method: removeCanvasStyle44444445Remove canvas style properties from data.44464447Parameters:44484449A variable number of canvas style strings.44504451See also:44524453<Accessors.removeData>.4454*/4455removeCanvasStyle: function() {4456removeDataInternal.call(this, 'canvas', Array.prototype.slice.call(arguments));4457},44584459/*4460Method: getLabelData44614462Returns the specified label data value property. This is useful for4463querying special/reserved <Graph.Node> label options (i.e.4464dollar prefixed properties that match with $label-<name of label style>).44654466Parameters:44674468prop - (string) The name of the property. The dollar sign prefix is not needed. For4469example *getLabelData(size)* will return *data[$label-size]*.4470type - (string) The type of the data property queried. Default's *current*. You can access *start* and *end*4471data properties also.44724473See also:44744475<Accessors.getData>.4476*/4477getLabelData: function(prop, type, force) {4478return getDataInternal.call(4479this, 'label', prop, type, force, this.Label);4480},44814482/*4483Method: setLabelData44844485Sets the current label data with some specific value.4486This method is only useful for reserved (dollar prefixed) properties.44874488Parameters:44894490prop - (string) Name of the property. Can be any canvas property like 'shadowBlur', 'shadowColor', 'strokeStyle', etc.4491value - (mixed) The value to set to the property.4492type - (string) Default's *current*. Whether to set *start*, *current* or *end* type properties.44934494Example:44954496(start code js)4497node.setLabelData('size', 30);4498(end code)44994500If we were to make an animation of a node label size then we could do45014502(start code js)4503var node = viz.getNode('nodeId');4504//set start and end values4505node.setLabelData('size', 10, 'start');4506node.setLabelData('size', 30, 'end');4507//will animate nodes label size4508viz.fx.animate({4509modes: ['label-property:size'],4510duration: 10004511});4512(end code)45134514See also:45154516<Accessors.setData>.4517*/4518setLabelData: function(prop, value, type) {4519setDataInternal.call(this, 'label', prop, value, type);4520},45214522/*4523Method: setLabelDataset45244525Convenience function to set multiple label data at once.45264527Parameters:45284529types - (array|string) A set of 'current', 'end' or 'start' values.4530obj - (object) A hash containing the names and values of the properties to be altered.45314532See also:45334534<Accessors.setDataset>.4535*/4536setLabelDataset: function(types, obj) {4537types = $.splat(types);4538for(var attr in obj) {4539for(var i=0, val = $.splat(obj[attr]), l=types.length; i<l; i++) {4540this.setLabelData(attr, val[i], types[i]);4541}4542}4543},45444545/*4546Method: removeLabelData45474548Remove label properties from data.45494550Parameters:45514552A variable number of label property strings.45534554See also:45554556<Accessors.removeData>.4557*/4558removeLabelData: function() {4559removeDataInternal.call(this, 'label', Array.prototype.slice.call(arguments));4560}4561};4562})();45634564/*4565Class: Graph.Node45664567A <Graph> node.45684569Implements:45704571<Accessors> methods.45724573The following <Graph.Util> methods are implemented by <Graph.Node>45744575- <Graph.Util.eachAdjacency>4576- <Graph.Util.eachLevel>4577- <Graph.Util.eachSubgraph>4578- <Graph.Util.eachSubnode>4579- <Graph.Util.anySubnode>4580- <Graph.Util.getSubnodes>4581- <Graph.Util.getParents>4582- <Graph.Util.isDescendantOf>4583*/4584Graph.Node = new Class({45854586initialize: function(opt, klass, Node, Edge, Label) {4587var innerOptions = {4588'id': '',4589'name': '',4590'data': {},4591'startData': {},4592'endData': {},4593'adjacencies': {},45944595'selected': false,4596'drawn': false,4597'exist': false,45984599'angleSpan': {4600'begin': 0,4601'end' : 04602},46034604'pos': new klass,4605'startPos': new klass,4606'endPos': new klass4607};46084609$.extend(this, $.extend(innerOptions, opt));4610this.Config = this.Node = Node;4611this.Edge = Edge;4612this.Label = Label;4613},46144615/*4616Method: adjacentTo46174618Indicates if the node is adjacent to the node specified by id46194620Parameters:46214622id - (string) A node id.46234624Example:4625(start code js)4626node.adjacentTo('nodeId') == true;4627(end code)4628*/4629adjacentTo: function(node) {4630return node.id in this.adjacencies;4631},46324633/*4634Method: getAdjacency46354636Returns a <Graph.Adjacence> object connecting the current <Graph.Node> and the node having *id* as id.46374638Parameters:46394640id - (string) A node id.4641*/4642getAdjacency: function(id) {4643return this.adjacencies[id];4644},46454646/*4647Method: getPos46484649Returns the position of the node.46504651Parameters:46524653type - (string) Default's *current*. Possible values are "start", "end" or "current".46544655Returns:46564657A <Complex> or <Polar> instance.46584659Example:4660(start code js)4661var pos = node.getPos('end');4662(end code)4663*/4664getPos: function(type) {4665type = type || "current";4666if(type == "current") {4667return this.pos;4668} else if(type == "end") {4669return this.endPos;4670} else if(type == "start") {4671return this.startPos;4672}4673},4674/*4675Method: setPos46764677Sets the node's position.46784679Parameters:46804681value - (object) A <Complex> or <Polar> instance.4682type - (string) Default's *current*. Possible values are "start", "end" or "current".46834684Example:4685(start code js)4686node.setPos(new $jit.Complex(0, 0), 'end');4687(end code)4688*/4689setPos: function(value, type) {4690type = type || "current";4691var pos;4692if(type == "current") {4693pos = this.pos;4694} else if(type == "end") {4695pos = this.endPos;4696} else if(type == "start") {4697pos = this.startPos;4698}4699pos.set(value);4700}4701});47024703Graph.Node.implement(Accessors);47044705/*4706Class: Graph.Adjacence47074708A <Graph> adjacence (or edge) connecting two <Graph.Nodes>.47094710Implements:47114712<Accessors> methods.47134714See also:47154716<Graph>, <Graph.Node>47174718Properties:47194720nodeFrom - A <Graph.Node> connected by this edge.4721nodeTo - Another <Graph.Node> connected by this edge.4722data - Node data property containing a hash (i.e {}) with custom options.4723*/4724Graph.Adjacence = new Class({47254726initialize: function(nodeFrom, nodeTo, data, Edge, Label) {4727this.nodeFrom = nodeFrom;4728this.nodeTo = nodeTo;4729this.data = data || {};4730this.startData = {};4731this.endData = {};4732this.Config = this.Edge = Edge;4733this.Label = Label;4734}4735});47364737Graph.Adjacence.implement(Accessors);47384739/*4740Object: Graph.Util47414742<Graph> traversal and processing utility object.47434744Note:47454746For your convenience some of these methods have also been appended to <Graph> and <Graph.Node> classes.4747*/4748Graph.Util = {4749/*4750filter47514752For internal use only. Provides a filtering function based on flags.4753*/4754filter: function(param) {4755if(!param || !($.type(param) == 'string')) return function() { return true; };4756var props = param.split(" ");4757return function(elem) {4758for(var i=0; i<props.length; i++) {4759if(elem[props[i]]) {4760return false;4761}4762}4763return true;4764};4765},4766/*4767Method: getNode47684769Returns a <Graph.Node> by *id*.47704771Also implemented by:47724773<Graph>47744775Parameters:47764777graph - (object) A <Graph> instance.4778id - (string) A <Graph.Node> id.47794780Example:47814782(start code js)4783$jit.Graph.Util.getNode(graph, 'nodeid');4784//or...4785graph.getNode('nodeid');4786(end code)4787*/4788getNode: function(graph, id) {4789return graph.nodes[id];4790},47914792/*4793Method: eachNode47944795Iterates over <Graph> nodes performing an *action*.47964797Also implemented by:47984799<Graph>.48004801Parameters:48024803graph - (object) A <Graph> instance.4804action - (function) A callback function having a <Graph.Node> as first formal parameter.48054806Example:4807(start code js)4808$jit.Graph.Util.eachNode(graph, function(node) {4809alert(node.name);4810});4811//or...4812graph.eachNode(function(node) {4813alert(node.name);4814});4815(end code)4816*/4817eachNode: function(graph, action, flags) {4818var filter = this.filter(flags);4819for(var i in graph.nodes) {4820if(filter(graph.nodes[i])) action(graph.nodes[i]);4821}4822},48234824/*4825Method: each48264827Iterates over <Graph> nodes performing an *action*. It's an alias for <Graph.Util.eachNode>.48284829Also implemented by:48304831<Graph>.48324833Parameters:48344835graph - (object) A <Graph> instance.4836action - (function) A callback function having a <Graph.Node> as first formal parameter.48374838Example:4839(start code js)4840$jit.Graph.Util.each(graph, function(node) {4841alert(node.name);4842});4843//or...4844graph.each(function(node) {4845alert(node.name);4846});4847(end code)4848*/4849each: function(graph, action, flags) {4850this.eachNode(graph, action, flags);4851},48524853/*4854Method: eachAdjacency48554856Iterates over <Graph.Node> adjacencies applying the *action* function.48574858Also implemented by:48594860<Graph.Node>.48614862Parameters:48634864node - (object) A <Graph.Node>.4865action - (function) A callback function having <Graph.Adjacence> as first formal parameter.48664867Example:4868(start code js)4869$jit.Graph.Util.eachAdjacency(node, function(adj) {4870alert(adj.nodeTo.name);4871});4872//or...4873node.eachAdjacency(function(adj) {4874alert(adj.nodeTo.name);4875});4876(end code)4877*/4878eachAdjacency: function(node, action, flags) {4879var adj = node.adjacencies, filter = this.filter(flags);4880for(var id in adj) {4881var a = adj[id];4882if(filter(a)) {4883if(a.nodeFrom != node) {4884var tmp = a.nodeFrom;4885a.nodeFrom = a.nodeTo;4886a.nodeTo = tmp;4887}4888action(a, id);4889}4890}4891},48924893/*4894Method: computeLevels48954896Performs a BFS traversal setting the correct depth for each node.48974898Also implemented by:48994900<Graph>.49014902Note:49034904The depth of each node can then be accessed by4905>node._depth49064907Parameters:49084909graph - (object) A <Graph>.4910id - (string) A starting node id for the BFS traversal.4911startDepth - (optional|number) A minimum depth value. Default's 0.49124913*/4914computeLevels: function(graph, id, startDepth, flags) {4915startDepth = startDepth || 0;4916var filter = this.filter(flags);4917this.eachNode(graph, function(elem) {4918elem._flag = false;4919elem._depth = -1;4920}, flags);4921var root = graph.getNode(id);4922root._depth = startDepth;4923var queue = [root];4924while(queue.length != 0) {4925var node = queue.pop();4926node._flag = true;4927this.eachAdjacency(node, function(adj) {4928var n = adj.nodeTo;4929if(n._flag == false && filter(n)) {4930if(n._depth < 0) n._depth = node._depth + 1 + startDepth;4931queue.unshift(n);4932}4933}, flags);4934}4935},49364937/*4938Method: eachBFS49394940Performs a BFS traversal applying *action* to each <Graph.Node>.49414942Also implemented by:49434944<Graph>.49454946Parameters:49474948graph - (object) A <Graph>.4949id - (string) A starting node id for the BFS traversal.4950action - (function) A callback function having a <Graph.Node> as first formal parameter.49514952Example:4953(start code js)4954$jit.Graph.Util.eachBFS(graph, 'mynodeid', function(node) {4955alert(node.name);4956});4957//or...4958graph.eachBFS('mynodeid', function(node) {4959alert(node.name);4960});4961(end code)4962*/4963eachBFS: function(graph, id, action, flags) {4964var filter = this.filter(flags);4965this.clean(graph);4966var queue = [graph.getNode(id)];4967while(queue.length != 0) {4968var node = queue.pop();4969node._flag = true;4970action(node, node._depth);4971this.eachAdjacency(node, function(adj) {4972var n = adj.nodeTo;4973if(n._flag == false && filter(n)) {4974n._flag = true;4975queue.unshift(n);4976}4977}, flags);4978}4979},49804981/*4982Method: eachLevel49834984Iterates over a node's subgraph applying *action* to the nodes of relative depth between *levelBegin* and *levelEnd*.49854986Also implemented by:49874988<Graph.Node>.49894990Parameters:49914992node - (object) A <Graph.Node>.4993levelBegin - (number) A relative level value.4994levelEnd - (number) A relative level value.4995action - (function) A callback function having a <Graph.Node> as first formal parameter.49964997*/4998eachLevel: function(node, levelBegin, levelEnd, action, flags) {4999var d = node._depth, filter = this.filter(flags), that = this;5000levelEnd = levelEnd === false? Number.MAX_VALUE -d : levelEnd;5001(function loopLevel(node, levelBegin, levelEnd) {5002var d = node._depth;5003if(d >= levelBegin && d <= levelEnd && filter(node)) action(node, d);5004if(d < levelEnd) {5005that.eachAdjacency(node, function(adj) {5006var n = adj.nodeTo;5007if(n._depth > d) loopLevel(n, levelBegin, levelEnd);5008});5009}5010})(node, levelBegin + d, levelEnd + d);5011},50125013/*5014Method: eachSubgraph50155016Iterates over a node's children recursively.50175018Also implemented by:50195020<Graph.Node>.50215022Parameters:5023node - (object) A <Graph.Node>.5024action - (function) A callback function having a <Graph.Node> as first formal parameter.50255026Example:5027(start code js)5028$jit.Graph.Util.eachSubgraph(node, function(node) {5029alert(node.name);5030});5031//or...5032node.eachSubgraph(function(node) {5033alert(node.name);5034});5035(end code)5036*/5037eachSubgraph: function(node, action, flags) {5038this.eachLevel(node, 0, false, action, flags);5039},50405041/*5042Method: eachSubnode50435044Iterates over a node's children (without deeper recursion).50455046Also implemented by:50475048<Graph.Node>.50495050Parameters:5051node - (object) A <Graph.Node>.5052action - (function) A callback function having a <Graph.Node> as first formal parameter.50535054Example:5055(start code js)5056$jit.Graph.Util.eachSubnode(node, function(node) {5057alert(node.name);5058});5059//or...5060node.eachSubnode(function(node) {5061alert(node.name);5062});5063(end code)5064*/5065eachSubnode: function(node, action, flags) {5066this.eachLevel(node, 1, 1, action, flags);5067},50685069/*5070Method: anySubnode50715072Returns *true* if any subnode matches the given condition.50735074Also implemented by:50755076<Graph.Node>.50775078Parameters:5079node - (object) A <Graph.Node>.5080cond - (function) A callback function returning a Boolean instance. This function has as first formal parameter a <Graph.Node>.50815082Example:5083(start code js)5084$jit.Graph.Util.anySubnode(node, function(node) { return node.name == "mynodename"; });5085//or...5086node.anySubnode(function(node) { return node.name == 'mynodename'; });5087(end code)5088*/5089anySubnode: function(node, cond, flags) {5090var flag = false;5091cond = cond || $.lambda(true);5092var c = $.type(cond) == 'string'? function(n) { return n[cond]; } : cond;5093this.eachSubnode(node, function(elem) {5094if(c(elem)) flag = true;5095}, flags);5096return flag;5097},50985099/*5100Method: getSubnodes51015102Collects all subnodes for a specified node.5103The *level* parameter filters nodes having relative depth of *level* from the root node.51045105Also implemented by:51065107<Graph.Node>.51085109Parameters:5110node - (object) A <Graph.Node>.5111level - (optional|number) Default's *0*. A starting relative depth for collecting nodes.51125113Returns:5114An array of nodes.51155116*/5117getSubnodes: function(node, level, flags) {5118var ans = [], that = this;5119level = level || 0;5120var levelStart, levelEnd;5121if($.type(level) == 'array') {5122levelStart = level[0];5123levelEnd = level[1];5124} else {5125levelStart = level;5126levelEnd = Number.MAX_VALUE - node._depth;5127}5128this.eachLevel(node, levelStart, levelEnd, function(n) {5129ans.push(n);5130}, flags);5131return ans;5132},513351345135/*5136Method: getParents51375138Returns an Array of <Graph.Nodes> which are parents of the given node.51395140Also implemented by:51415142<Graph.Node>.51435144Parameters:5145node - (object) A <Graph.Node>.51465147Returns:5148An Array of <Graph.Nodes>.51495150Example:5151(start code js)5152var pars = $jit.Graph.Util.getParents(node);5153//or...5154var pars = node.getParents();51555156if(pars.length > 0) {5157//do stuff with parents5158}5159(end code)5160*/5161getParents: function(node) {5162var ans = [];5163this.eachAdjacency(node, function(adj) {5164var n = adj.nodeTo;5165if(n._depth < node._depth) ans.push(n);5166});5167return ans;5168},51695170/*5171Method: isDescendantOf51725173Returns a boolean indicating if some node is descendant of the node with the given id.51745175Also implemented by:51765177<Graph.Node>.517851795180Parameters:5181node - (object) A <Graph.Node>.5182id - (string) A <Graph.Node> id.51835184Example:5185(start code js)5186$jit.Graph.Util.isDescendantOf(node, "nodeid"); //true|false5187//or...5188node.isDescendantOf('nodeid');//true|false5189(end code)5190*/5191isDescendantOf: function(node, id) {5192if(node.id == id) return true;5193var pars = this.getParents(node), ans = false;5194for ( var i = 0; !ans && i < pars.length; i++) {5195ans = ans || this.isDescendantOf(pars[i], id);5196}5197return ans;5198},51995200/*5201Method: clean52025203Cleans flags from nodes.52045205Also implemented by:52065207<Graph>.52085209Parameters:5210graph - A <Graph> instance.5211*/5212clean: function(graph) { this.eachNode(graph, function(elem) { elem._flag = false; }); },52135214/*5215Method: getClosestNodeToOrigin52165217Returns the closest node to the center of canvas.52185219Also implemented by:52205221<Graph>.52225223Parameters:52245225graph - (object) A <Graph> instance.5226prop - (optional|string) Default's 'current'. A <Graph.Node> position property. Possible properties are 'start', 'current' or 'end'.52275228*/5229getClosestNodeToOrigin: function(graph, prop, flags) {5230return this.getClosestNodeToPos(graph, Polar.KER, prop, flags);5231},52325233/*5234Method: getClosestNodeToPos52355236Returns the closest node to the given position.52375238Also implemented by:52395240<Graph>.52415242Parameters:52435244graph - (object) A <Graph> instance.5245pos - (object) A <Complex> or <Polar> instance.5246prop - (optional|string) Default's *current*. A <Graph.Node> position property. Possible properties are 'start', 'current' or 'end'.52475248*/5249getClosestNodeToPos: function(graph, pos, prop, flags) {5250var node = null;5251prop = prop || 'current';5252pos = pos && pos.getc(true) || Complex.KER;5253var distance = function(a, b) {5254var d1 = a.x - b.x, d2 = a.y - b.y;5255return d1 * d1 + d2 * d2;5256};5257this.eachNode(graph, function(elem) {5258node = (node == null || distance(elem.getPos(prop).getc(true), pos) < distance(5259node.getPos(prop).getc(true), pos)) ? elem : node;5260}, flags);5261return node;5262}5263};52645265//Append graph methods to <Graph>5266$.each(['get', 'getNode', 'each', 'eachNode', 'computeLevels', 'eachBFS', 'clean', 'getClosestNodeToPos', 'getClosestNodeToOrigin'], function(m) {5267Graph.prototype[m] = function() {5268return Graph.Util[m].apply(Graph.Util, [this].concat(Array.prototype.slice.call(arguments)));5269};5270});52715272//Append node methods to <Graph.Node>5273$.each(['eachAdjacency', 'eachLevel', 'eachSubgraph', 'eachSubnode', 'anySubnode', 'getSubnodes', 'getParents', 'isDescendantOf'], function(m) {5274Graph.Node.prototype[m] = function() {5275return Graph.Util[m].apply(Graph.Util, [this].concat(Array.prototype.slice.call(arguments)));5276};5277});52785279/*5280* File: Graph.Op.js5281*5282*/52835284/*5285Object: Graph.Op52865287Perform <Graph> operations like adding/removing <Graph.Nodes> or <Graph.Adjacences>,5288morphing a <Graph> into another <Graph>, contracting or expanding subtrees, etc.52895290*/5291Graph.Op = {52925293options: {5294type: 'nothing',5295duration: 2000,5296hideLabels: true,5297fps:305298},52995300initialize: function(viz) {5301this.viz = viz;5302},53035304/*5305Method: removeNode53065307Removes one or more <Graph.Nodes> from the visualization.5308It can also perform several animations like fading sequentially, fading concurrently, iterating or replotting.53095310Parameters:53115312node - (string|array) The node's id. Can also be an array having many ids.5313opt - (object) Animation options. It's an object with optional properties described below5314type - (string) Default's *nothing*. Type of the animation. Can be "nothing", "replot", "fade:seq", "fade:con" or "iter".5315duration - Described in <Options.Fx>.5316fps - Described in <Options.Fx>.5317transition - Described in <Options.Fx>.5318hideLabels - (boolean) Default's *true*. Hide labels during the animation.53195320Example:5321(start code js)5322var viz = new $jit.Viz(options);5323viz.op.removeNode('nodeId', {5324type: 'fade:seq',5325duration: 1000,5326hideLabels: false,5327transition: $jit.Trans.Quart.easeOut5328});5329//or also5330viz.op.removeNode(['someId', 'otherId'], {5331type: 'fade:con',5332duration: 15005333});5334(end code)5335*/53365337removeNode: function(node, opt) {5338var viz = this.viz;5339var options = $.merge(this.options, viz.controller, opt);5340var n = $.splat(node);5341var i, that, nodeObj;5342switch(options.type) {5343case 'nothing':5344for(i=0; i<n.length; i++) viz.graph.removeNode(n[i]);5345break;53465347case 'replot':5348this.removeNode(n, { type: 'nothing' });5349viz.labels.clearLabels();5350viz.refresh(true);5351break;53525353case 'fade:seq': case 'fade':5354that = this;5355//set alpha to 0 for nodes to remove.5356for(i=0; i<n.length; i++) {5357nodeObj = viz.graph.getNode(n[i]);5358nodeObj.setData('alpha', 0, 'end');5359}5360viz.fx.animate($.merge(options, {5361modes: ['node-property:alpha'],5362onComplete: function() {5363that.removeNode(n, { type: 'nothing' });5364viz.labels.clearLabels();5365viz.reposition();5366viz.fx.animate($.merge(options, {5367modes: ['linear']5368}));5369}5370}));5371break;53725373case 'fade:con':5374that = this;5375//set alpha to 0 for nodes to remove. Tag them for being ignored on computing positions.5376for(i=0; i<n.length; i++) {5377nodeObj = viz.graph.getNode(n[i]);5378nodeObj.setData('alpha', 0, 'end');5379nodeObj.ignore = true;5380}5381viz.reposition();5382viz.fx.animate($.merge(options, {5383modes: ['node-property:alpha', 'linear'],5384onComplete: function() {5385that.removeNode(n, { type: 'nothing' });5386options.onComplete && options.onComplete();5387}5388}));5389break;53905391case 'iter':5392that = this;5393viz.fx.sequence({5394condition: function() { return n.length != 0; },5395step: function() { that.removeNode(n.shift(), { type: 'nothing' }); viz.labels.clearLabels(); },5396onComplete: function() { options.onComplete && options.onComplete(); },5397duration: Math.ceil(options.duration / n.length)5398});5399break;54005401default: this.doError();5402}5403},54045405/*5406Method: removeEdge54075408Removes one or more <Graph.Adjacences> from the visualization.5409It can also perform several animations like fading sequentially, fading concurrently, iterating or replotting.54105411Parameters:54125413vertex - (array) An array having two strings which are the ids of the nodes connected by this edge (i.e ['id1', 'id2']). Can also be a two dimensional array holding many edges (i.e [['id1', 'id2'], ['id3', 'id4'], ...]).5414opt - (object) Animation options. It's an object with optional properties described below5415type - (string) Default's *nothing*. Type of the animation. Can be "nothing", "replot", "fade:seq", "fade:con" or "iter".5416duration - Described in <Options.Fx>.5417fps - Described in <Options.Fx>.5418transition - Described in <Options.Fx>.5419hideLabels - (boolean) Default's *true*. Hide labels during the animation.54205421Example:5422(start code js)5423var viz = new $jit.Viz(options);5424viz.op.removeEdge(['nodeId', 'otherId'], {5425type: 'fade:seq',5426duration: 1000,5427hideLabels: false,5428transition: $jit.Trans.Quart.easeOut5429});5430//or also5431viz.op.removeEdge([['someId', 'otherId'], ['id3', 'id4']], {5432type: 'fade:con',5433duration: 15005434});5435(end code)54365437*/5438removeEdge: function(vertex, opt) {5439var viz = this.viz;5440var options = $.merge(this.options, viz.controller, opt);5441var v = ($.type(vertex[0]) == 'string')? [vertex] : vertex;5442var i, that, adj;5443switch(options.type) {5444case 'nothing':5445for(i=0; i<v.length; i++) viz.graph.removeAdjacence(v[i][0], v[i][1]);5446break;54475448case 'replot':5449this.removeEdge(v, { type: 'nothing' });5450viz.refresh(true);5451break;54525453case 'fade:seq': case 'fade':5454that = this;5455//set alpha to 0 for edges to remove.5456for(i=0; i<v.length; i++) {5457adj = viz.graph.getAdjacence(v[i][0], v[i][1]);5458if(adj) {5459adj.setData('alpha', 0,'end');5460}5461}5462viz.fx.animate($.merge(options, {5463modes: ['edge-property:alpha'],5464onComplete: function() {5465that.removeEdge(v, { type: 'nothing' });5466viz.reposition();5467viz.fx.animate($.merge(options, {5468modes: ['linear']5469}));5470}5471}));5472break;54735474case 'fade:con':5475that = this;5476//set alpha to 0 for nodes to remove. Tag them for being ignored when computing positions.5477for(i=0; i<v.length; i++) {5478adj = viz.graph.getAdjacence(v[i][0], v[i][1]);5479if(adj) {5480adj.setData('alpha',0 ,'end');5481adj.ignore = true;5482}5483}5484viz.reposition();5485viz.fx.animate($.merge(options, {5486modes: ['edge-property:alpha', 'linear'],5487onComplete: function() {5488that.removeEdge(v, { type: 'nothing' });5489options.onComplete && options.onComplete();5490}5491}));5492break;54935494case 'iter':5495that = this;5496viz.fx.sequence({5497condition: function() { return v.length != 0; },5498step: function() { that.removeEdge(v.shift(), { type: 'nothing' }); viz.labels.clearLabels(); },5499onComplete: function() { options.onComplete(); },5500duration: Math.ceil(options.duration / v.length)5501});5502break;55035504default: this.doError();5505}5506},55075508/*5509Method: sum55105511Adds a new graph to the visualization.5512The JSON graph (or tree) must at least have a common node with the current graph plotted by the visualization.5513The resulting graph can be defined as follows <http://mathworld.wolfram.com/GraphSum.html>55145515Parameters:55165517json - (object) A json tree or graph structure. See also <Loader.loadJSON>.5518opt - (object) Animation options. It's an object with optional properties described below5519type - (string) Default's *nothing*. Type of the animation. Can be "nothing", "replot", "fade:seq", "fade:con".5520duration - Described in <Options.Fx>.5521fps - Described in <Options.Fx>.5522transition - Described in <Options.Fx>.5523hideLabels - (boolean) Default's *true*. Hide labels during the animation.55245525Example:5526(start code js)5527//...json contains a tree or graph structure...55285529var viz = new $jit.Viz(options);5530viz.op.sum(json, {5531type: 'fade:seq',5532duration: 1000,5533hideLabels: false,5534transition: $jit.Trans.Quart.easeOut5535});5536//or also5537viz.op.sum(json, {5538type: 'fade:con',5539duration: 15005540});5541(end code)55425543*/5544sum: function(json, opt) {5545var viz = this.viz;5546var options = $.merge(this.options, viz.controller, opt), root = viz.root;5547var graph;5548viz.root = opt.id || viz.root;5549switch(options.type) {5550case 'nothing':5551graph = viz.construct(json);5552graph.eachNode(function(elem) {5553elem.eachAdjacency(function(adj) {5554viz.graph.addAdjacence(adj.nodeFrom, adj.nodeTo, adj.data);5555});5556});5557break;55585559case 'replot':5560viz.refresh(true);5561this.sum(json, { type: 'nothing' });5562viz.refresh(true);5563break;55645565case 'fade:seq': case 'fade': case 'fade:con':5566that = this;5567graph = viz.construct(json);55685569//set alpha to 0 for nodes to add.5570var fadeEdges = this.preprocessSum(graph);5571var modes = !fadeEdges? ['node-property:alpha'] : ['node-property:alpha', 'edge-property:alpha'];5572viz.reposition();5573if(options.type != 'fade:con') {5574viz.fx.animate($.merge(options, {5575modes: ['linear'],5576onComplete: function() {5577viz.fx.animate($.merge(options, {5578modes: modes,5579onComplete: function() {5580options.onComplete();5581}5582}));5583}5584}));5585} else {5586viz.graph.eachNode(function(elem) {5587if (elem.id != root && elem.pos.isZero()) {5588elem.pos.set(elem.endPos);5589elem.startPos.set(elem.endPos);5590}5591});5592viz.fx.animate($.merge(options, {5593modes: ['linear'].concat(modes)5594}));5595}5596break;55975598default: this.doError();5599}5600},56015602/*5603Method: morph56045605This method will transform the current visualized graph into the new JSON representation passed in the method.5606The JSON object must at least have the root node in common with the current visualized graph.56075608Parameters:56095610json - (object) A json tree or graph structure. See also <Loader.loadJSON>.5611opt - (object) Animation options. It's an object with optional properties described below5612type - (string) Default's *nothing*. Type of the animation. Can be "nothing", "replot", "fade:con".5613duration - Described in <Options.Fx>.5614fps - Described in <Options.Fx>.5615transition - Described in <Options.Fx>.5616hideLabels - (boolean) Default's *true*. Hide labels during the animation.5617id - (string) The shared <Graph.Node> id between both graphs.56185619extraModes - (optional|object) When morphing with an animation, dollar prefixed data parameters are added to5620*endData* and not *data* itself. This way you can animate dollar prefixed parameters during your morphing operation.5621For animating these extra-parameters you have to specify an object that has animation groups as keys and animation5622properties as values, just like specified in <Graph.Plot.animate>.56235624Example:5625(start code js)5626//...json contains a tree or graph structure...56275628var viz = new $jit.Viz(options);5629viz.op.morph(json, {5630type: 'fade',5631duration: 1000,5632hideLabels: false,5633transition: $jit.Trans.Quart.easeOut5634});5635//or also5636viz.op.morph(json, {5637type: 'fade',5638duration: 15005639});5640//if the json data contains dollar prefixed params5641//like $width or $height these too can be animated5642viz.op.morph(json, {5643type: 'fade',5644duration: 15005645}, {5646'node-property': ['width', 'height']5647});5648(end code)56495650*/5651morph: function(json, opt, extraModes) {5652extraModes = extraModes || {};5653var viz = this.viz;5654var options = $.merge(this.options, viz.controller, opt), root = viz.root;5655var graph;5656//TODO(nico) this hack makes morphing work with the Hypertree.5657//Need to check if it has been solved and this can be removed.5658viz.root = opt.id || viz.root;5659switch(options.type) {5660case 'nothing':5661graph = viz.construct(json);5662graph.eachNode(function(elem) {5663var nodeExists = viz.graph.hasNode(elem.id);5664elem.eachAdjacency(function(adj) {5665var adjExists = !!viz.graph.getAdjacence(adj.nodeFrom.id, adj.nodeTo.id);5666viz.graph.addAdjacence(adj.nodeFrom, adj.nodeTo, adj.data);5667//Update data properties if the node existed5668if(adjExists) {5669var addedAdj = viz.graph.getAdjacence(adj.nodeFrom.id, adj.nodeTo.id);5670for(var prop in (adj.data || {})) {5671addedAdj.data[prop] = adj.data[prop];5672}5673}5674});5675//Update data properties if the node existed5676if(nodeExists) {5677var addedNode = viz.graph.getNode(elem.id);5678for(var prop in (elem.data || {})) {5679addedNode.data[prop] = elem.data[prop];5680}5681}5682});5683viz.graph.eachNode(function(elem) {5684elem.eachAdjacency(function(adj) {5685if(!graph.getAdjacence(adj.nodeFrom.id, adj.nodeTo.id)) {5686viz.graph.removeAdjacence(adj.nodeFrom.id, adj.nodeTo.id);5687}5688});5689if(!graph.hasNode(elem.id)) viz.graph.removeNode(elem.id);5690});56915692break;56935694case 'replot':5695viz.labels.clearLabels(true);5696this.morph(json, { type: 'nothing' });5697viz.refresh(true);5698viz.refresh(true);5699break;57005701case 'fade:seq': case 'fade': case 'fade:con':5702that = this;5703graph = viz.construct(json);5704//preprocessing for nodes to delete.5705//get node property modes to interpolate5706var nodeModes = ('node-property' in extraModes)5707&& $.map($.splat(extraModes['node-property']),5708function(n) { return '$' + n; });5709viz.graph.eachNode(function(elem) {5710var graphNode = graph.getNode(elem.id);5711if(!graphNode) {5712elem.setData('alpha', 1);5713elem.setData('alpha', 1, 'start');5714elem.setData('alpha', 0, 'end');5715elem.ignore = true;5716} else {5717//Update node data information5718var graphNodeData = graphNode.data;5719for(var prop in graphNodeData) {5720if(nodeModes && ($.indexOf(nodeModes, prop) > -1)) {5721elem.endData[prop] = graphNodeData[prop];5722} else {5723elem.data[prop] = graphNodeData[prop];5724}5725}5726}5727});5728viz.graph.eachNode(function(elem) {5729if(elem.ignore) return;5730elem.eachAdjacency(function(adj) {5731if(adj.nodeFrom.ignore || adj.nodeTo.ignore) return;5732var nodeFrom = graph.getNode(adj.nodeFrom.id);5733var nodeTo = graph.getNode(adj.nodeTo.id);5734if(!nodeFrom.adjacentTo(nodeTo)) {5735var adj = viz.graph.getAdjacence(nodeFrom.id, nodeTo.id);5736fadeEdges = true;5737adj.setData('alpha', 1);5738adj.setData('alpha', 1, 'start');5739adj.setData('alpha', 0, 'end');5740}5741});5742});5743//preprocessing for adding nodes.5744var fadeEdges = this.preprocessSum(graph);57455746var modes = !fadeEdges? ['node-property:alpha'] :5747['node-property:alpha',5748'edge-property:alpha'];5749//Append extra node-property animations (if any)5750modes[0] = modes[0] + (('node-property' in extraModes)?5751(':' + $.splat(extraModes['node-property']).join(':')) : '');5752//Append extra edge-property animations (if any)5753modes[1] = (modes[1] || 'edge-property:alpha') + (('edge-property' in extraModes)?5754(':' + $.splat(extraModes['edge-property']).join(':')) : '');5755//Add label-property animations (if any)5756if('label-property' in extraModes) {5757modes.push('label-property:' + $.splat(extraModes['label-property']).join(':'))5758}5759//only use reposition if its implemented.5760if (viz.reposition) {5761viz.reposition();5762} else {5763viz.compute('end');5764}5765viz.graph.eachNode(function(elem) {5766if (elem.id != root && elem.pos.getp().equals(Polar.KER)) {5767elem.pos.set(elem.endPos); elem.startPos.set(elem.endPos);5768}5769});5770viz.fx.animate($.merge(options, {5771modes: [extraModes.position || 'polar'].concat(modes),5772onComplete: function() {5773viz.graph.eachNode(function(elem) {5774if(elem.ignore) viz.graph.removeNode(elem.id);5775});5776viz.graph.eachNode(function(elem) {5777elem.eachAdjacency(function(adj) {5778if(adj.ignore) viz.graph.removeAdjacence(adj.nodeFrom.id, adj.nodeTo.id);5779});5780});5781options.onComplete();5782}5783}));5784break;57855786default:;5787}5788},578957905791/*5792Method: contract57935794Collapses the subtree of the given node. The node will have a _collapsed=true_ property.57955796Parameters:57975798node - (object) A <Graph.Node>.5799opt - (object) An object containing options described below5800type - (string) Whether to 'replot' or 'animate' the contraction.58015802There are also a number of Animation options. For more information see <Options.Fx>.58035804Example:5805(start code js)5806var viz = new $jit.Viz(options);5807viz.op.contract(node, {5808type: 'animate',5809duration: 1000,5810hideLabels: true,5811transition: $jit.Trans.Quart.easeOut5812});5813(end code)58145815*/5816contract: function(node, opt) {5817var viz = this.viz;5818if(node.collapsed || !node.anySubnode($.lambda(true))) return;5819opt = $.merge(this.options, viz.config, opt || {}, {5820'modes': ['node-property:alpha:span', 'linear']5821});5822node.collapsed = true;5823(function subn(n) {5824n.eachSubnode(function(ch) {5825ch.ignore = true;5826ch.setData('alpha', 0, opt.type == 'animate'? 'end' : 'current');5827subn(ch);5828});5829})(node);5830if(opt.type == 'animate') {5831viz.compute('end');5832if(viz.rotated) {5833viz.rotate(viz.rotated, 'none', {5834'property':'end'5835});5836}5837(function subn(n) {5838n.eachSubnode(function(ch) {5839ch.setPos(node.getPos('end'), 'end');5840subn(ch);5841});5842})(node);5843viz.fx.animate(opt);5844} else if(opt.type == 'replot'){5845viz.refresh();5846}5847},58485849/*5850Method: expand58515852Expands the previously contracted subtree. The given node must have the _collapsed=true_ property.58535854Parameters:58555856node - (object) A <Graph.Node>.5857opt - (object) An object containing options described below5858type - (string) Whether to 'replot' or 'animate'.58595860There are also a number of Animation options. For more information see <Options.Fx>.58615862Example:5863(start code js)5864var viz = new $jit.Viz(options);5865viz.op.expand(node, {5866type: 'animate',5867duration: 1000,5868hideLabels: true,5869transition: $jit.Trans.Quart.easeOut5870});5871(end code)58725873*/5874expand: function(node, opt) {5875if(!('collapsed' in node)) return;5876var viz = this.viz;5877opt = $.merge(this.options, viz.config, opt || {}, {5878'modes': ['node-property:alpha:span', 'linear']5879});5880delete node.collapsed;5881(function subn(n) {5882n.eachSubnode(function(ch) {5883delete ch.ignore;5884ch.setData('alpha', 1, opt.type == 'animate'? 'end' : 'current');5885subn(ch);5886});5887})(node);5888if(opt.type == 'animate') {5889viz.compute('end');5890if(viz.rotated) {5891viz.rotate(viz.rotated, 'none', {5892'property':'end'5893});5894}5895viz.fx.animate(opt);5896} else if(opt.type == 'replot'){5897viz.refresh();5898}5899},59005901preprocessSum: function(graph) {5902var viz = this.viz;5903graph.eachNode(function(elem) {5904if(!viz.graph.hasNode(elem.id)) {5905viz.graph.addNode(elem);5906var n = viz.graph.getNode(elem.id);5907n.setData('alpha', 0);5908n.setData('alpha', 0, 'start');5909n.setData('alpha', 1, 'end');5910}5911});5912var fadeEdges = false;5913graph.eachNode(function(elem) {5914elem.eachAdjacency(function(adj) {5915var nodeFrom = viz.graph.getNode(adj.nodeFrom.id);5916var nodeTo = viz.graph.getNode(adj.nodeTo.id);5917if(!nodeFrom.adjacentTo(nodeTo)) {5918var adj = viz.graph.addAdjacence(nodeFrom, nodeTo, adj.data);5919if(nodeFrom.startAlpha == nodeFrom.endAlpha5920&& nodeTo.startAlpha == nodeTo.endAlpha) {5921fadeEdges = true;5922adj.setData('alpha', 0);5923adj.setData('alpha', 0, 'start');5924adj.setData('alpha', 1, 'end');5925}5926}5927});5928});5929return fadeEdges;5930}5931};5932593359345935/*5936File: Helpers.js59375938Helpers are objects that contain rendering primitives (like rectangles, ellipses, etc), for plotting nodes and edges.5939Helpers also contain implementations of the *contains* method, a method returning a boolean indicating whether the mouse5940position is over the rendered shape.59415942Helpers are very useful when implementing new NodeTypes, since you can access them through *this.nodeHelper* and5943*this.edgeHelper* <Graph.Plot> properties, providing you with simple primitives and mouse-position check functions.59445945Example:5946(start code js)5947//implement a new node type5948$jit.Viz.Plot.NodeTypes.implement({5949'customNodeType': {5950'render': function(node, canvas) {5951this.nodeHelper.circle.render ...5952},5953'contains': function(node, pos) {5954this.nodeHelper.circle.contains ...5955}5956}5957});5958//implement an edge type5959$jit.Viz.Plot.EdgeTypes.implement({5960'customNodeType': {5961'render': function(node, canvas) {5962this.edgeHelper.circle.render ...5963},5964//optional5965'contains': function(node, pos) {5966this.edgeHelper.circle.contains ...5967}5968}5969});5970(end code)59715972*/59735974/*5975Object: NodeHelper59765977Contains rendering and other type of primitives for simple shapes.5978*/5979var NodeHelper = {5980'none': {5981'render': $.empty,5982'contains': $.lambda(false)5983},5984/*5985Object: NodeHelper.circle5986*/5987'circle': {5988/*5989Method: render59905991Renders a circle into the canvas.59925993Parameters:59945995type - (string) Possible options are 'fill' or 'stroke'.5996pos - (object) An *x*, *y* object with the position of the center of the circle.5997radius - (number) The radius of the circle to be rendered.5998canvas - (object) A <Canvas> instance.59996000Example:6001(start code js)6002NodeHelper.circle.render('fill', { x: 10, y: 30 }, 30, viz.canvas);6003(end code)6004*/6005'render': function(type, pos, radius, canvas){6006var ctx = canvas.getCtx();6007ctx.beginPath();6008ctx.arc(pos.x, pos.y, radius, 0, Math.PI * 2, true);6009ctx.closePath();6010ctx[type]();6011},6012/*6013Method: contains60146015Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.60166017Parameters:60186019npos - (object) An *x*, *y* object with the <Graph.Node> position.6020pos - (object) An *x*, *y* object with the position to check.6021radius - (number) The radius of the rendered circle.60226023Example:6024(start code js)6025NodeHelper.circle.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, 30); //true6026(end code)6027*/6028'contains': function(npos, pos, radius){6029var diffx = npos.x - pos.x,6030diffy = npos.y - pos.y,6031diff = diffx * diffx + diffy * diffy;6032return diff <= radius * radius;6033}6034},6035/*6036Object: NodeHelper.ellipse6037*/6038'ellipse': {6039/*6040Method: render60416042Renders an ellipse into the canvas.60436044Parameters:60456046type - (string) Possible options are 'fill' or 'stroke'.6047pos - (object) An *x*, *y* object with the position of the center of the ellipse.6048width - (number) The width of the ellipse.6049height - (number) The height of the ellipse.6050canvas - (object) A <Canvas> instance.60516052Example:6053(start code js)6054NodeHelper.ellipse.render('fill', { x: 10, y: 30 }, 30, 40, viz.canvas);6055(end code)6056*/6057'render': function(type, pos, width, height, canvas){6058var ctx = canvas.getCtx(),6059scalex = 1,6060scaley = 1,6061scaleposx = 1,6062scaleposy = 1,6063radius = 0;60646065if (width > height) {6066radius = width / 2;6067scaley = height / width;6068scaleposy = width / height;6069} else {6070radius = height / 2;6071scalex = width / height;6072scaleposx = height / width;6073}60746075ctx.save();6076ctx.scale(scalex, scaley);6077ctx.beginPath();6078ctx.arc(pos.x * scaleposx, pos.y * scaleposy, radius, 0, Math.PI * 2, true);6079ctx.closePath();6080ctx[type]();6081ctx.restore();6082},6083/*6084Method: contains60856086Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.60876088Parameters:60896090npos - (object) An *x*, *y* object with the <Graph.Node> position.6091pos - (object) An *x*, *y* object with the position to check.6092width - (number) The width of the rendered ellipse.6093height - (number) The height of the rendered ellipse.60946095Example:6096(start code js)6097NodeHelper.ellipse.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, 30, 40);6098(end code)6099*/6100'contains': function(npos, pos, width, height){6101var radius = 0,6102scalex = 1,6103scaley = 1,6104diffx = 0,6105diffy = 0,6106diff = 0;61076108if (width > height) {6109radius = width / 2;6110scaley = height / width;6111} else {6112radius = height / 2;6113scalex = width / height;6114}61156116diffx = (npos.x - pos.x) * (1 / scalex);6117diffy = (npos.y - pos.y) * (1 / scaley);6118diff = diffx * diffx + diffy * diffy;6119return diff <= radius * radius;6120}6121},6122/*6123Object: NodeHelper.square6124*/6125'square': {6126/*6127Method: render61286129Renders a square into the canvas.61306131Parameters:61326133type - (string) Possible options are 'fill' or 'stroke'.6134pos - (object) An *x*, *y* object with the position of the center of the square.6135dim - (number) The radius (or half-diameter) of the square.6136canvas - (object) A <Canvas> instance.61376138Example:6139(start code js)6140NodeHelper.square.render('stroke', { x: 10, y: 30 }, 40, viz.canvas);6141(end code)6142*/6143'render': function(type, pos, dim, canvas){6144canvas.getCtx()[type + "Rect"](pos.x - dim, pos.y - dim, 2*dim, 2*dim);6145},6146/*6147Method: contains61486149Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.61506151Parameters:61526153npos - (object) An *x*, *y* object with the <Graph.Node> position.6154pos - (object) An *x*, *y* object with the position to check.6155dim - (number) The radius (or half-diameter) of the square.61566157Example:6158(start code js)6159NodeHelper.square.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, 30);6160(end code)6161*/6162'contains': function(npos, pos, dim){6163return Math.abs(pos.x - npos.x) <= dim && Math.abs(pos.y - npos.y) <= dim;6164}6165},6166/*6167Object: NodeHelper.rectangle6168*/6169'rectangle': {6170/*6171Method: render61726173Renders a rectangle into the canvas.61746175Parameters:61766177type - (string) Possible options are 'fill' or 'stroke'.6178pos - (object) An *x*, *y* object with the position of the center of the rectangle.6179width - (number) The width of the rectangle.6180height - (number) The height of the rectangle.6181canvas - (object) A <Canvas> instance.61826183Example:6184(start code js)6185NodeHelper.rectangle.render('fill', { x: 10, y: 30 }, 30, 40, viz.canvas);6186(end code)6187*/6188'render': function(type, pos, width, height, canvas){6189canvas.getCtx()[type + "Rect"](pos.x - width / 2, pos.y - height / 2,6190width, height);6191},6192/*6193Method: contains61946195Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.61966197Parameters:61986199npos - (object) An *x*, *y* object with the <Graph.Node> position.6200pos - (object) An *x*, *y* object with the position to check.6201width - (number) The width of the rendered rectangle.6202height - (number) The height of the rendered rectangle.62036204Example:6205(start code js)6206NodeHelper.rectangle.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, 30, 40);6207(end code)6208*/6209'contains': function(npos, pos, width, height){6210return Math.abs(pos.x - npos.x) <= width / 26211&& Math.abs(pos.y - npos.y) <= height / 2;6212}6213},6214/*6215Object: NodeHelper.triangle6216*/6217'triangle': {6218/*6219Method: render62206221Renders a triangle into the canvas.62226223Parameters:62246225type - (string) Possible options are 'fill' or 'stroke'.6226pos - (object) An *x*, *y* object with the position of the center of the triangle.6227dim - (number) Half the base and half the height of the triangle.6228canvas - (object) A <Canvas> instance.62296230Example:6231(start code js)6232NodeHelper.triangle.render('stroke', { x: 10, y: 30 }, 40, viz.canvas);6233(end code)6234*/6235'render': function(type, pos, dim, canvas){6236var ctx = canvas.getCtx(),6237c1x = pos.x,6238c1y = pos.y - dim,6239c2x = c1x - dim,6240c2y = pos.y + dim,6241c3x = c1x + dim,6242c3y = c2y;6243ctx.beginPath();6244ctx.moveTo(c1x, c1y);6245ctx.lineTo(c2x, c2y);6246ctx.lineTo(c3x, c3y);6247ctx.closePath();6248ctx[type]();6249},6250/*6251Method: contains62526253Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.62546255Parameters:62566257npos - (object) An *x*, *y* object with the <Graph.Node> position.6258pos - (object) An *x*, *y* object with the position to check.6259dim - (number) Half the base and half the height of the triangle.62606261Example:6262(start code js)6263NodeHelper.triangle.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, 30);6264(end code)6265*/6266'contains': function(npos, pos, dim) {6267return NodeHelper.circle.contains(npos, pos, dim);6268}6269},6270/*6271Object: NodeHelper.star6272*/6273'star': {6274/*6275Method: render62766277Renders a star (concave decagon) into the canvas.62786279Parameters:62806281type - (string) Possible options are 'fill' or 'stroke'.6282pos - (object) An *x*, *y* object with the position of the center of the star.6283dim - (number) The length of a side of a concave decagon.6284canvas - (object) A <Canvas> instance.62856286Example:6287(start code js)6288NodeHelper.star.render('stroke', { x: 10, y: 30 }, 40, viz.canvas);6289(end code)6290*/6291'render': function(type, pos, dim, canvas){6292var ctx = canvas.getCtx(),6293pi5 = Math.PI / 5;6294ctx.save();6295ctx.translate(pos.x, pos.y);6296ctx.beginPath();6297ctx.moveTo(dim, 0);6298for (var i = 0; i < 9; i++) {6299ctx.rotate(pi5);6300if (i % 2 == 0) {6301ctx.lineTo((dim / 0.525731) * 0.200811, 0);6302} else {6303ctx.lineTo(dim, 0);6304}6305}6306ctx.closePath();6307ctx[type]();6308ctx.restore();6309},6310/*6311Method: contains63126313Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.63146315Parameters:63166317npos - (object) An *x*, *y* object with the <Graph.Node> position.6318pos - (object) An *x*, *y* object with the position to check.6319dim - (number) The length of a side of a concave decagon.63206321Example:6322(start code js)6323NodeHelper.star.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, 30);6324(end code)6325*/6326'contains': function(npos, pos, dim) {6327return NodeHelper.circle.contains(npos, pos, dim);6328}6329}6330};63316332/*6333Object: EdgeHelper63346335Contains rendering primitives for simple edge shapes.6336*/6337var EdgeHelper = {6338/*6339Object: EdgeHelper.line6340*/6341'line': {6342/*6343Method: render63446345Renders a line into the canvas.63466347Parameters:63486349from - (object) An *x*, *y* object with the starting position of the line.6350to - (object) An *x*, *y* object with the ending position of the line.6351canvas - (object) A <Canvas> instance.63526353Example:6354(start code js)6355EdgeHelper.line.render({ x: 10, y: 30 }, { x: 10, y: 50 }, viz.canvas);6356(end code)6357*/6358'render': function(from, to, canvas){6359var ctx = canvas.getCtx();6360ctx.beginPath();6361ctx.moveTo(from.x, from.y);6362ctx.lineTo(to.x, to.y);6363ctx.stroke();6364},6365/*6366Method: contains63676368Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.63696370Parameters:63716372posFrom - (object) An *x*, *y* object with a <Graph.Node> position.6373posTo - (object) An *x*, *y* object with a <Graph.Node> position.6374pos - (object) An *x*, *y* object with the position to check.6375epsilon - (number) The dimension of the shape.63766377Example:6378(start code js)6379EdgeHelper.line.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, { x: 15, y: 35 }, 30);6380(end code)6381*/6382'contains': function(posFrom, posTo, pos, epsilon) {6383var min = Math.min,6384max = Math.max,6385minPosX = min(posFrom.x, posTo.x),6386maxPosX = max(posFrom.x, posTo.x),6387minPosY = min(posFrom.y, posTo.y),6388maxPosY = max(posFrom.y, posTo.y);63896390if(pos.x >= minPosX && pos.x <= maxPosX6391&& pos.y >= minPosY && pos.y <= maxPosY) {6392if(Math.abs(posTo.x - posFrom.x) <= epsilon) {6393return true;6394}6395var dist = (posTo.y - posFrom.y) / (posTo.x - posFrom.x) * (pos.x - posFrom.x) + posFrom.y;6396return Math.abs(dist - pos.y) <= epsilon;6397}6398return false;6399}6400},6401/*6402Object: EdgeHelper.arrow6403*/6404'arrow': {6405/*6406Method: render64076408Renders an arrow into the canvas.64096410Parameters:64116412from - (object) An *x*, *y* object with the starting position of the arrow.6413to - (object) An *x*, *y* object with the ending position of the arrow.6414dim - (number) The dimension of the arrow.6415swap - (boolean) Whether to set the arrow pointing to the starting position or the ending position.6416canvas - (object) A <Canvas> instance.64176418Example:6419(start code js)6420EdgeHelper.arrow.render({ x: 10, y: 30 }, { x: 10, y: 50 }, 13, false, viz.canvas);6421(end code)6422*/6423'render': function(from, to, dim, swap, canvas){6424var ctx = canvas.getCtx();6425// invert edge direction6426if (swap) {6427var tmp = from;6428from = to;6429to = tmp;6430}6431var vect = new Complex(to.x - from.x, to.y - from.y);6432vect.$scale(dim / vect.norm());6433var intermediatePoint = new Complex(to.x - vect.x, to.y - vect.y),6434normal = new Complex(-vect.y / 2, vect.x / 2),6435v1 = intermediatePoint.add(normal),6436v2 = intermediatePoint.$add(normal.$scale(-1));64376438ctx.beginPath();6439ctx.moveTo(from.x, from.y);6440ctx.lineTo(to.x, to.y);6441ctx.stroke();6442ctx.beginPath();6443ctx.moveTo(v1.x, v1.y);6444ctx.lineTo(v2.x, v2.y);6445ctx.lineTo(to.x, to.y);6446ctx.closePath();6447ctx.fill();6448},6449/*6450Method: contains64516452Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.64536454Parameters:64556456posFrom - (object) An *x*, *y* object with a <Graph.Node> position.6457posTo - (object) An *x*, *y* object with a <Graph.Node> position.6458pos - (object) An *x*, *y* object with the position to check.6459epsilon - (number) The dimension of the shape.64606461Example:6462(start code js)6463EdgeHelper.arrow.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, { x: 15, y: 35 }, 30);6464(end code)6465*/6466'contains': function(posFrom, posTo, pos, epsilon) {6467return EdgeHelper.line.contains(posFrom, posTo, pos, epsilon);6468}6469},6470/*6471Object: EdgeHelper.hyperline6472*/6473'hyperline': {6474/*6475Method: render64766477Renders a hyperline into the canvas. A hyperline are the lines drawn for the <Hypertree> visualization.64786479Parameters:64806481from - (object) An *x*, *y* object with the starting position of the hyperline. *x* and *y* must belong to [0, 1).6482to - (object) An *x*, *y* object with the ending position of the hyperline. *x* and *y* must belong to [0, 1).6483r - (number) The scaling factor.6484canvas - (object) A <Canvas> instance.64856486Example:6487(start code js)6488EdgeHelper.hyperline.render({ x: 10, y: 30 }, { x: 10, y: 50 }, 100, viz.canvas);6489(end code)6490*/6491'render': function(from, to, r, canvas){6492var ctx = canvas.getCtx();6493var centerOfCircle = computeArcThroughTwoPoints(from, to);6494if (centerOfCircle.a > 1000 || centerOfCircle.b > 10006495|| centerOfCircle.ratio < 0) {6496ctx.beginPath();6497ctx.moveTo(from.x * r, from.y * r);6498ctx.lineTo(to.x * r, to.y * r);6499ctx.stroke();6500} else {6501var angleBegin = Math.atan2(to.y - centerOfCircle.y, to.x6502- centerOfCircle.x);6503var angleEnd = Math.atan2(from.y - centerOfCircle.y, from.x6504- centerOfCircle.x);6505var sense = sense(angleBegin, angleEnd);6506ctx.beginPath();6507ctx.arc(centerOfCircle.x * r, centerOfCircle.y * r, centerOfCircle.ratio6508* r, angleBegin, angleEnd, sense);6509ctx.stroke();6510}6511/*6512Calculates the arc parameters through two points.65136514More information in <http://en.wikipedia.org/wiki/Poincar%C3%A9_disc_model#Analytic_geometry_constructions_in_the_hyperbolic_plane>65156516Parameters:65176518p1 - A <Complex> instance.6519p2 - A <Complex> instance.6520scale - The Disk's diameter.65216522Returns:65236524An object containing some arc properties.6525*/6526function computeArcThroughTwoPoints(p1, p2){6527var aDen = (p1.x * p2.y - p1.y * p2.x), bDen = aDen;6528var sq1 = p1.squaredNorm(), sq2 = p2.squaredNorm();6529// Fall back to a straight line6530if (aDen == 0)6531return {6532x: 0,6533y: 0,6534ratio: -16535};65366537var a = (p1.y * sq2 - p2.y * sq1 + p1.y - p2.y) / aDen;6538var b = (p2.x * sq1 - p1.x * sq2 + p2.x - p1.x) / bDen;6539var x = -a / 2;6540var y = -b / 2;6541var squaredRatio = (a * a + b * b) / 4 - 1;6542// Fall back to a straight line6543if (squaredRatio < 0)6544return {6545x: 0,6546y: 0,6547ratio: -16548};6549var ratio = Math.sqrt(squaredRatio);6550var out = {6551x: x,6552y: y,6553ratio: ratio > 1000? -1 : ratio,6554a: a,6555b: b6556};65576558return out;6559}6560/*6561Sets angle direction to clockwise (true) or counterclockwise (false).65626563Parameters:65646565angleBegin - Starting angle for drawing the arc.6566angleEnd - The HyperLine will be drawn from angleBegin to angleEnd.65676568Returns:65696570A Boolean instance describing the sense for drawing the HyperLine.6571*/6572function sense(angleBegin, angleEnd){6573return (angleBegin < angleEnd)? ((angleBegin + Math.PI > angleEnd)? false6574: true) : ((angleEnd + Math.PI > angleBegin)? true : false);6575}6576},6577/*6578Method: contains65796580Not Implemented65816582Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.65836584Parameters:65856586posFrom - (object) An *x*, *y* object with a <Graph.Node> position.6587posTo - (object) An *x*, *y* object with a <Graph.Node> position.6588pos - (object) An *x*, *y* object with the position to check.6589epsilon - (number) The dimension of the shape.65906591Example:6592(start code js)6593EdgeHelper.hyperline.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, { x: 15, y: 35 }, 30);6594(end code)6595*/6596'contains': $.lambda(false)6597}6598};659966006601/*6602* File: Graph.Plot.js6603*/66046605/*6606Object: Graph.Plot66076608<Graph> rendering and animation methods.66096610Properties:66116612nodeHelper - <NodeHelper> object.6613edgeHelper - <EdgeHelper> object.6614*/6615Graph.Plot = {6616//Default initializer6617initialize: function(viz, klass){6618this.viz = viz;6619this.config = viz.config;6620this.node = viz.config.Node;6621this.edge = viz.config.Edge;6622this.animation = new Animation;6623this.nodeTypes = new klass.Plot.NodeTypes;6624this.edgeTypes = new klass.Plot.EdgeTypes;6625this.labels = viz.labels;6626},66276628//Add helpers6629nodeHelper: NodeHelper,6630edgeHelper: EdgeHelper,66316632Interpolator: {6633//node/edge property parsers6634'map': {6635'border': 'color',6636'color': 'color',6637'width': 'number',6638'height': 'number',6639'dim': 'number',6640'alpha': 'number',6641'lineWidth': 'number',6642'angularWidth':'number',6643'span':'number',6644'valueArray':'array-number',6645'dimArray':'array-number'6646//'colorArray':'array-color'6647},66486649//canvas specific parsers6650'canvas': {6651'globalAlpha': 'number',6652'fillStyle': 'color',6653'strokeStyle': 'color',6654'lineWidth': 'number',6655'shadowBlur': 'number',6656'shadowColor': 'color',6657'shadowOffsetX': 'number',6658'shadowOffsetY': 'number',6659'miterLimit': 'number'6660},66616662//label parsers6663'label': {6664'size': 'number',6665'color': 'color'6666},66676668//Number interpolator6669'compute': function(from, to, delta) {6670return from + (to - from) * delta;6671},66726673//Position interpolators6674'moebius': function(elem, props, delta, vector) {6675var v = vector.scale(-delta);6676if(v.norm() < 1) {6677var x = v.x, y = v.y;6678var ans = elem.startPos6679.getc().moebiusTransformation(v);6680elem.pos.setc(ans.x, ans.y);6681v.x = x; v.y = y;6682}6683},66846685'linear': function(elem, props, delta) {6686var from = elem.startPos.getc(true);6687var to = elem.endPos.getc(true);6688elem.pos.setc(this.compute(from.x, to.x, delta),6689this.compute(from.y, to.y, delta));6690},66916692'polar': function(elem, props, delta) {6693var from = elem.startPos.getp(true);6694var to = elem.endPos.getp();6695var ans = to.interpolate(from, delta);6696elem.pos.setp(ans.theta, ans.rho);6697},66986699//Graph's Node/Edge interpolators6700'number': function(elem, prop, delta, getter, setter) {6701var from = elem[getter](prop, 'start');6702var to = elem[getter](prop, 'end');6703elem[setter](prop, this.compute(from, to, delta));6704},67056706'color': function(elem, prop, delta, getter, setter) {6707var from = $.hexToRgb(elem[getter](prop, 'start'));6708var to = $.hexToRgb(elem[getter](prop, 'end'));6709var comp = this.compute;6710var val = $.rgbToHex([parseInt(comp(from[0], to[0], delta)),6711parseInt(comp(from[1], to[1], delta)),6712parseInt(comp(from[2], to[2], delta))]);67136714elem[setter](prop, val);6715},67166717'array-number': function(elem, prop, delta, getter, setter) {6718var from = elem[getter](prop, 'start'),6719to = elem[getter](prop, 'end'),6720cur = [];6721for(var i=0, l=from.length; i<l; i++) {6722var fromi = from[i], toi = to[i];6723if(fromi.length) {6724for(var j=0, len=fromi.length, curi=[]; j<len; j++) {6725curi.push(this.compute(fromi[j], toi[j], delta));6726}6727cur.push(curi);6728} else {6729cur.push(this.compute(fromi, toi, delta));6730}6731}6732elem[setter](prop, cur);6733},67346735'node': function(elem, props, delta, map, getter, setter) {6736map = this[map];6737if(props) {6738var len = props.length;6739for(var i=0; i<len; i++) {6740var pi = props[i];6741this[map[pi]](elem, pi, delta, getter, setter);6742}6743} else {6744for(var pi in map) {6745this[map[pi]](elem, pi, delta, getter, setter);6746}6747}6748},67496750'edge': function(elem, props, delta, mapKey, getter, setter) {6751var adjs = elem.adjacencies;6752for(var id in adjs) this['node'](adjs[id], props, delta, mapKey, getter, setter);6753},67546755'node-property': function(elem, props, delta) {6756this['node'](elem, props, delta, 'map', 'getData', 'setData');6757},67586759'edge-property': function(elem, props, delta) {6760this['edge'](elem, props, delta, 'map', 'getData', 'setData');6761},67626763'label-property': function(elem, props, delta) {6764this['node'](elem, props, delta, 'label', 'getLabelData', 'setLabelData');6765},67666767'node-style': function(elem, props, delta) {6768this['node'](elem, props, delta, 'canvas', 'getCanvasStyle', 'setCanvasStyle');6769},67706771'edge-style': function(elem, props, delta) {6772this['edge'](elem, props, delta, 'canvas', 'getCanvasStyle', 'setCanvasStyle');6773}6774},677567766777/*6778sequence67796780Iteratively performs an action while refreshing the state of the visualization.67816782Parameters:67836784options - (object) An object containing some sequence options described below6785condition - (function) A function returning a boolean instance in order to stop iterations.6786step - (function) A function to execute on each step of the iteration.6787onComplete - (function) A function to execute when the sequence finishes.6788duration - (number) Duration (in milliseconds) of each step.67896790Example:6791(start code js)6792var rg = new $jit.RGraph(options);6793var i = 0;6794rg.fx.sequence({6795condition: function() {6796return i == 10;6797},6798step: function() {6799alert(i++);6800},6801onComplete: function() {6802alert('done!');6803}6804});6805(end code)68066807*/6808sequence: function(options) {6809var that = this;6810options = $.merge({6811condition: $.lambda(false),6812step: $.empty,6813onComplete: $.empty,6814duration: 2006815}, options || {});68166817var interval = setInterval(function() {6818if(options.condition()) {6819options.step();6820} else {6821clearInterval(interval);6822options.onComplete();6823}6824that.viz.refresh(true);6825}, options.duration);6826},68276828/*6829prepare68306831Prepare graph position and other attribute values before performing an Animation.6832This method is used internally by the Toolkit.68336834See also:68356836<Animation>, <Graph.Plot.animate>68376838*/6839prepare: function(modes) {6840var graph = this.viz.graph,6841accessors = {6842'node-property': {6843'getter': 'getData',6844'setter': 'setData'6845},6846'edge-property': {6847'getter': 'getData',6848'setter': 'setData'6849},6850'node-style': {6851'getter': 'getCanvasStyle',6852'setter': 'setCanvasStyle'6853},6854'edge-style': {6855'getter': 'getCanvasStyle',6856'setter': 'setCanvasStyle'6857}6858};68596860//parse modes6861var m = {};6862if($.type(modes) == 'array') {6863for(var i=0, len=modes.length; i < len; i++) {6864var elems = modes[i].split(':');6865m[elems.shift()] = elems;6866}6867} else {6868for(var p in modes) {6869if(p == 'position') {6870m[modes.position] = [];6871} else {6872m[p] = $.splat(modes[p]);6873}6874}6875}68766877graph.eachNode(function(node) {6878node.startPos.set(node.pos);6879$.each(['node-property', 'node-style'], function(p) {6880if(p in m) {6881var prop = m[p];6882for(var i=0, l=prop.length; i < l; i++) {6883node[accessors[p].setter](prop[i], node[accessors[p].getter](prop[i]), 'start');6884}6885}6886});6887$.each(['edge-property', 'edge-style'], function(p) {6888if(p in m) {6889var prop = m[p];6890node.eachAdjacency(function(adj) {6891for(var i=0, l=prop.length; i < l; i++) {6892adj[accessors[p].setter](prop[i], adj[accessors[p].getter](prop[i]), 'start');6893}6894});6895}6896});6897});6898return m;6899},69006901/*6902Method: animate69036904Animates a <Graph> by interpolating some <Graph.Node>, <Graph.Adjacence> or <Graph.Label> properties.69056906Parameters:69076908opt - (object) Animation options. The object properties are described below6909duration - (optional) Described in <Options.Fx>.6910fps - (optional) Described in <Options.Fx>.6911hideLabels - (optional|boolean) Whether to hide labels during the animation.6912modes - (required|object) An object with animation modes (described below).69136914Animation modes:69156916Animation modes are strings representing different node/edge and graph properties that you'd like to animate.6917They are represented by an object that has as keys main categories of properties to animate and as values a list6918of these specific properties. The properties are described below69196920position - Describes the way nodes' positions must be interpolated. Possible values are 'linear', 'polar' or 'moebius'.6921node-property - Describes which Node properties will be interpolated. These properties can be any of the ones defined in <Options.Node>.6922edge-property - Describes which Edge properties will be interpolated. These properties can be any the ones defined in <Options.Edge>.6923label-property - Describes which Label properties will be interpolated. These properties can be any of the ones defined in <Options.Label> like color or size.6924node-style - Describes which Node Canvas Styles will be interpolated. These are specific canvas properties like fillStyle, strokeStyle, lineWidth, shadowBlur, shadowColor, shadowOffsetX, shadowOffsetY, etc.6925edge-style - Describes which Edge Canvas Styles will be interpolated. These are specific canvas properties like fillStyle, strokeStyle, lineWidth, shadowBlur, shadowColor, shadowOffsetX, shadowOffsetY, etc.69266927Example:6928(start code js)6929var viz = new $jit.Viz(options);6930//...tweak some Data, CanvasStyles or LabelData properties...6931viz.fx.animate({6932modes: {6933'position': 'linear',6934'node-property': ['width', 'height'],6935'node-style': 'shadowColor',6936'label-property': 'size'6937},6938hideLabels: false6939});6940//...can also be written like this...6941viz.fx.animate({6942modes: ['linear',6943'node-property:width:height',6944'node-style:shadowColor',6945'label-property:size'],6946hideLabels: false6947});6948(end code)6949*/6950animate: function(opt, versor) {6951opt = $.merge(this.viz.config, opt || {});6952var that = this,6953viz = this.viz,6954graph = viz.graph,6955interp = this.Interpolator,6956animation = opt.type === 'nodefx'? this.nodeFxAnimation : this.animation;6957//prepare graph values6958var m = this.prepare(opt.modes);69596960//animate6961if(opt.hideLabels) this.labels.hideLabels(true);6962animation.setOptions($.extend(opt, {6963$animating: false,6964compute: function(delta) {6965graph.eachNode(function(node) {6966for(var p in m) {6967interp[p](node, m[p], delta, versor);6968}6969});6970that.plot(opt, this.$animating, delta);6971this.$animating = true;6972},6973complete: function() {6974if(opt.hideLabels) that.labels.hideLabels(false);6975that.plot(opt);6976opt.onComplete();6977//This shouldn't be here!6978//opt.onAfterCompute();6979}6980})).start();6981},69826983/*6984nodeFx69856986Apply animation to node properties like color, width, height, dim, etc.69876988Parameters:69896990options - Animation options. This object properties is described below6991elements - The Elements to be transformed. This is an object that has a properties69926993(start code js)6994'elements': {6995//can also be an array of ids6996'id': 'id-of-node-to-transform',6997//properties to be modified. All properties are optional.6998'properties': {6999'color': '#ccc', //some color7000'width': 10, //some width7001'height': 10, //some height7002'dim': 20, //some dim7003'lineWidth': 10 //some line width7004}7005}7006(end code)70077008- _reposition_ Whether to recalculate positions and add a motion animation.7009This might be used when changing _width_ or _height_ properties in a <Layouts.Tree> like layout. Default's *false*.70107011- _onComplete_ A method that is called when the animation completes.70127013...and all other <Graph.Plot.animate> options like _duration_, _fps_, _transition_, etc.70147015Example:7016(start code js)7017var rg = new RGraph(canvas, config); //can be also Hypertree or ST7018rg.fx.nodeFx({7019'elements': {7020'id':'mynodeid',7021'properties': {7022'color':'#ccf'7023},7024'transition': Trans.Quart.easeOut7025}7026});7027(end code)7028*/7029nodeFx: function(opt) {7030var viz = this.viz,7031graph = viz.graph,7032animation = this.nodeFxAnimation,7033options = $.merge(this.viz.config, {7034'elements': {7035'id': false,7036'properties': {}7037},7038'reposition': false7039});7040opt = $.merge(options, opt || {}, {7041onBeforeCompute: $.empty,7042onAfterCompute: $.empty7043});7044//check if an animation is running7045animation.stopTimer();7046var props = opt.elements.properties;7047//set end values for nodes7048if(!opt.elements.id) {7049graph.eachNode(function(n) {7050for(var prop in props) {7051n.setData(prop, props[prop], 'end');7052}7053});7054} else {7055var ids = $.splat(opt.elements.id);7056$.each(ids, function(id) {7057var n = graph.getNode(id);7058if(n) {7059for(var prop in props) {7060n.setData(prop, props[prop], 'end');7061}7062}7063});7064}7065//get keys7066var propnames = [];7067for(var prop in props) propnames.push(prop);7068//add node properties modes7069var modes = ['node-property:' + propnames.join(':')];7070//set new node positions7071if(opt.reposition) {7072modes.push('linear');7073viz.compute('end');7074}7075//animate7076this.animate($.merge(opt, {7077modes: modes,7078type: 'nodefx'7079}));7080},708170827083/*7084Method: plot70857086Plots a <Graph>.70877088Parameters:70897090opt - (optional) Plotting options. Most of them are described in <Options.Fx>.70917092Example:70937094(start code js)7095var viz = new $jit.Viz(options);7096viz.fx.plot();7097(end code)70987099*/7100plot: function(opt, animating) {7101var viz = this.viz,7102aGraph = viz.graph,7103canvas = viz.canvas,7104id = viz.root,7105that = this,7106ctx = canvas.getCtx(),7107min = Math.min,7108opt = opt || this.viz.controller;71097110opt.clearCanvas && canvas.clear();71117112var root = aGraph.getNode(id);7113if(!root) return;71147115var T = !!root.visited;7116aGraph.eachNode(function(node) {7117var nodeAlpha = node.getData('alpha');7118node.eachAdjacency(function(adj) {7119var nodeTo = adj.nodeTo;7120if(!!nodeTo.visited === T && node.drawn && nodeTo.drawn) {7121!animating && opt.onBeforePlotLine(adj);7122that.plotLine(adj, canvas, animating);7123!animating && opt.onAfterPlotLine(adj);7124}7125});7126if(node.drawn) {7127!animating && opt.onBeforePlotNode(node);7128that.plotNode(node, canvas, animating);7129!animating && opt.onAfterPlotNode(node);7130}7131if(!that.labelsHidden && opt.withLabels) {7132if(node.drawn && nodeAlpha >= 0.95) {7133that.labels.plotLabel(canvas, node, opt);7134} else {7135that.labels.hideLabel(node, false);7136}7137}7138node.visited = !T;7139});7140},71417142/*7143Plots a Subtree.7144*/7145plotTree: function(node, opt, animating) {7146var that = this,7147viz = this.viz,7148canvas = viz.canvas,7149config = this.config,7150ctx = canvas.getCtx();7151var nodeAlpha = node.getData('alpha');7152node.eachSubnode(function(elem) {7153if(opt.plotSubtree(node, elem) && elem.exist && elem.drawn) {7154var adj = node.getAdjacency(elem.id);7155!animating && opt.onBeforePlotLine(adj);7156that.plotLine(adj, canvas, animating);7157!animating && opt.onAfterPlotLine(adj);7158that.plotTree(elem, opt, animating);7159}7160});7161if(node.drawn) {7162!animating && opt.onBeforePlotNode(node);7163this.plotNode(node, canvas, animating);7164!animating && opt.onAfterPlotNode(node);7165if(!opt.hideLabels && opt.withLabels && nodeAlpha >= 0.95)7166this.labels.plotLabel(canvas, node, opt);7167else7168this.labels.hideLabel(node, false);7169} else {7170this.labels.hideLabel(node, true);7171}7172},71737174/*7175Method: plotNode71767177Plots a <Graph.Node>.71787179Parameters:71807181node - (object) A <Graph.Node>.7182canvas - (object) A <Canvas> element.71837184*/7185plotNode: function(node, canvas, animating) {7186var f = node.getData('type'),7187ctxObj = this.node.CanvasStyles;7188if(f != 'none') {7189var width = node.getData('lineWidth'),7190color = node.getData('color'),7191alpha = node.getData('alpha'),7192ctx = canvas.getCtx();7193ctx.save();7194ctx.lineWidth = width;7195ctx.fillStyle = ctx.strokeStyle = color;7196ctx.globalAlpha = alpha;71977198for(var s in ctxObj) {7199ctx[s] = node.getCanvasStyle(s);7200}72017202this.nodeTypes[f].render.call(this, node, canvas, animating);7203ctx.restore();7204}7205},72067207/*7208Method: plotLine72097210Plots a <Graph.Adjacence>.72117212Parameters:72137214adj - (object) A <Graph.Adjacence>.7215canvas - (object) A <Canvas> instance.72167217*/7218plotLine: function(adj, canvas, animating) {7219var f = adj.getData('type'),7220ctxObj = this.edge.CanvasStyles;7221if(f != 'none') {7222var width = adj.getData('lineWidth'),7223color = adj.getData('color'),7224ctx = canvas.getCtx(),7225nodeFrom = adj.nodeFrom,7226nodeTo = adj.nodeTo;72277228ctx.save();7229ctx.lineWidth = width;7230ctx.fillStyle = ctx.strokeStyle = color;7231ctx.globalAlpha = Math.min(nodeFrom.getData('alpha'),7232nodeTo.getData('alpha'),7233adj.getData('alpha'));72347235for(var s in ctxObj) {7236ctx[s] = adj.getCanvasStyle(s);7237}72387239this.edgeTypes[f].render.call(this, adj, canvas, animating);7240ctx.restore();7241}7242}72437244};72457246/*7247Object: Graph.Plot3D72487249<Graph> 3D rendering and animation methods.72507251Properties:72527253nodeHelper - <NodeHelper> object.7254edgeHelper - <EdgeHelper> object.72557256*/7257Graph.Plot3D = $.merge(Graph.Plot, {7258Interpolator: {7259'linear': function(elem, props, delta) {7260var from = elem.startPos.getc(true);7261var to = elem.endPos.getc(true);7262elem.pos.setc(this.compute(from.x, to.x, delta),7263this.compute(from.y, to.y, delta),7264this.compute(from.z, to.z, delta));7265}7266},72677268plotNode: function(node, canvas) {7269if(node.getData('type') == 'none') return;7270this.plotElement(node, canvas, {7271getAlpha: function() {7272return node.getData('alpha');7273}7274});7275},72767277plotLine: function(adj, canvas) {7278if(adj.getData('type') == 'none') return;7279this.plotElement(adj, canvas, {7280getAlpha: function() {7281return Math.min(adj.nodeFrom.getData('alpha'),7282adj.nodeTo.getData('alpha'),7283adj.getData('alpha'));7284}7285});7286},72877288plotElement: function(elem, canvas, opt) {7289var gl = canvas.getCtx(),7290viewMatrix = new Matrix4,7291lighting = canvas.config.Scene.Lighting,7292wcanvas = canvas.canvases[0],7293program = wcanvas.program,7294camera = wcanvas.camera;72957296if(!elem.geometry) {7297elem.geometry = new O3D[elem.getData('type')];7298}7299elem.geometry.update(elem);7300if(!elem.webGLVertexBuffer) {7301var vertices = [],7302faces = [],7303normals = [],7304vertexIndex = 0,7305geom = elem.geometry;73067307for(var i=0, vs=geom.vertices, fs=geom.faces, fsl=fs.length; i<fsl; i++) {7308var face = fs[i],7309v1 = vs[face.a],7310v2 = vs[face.b],7311v3 = vs[face.c],7312v4 = face.d? vs[face.d] : false,7313n = face.normal;73147315vertices.push(v1.x, v1.y, v1.z);7316vertices.push(v2.x, v2.y, v2.z);7317vertices.push(v3.x, v3.y, v3.z);7318if(v4) vertices.push(v4.x, v4.y, v4.z);73197320normals.push(n.x, n.y, n.z);7321normals.push(n.x, n.y, n.z);7322normals.push(n.x, n.y, n.z);7323if(v4) normals.push(n.x, n.y, n.z);73247325faces.push(vertexIndex, vertexIndex +1, vertexIndex +2);7326if(v4) {7327faces.push(vertexIndex, vertexIndex +2, vertexIndex +3);7328vertexIndex += 4;7329} else {7330vertexIndex += 3;7331}7332}7333//create and store vertex data7334elem.webGLVertexBuffer = gl.createBuffer();7335gl.bindBuffer(gl.ARRAY_BUFFER, elem.webGLVertexBuffer);7336gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);7337//create and store faces index data7338elem.webGLFaceBuffer = gl.createBuffer();7339gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elem.webGLFaceBuffer);7340gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(faces), gl.STATIC_DRAW);7341elem.webGLFaceCount = faces.length;7342//calculate vertex normals and store them7343elem.webGLNormalBuffer = gl.createBuffer();7344gl.bindBuffer(gl.ARRAY_BUFFER, elem.webGLNormalBuffer);7345gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(normals), gl.STATIC_DRAW);7346}7347viewMatrix.multiply(camera.matrix, elem.geometry.matrix);7348//send matrix data7349gl.uniformMatrix4fv(program.viewMatrix, false, viewMatrix.flatten());7350gl.uniformMatrix4fv(program.projectionMatrix, false, camera.projectionMatrix.flatten());7351//send normal matrix for lighting7352var normalMatrix = Matrix4.makeInvert(viewMatrix);7353normalMatrix.$transpose();7354gl.uniformMatrix4fv(program.normalMatrix, false, normalMatrix.flatten());7355//send color data7356var color = $.hexToRgb(elem.getData('color'));7357color.push(opt.getAlpha());7358gl.uniform4f(program.color, color[0] / 255, color[1] / 255, color[2] / 255, color[3]);7359//send lighting data7360gl.uniform1i(program.enableLighting, lighting.enable);7361if(lighting.enable) {7362//set ambient light color7363if(lighting.ambient) {7364var acolor = lighting.ambient;7365gl.uniform3f(program.ambientColor, acolor[0], acolor[1], acolor[2]);7366}7367//set directional light7368if(lighting.directional) {7369var dir = lighting.directional,7370color = dir.color,7371pos = dir.direction,7372vd = new Vector3(pos.x, pos.y, pos.z).normalize().$scale(-1);7373gl.uniform3f(program.lightingDirection, vd.x, vd.y, vd.z);7374gl.uniform3f(program.directionalColor, color[0], color[1], color[2]);7375}7376}7377//send vertices data7378gl.bindBuffer(gl.ARRAY_BUFFER, elem.webGLVertexBuffer);7379gl.vertexAttribPointer(program.position, 3, gl.FLOAT, false, 0, 0);7380//send normals data7381gl.bindBuffer(gl.ARRAY_BUFFER, elem.webGLNormalBuffer);7382gl.vertexAttribPointer(program.normal, 3, gl.FLOAT, false, 0, 0);7383//draw!7384gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elem.webGLFaceBuffer );7385gl.drawElements(gl.TRIANGLES, elem.webGLFaceCount, gl.UNSIGNED_SHORT, 0);7386}7387});738873897390/*7391* File: Graph.Label.js7392*7393*/73947395/*7396Object: Graph.Label73977398An interface for plotting/hiding/showing labels.73997400Description:74017402This is a generic interface for plotting/hiding/showing labels.7403The <Graph.Label> interface is implemented in multiple ways to provide7404different label types.74057406For example, the Graph.Label interface is implemented as <Graph.Label.HTML> to provide7407HTML label elements. Also we provide the <Graph.Label.SVG> interface for SVG type labels.7408The <Graph.Label.Native> interface implements these methods with the native Canvas text rendering functions.74097410All subclasses (<Graph.Label.HTML>, <Graph.Label.SVG> and <Graph.Label.Native>) implement the method plotLabel.7411*/74127413Graph.Label = {};74147415/*7416Class: Graph.Label.Native74177418Implements labels natively, using the Canvas text API.7419*/7420Graph.Label.Native = new Class({7421initialize: function(viz) {7422this.viz = viz;7423},74247425/*7426Method: plotLabel74277428Plots a label for a given node.74297430Parameters:74317432canvas - (object) A <Canvas> instance.7433node - (object) A <Graph.Node>.7434controller - (object) A configuration object.74357436Example:74377438(start code js)7439var viz = new $jit.Viz(options);7440var node = viz.graph.getNode('nodeId');7441viz.labels.plotLabel(viz.canvas, node, viz.config);7442(end code)7443*/7444plotLabel: function(canvas, node, controller) {7445var ctx = canvas.getCtx();7446var pos = node.pos.getc(true);74477448ctx.font = node.getLabelData('style') + ' ' + node.getLabelData('size') + 'px ' + node.getLabelData('family');7449ctx.textAlign = node.getLabelData('textAlign');7450ctx.fillStyle = ctx.strokeStyle = node.getLabelData('color');7451ctx.textBaseline = node.getLabelData('textBaseline');74527453this.renderLabel(canvas, node, controller);7454},74557456/*7457renderLabel74587459Does the actual rendering of the label in the canvas. The default7460implementation renders the label close to the position of the node, this7461method should be overriden to position the labels differently.74627463Parameters:74647465canvas - A <Canvas> instance.7466node - A <Graph.Node>.7467controller - A configuration object. See also <Hypertree>, <RGraph>, <ST>.7468*/7469renderLabel: function(canvas, node, controller) {7470var ctx = canvas.getCtx();7471var pos = node.pos.getc(true);7472ctx.fillText(node.name, pos.x, pos.y + node.getData("height") / 2);7473},74747475hideLabel: $.empty,7476hideLabels: $.empty7477});74787479/*7480Class: Graph.Label.DOM74817482Abstract Class implementing some DOM label methods.74837484Implemented by:74857486<Graph.Label.HTML> and <Graph.Label.SVG>.74877488*/7489Graph.Label.DOM = new Class({7490//A flag value indicating if node labels are being displayed or not.7491labelsHidden: false,7492//Label container7493labelContainer: false,7494//Label elements hash.7495labels: {},74967497/*7498Method: getLabelContainer74997500Lazy fetcher for the label container.75017502Returns:75037504The label container DOM element.75057506Example:75077508(start code js)7509var viz = new $jit.Viz(options);7510var labelContainer = viz.labels.getLabelContainer();7511alert(labelContainer.innerHTML);7512(end code)7513*/7514getLabelContainer: function() {7515return this.labelContainer ?7516this.labelContainer :7517this.labelContainer = document.getElementById(this.viz.config.labelContainer);7518},75197520/*7521Method: getLabel75227523Lazy fetcher for the label element.75247525Parameters:75267527id - (string) The label id (which is also a <Graph.Node> id).75287529Returns:75307531The label element.75327533Example:75347535(start code js)7536var viz = new $jit.Viz(options);7537var label = viz.labels.getLabel('someid');7538alert(label.innerHTML);7539(end code)75407541*/7542getLabel: function(id) {7543return (id in this.labels && this.labels[id] != null) ?7544this.labels[id] :7545this.labels[id] = document.getElementById(id);7546},75477548/*7549Method: hideLabels75507551Hides all labels (by hiding the label container).75527553Parameters:75547555hide - (boolean) A boolean value indicating if the label container must be hidden or not.75567557Example:7558(start code js)7559var viz = new $jit.Viz(options);7560rg.labels.hideLabels(true);7561(end code)75627563*/7564hideLabels: function (hide) {7565var container = this.getLabelContainer();7566if(hide)7567container.style.display = 'none';7568else7569container.style.display = '';7570this.labelsHidden = hide;7571},75727573/*7574Method: clearLabels75757576Clears the label container.75777578Useful when using a new visualization with the same canvas element/widget.75797580Parameters:75817582force - (boolean) Forces deletion of all labels.75837584Example:7585(start code js)7586var viz = new $jit.Viz(options);7587viz.labels.clearLabels();7588(end code)7589*/7590clearLabels: function(force) {7591for(var id in this.labels) {7592if (force || !this.viz.graph.hasNode(id)) {7593this.disposeLabel(id);7594delete this.labels[id];7595}7596}7597},75987599/*7600Method: disposeLabel76017602Removes a label.76037604Parameters:76057606id - (string) A label id (which generally is also a <Graph.Node> id).76077608Example:7609(start code js)7610var viz = new $jit.Viz(options);7611viz.labels.disposeLabel('labelid');7612(end code)7613*/7614disposeLabel: function(id) {7615var elem = this.getLabel(id);7616if(elem && elem.parentNode) {7617elem.parentNode.removeChild(elem);7618}7619},76207621/*7622Method: hideLabel76237624Hides the corresponding <Graph.Node> label.76257626Parameters:76277628node - (object) A <Graph.Node>. Can also be an array of <Graph.Nodes>.7629show - (boolean) If *true*, nodes will be shown. Otherwise nodes will be hidden.76307631Example:7632(start code js)7633var rg = new $jit.Viz(options);7634viz.labels.hideLabel(viz.graph.getNode('someid'), false);7635(end code)7636*/7637hideLabel: function(node, show) {7638node = $.splat(node);7639var st = show ? "" : "none", lab, that = this;7640$.each(node, function(n) {7641var lab = that.getLabel(n.id);7642if (lab) {7643lab.style.display = st;7644}7645});7646},76477648/*7649fitsInCanvas76507651Returns _true_ or _false_ if the label for the node is contained in the canvas dom element or not.76527653Parameters:76547655pos - A <Complex> instance (I'm doing duck typing here so any object with _x_ and _y_ parameters will do).7656canvas - A <Canvas> instance.76577658Returns:76597660A boolean value specifying if the label is contained in the <Canvas> DOM element or not.76617662*/7663fitsInCanvas: function(pos, canvas) {7664var size = canvas.getSize();7665if(pos.x >= size.width || pos.x < 07666|| pos.y >= size.height || pos.y < 0) return false;7667return true;7668}7669});76707671/*7672Class: Graph.Label.HTML76737674Implements HTML labels.76757676Extends:76777678All <Graph.Label.DOM> methods.76797680*/7681Graph.Label.HTML = new Class({7682Implements: Graph.Label.DOM,76837684/*7685Method: plotLabel76867687Plots a label for a given node.76887689Parameters:76907691canvas - (object) A <Canvas> instance.7692node - (object) A <Graph.Node>.7693controller - (object) A configuration object.76947695Example:76967697(start code js)7698var viz = new $jit.Viz(options);7699var node = viz.graph.getNode('nodeId');7700viz.labels.plotLabel(viz.canvas, node, viz.config);7701(end code)770277037704*/7705plotLabel: function(canvas, node, controller) {7706var id = node.id, tag = this.getLabel(id);77077708if(!tag && !(tag = document.getElementById(id))) {7709tag = document.createElement('div');7710var container = this.getLabelContainer();7711tag.id = id;7712tag.className = 'node';7713tag.style.position = 'absolute';7714controller.onCreateLabel(tag, node);7715container.appendChild(tag);7716this.labels[node.id] = tag;7717}77187719this.placeLabel(tag, node, controller);7720}7721});77227723/*7724Class: Graph.Label.SVG77257726Implements SVG labels.77277728Extends:77297730All <Graph.Label.DOM> methods.7731*/7732Graph.Label.SVG = new Class({7733Implements: Graph.Label.DOM,77347735/*7736Method: plotLabel77377738Plots a label for a given node.77397740Parameters:77417742canvas - (object) A <Canvas> instance.7743node - (object) A <Graph.Node>.7744controller - (object) A configuration object.77457746Example:77477748(start code js)7749var viz = new $jit.Viz(options);7750var node = viz.graph.getNode('nodeId');7751viz.labels.plotLabel(viz.canvas, node, viz.config);7752(end code)775377547755*/7756plotLabel: function(canvas, node, controller) {7757var id = node.id, tag = this.getLabel(id);7758if(!tag && !(tag = document.getElementById(id))) {7759var ns = 'http://www.w3.org/2000/svg';7760tag = document.createElementNS(ns, 'svg:text');7761var tspan = document.createElementNS(ns, 'svg:tspan');7762tag.appendChild(tspan);7763var container = this.getLabelContainer();7764tag.setAttribute('id', id);7765tag.setAttribute('class', 'node');7766container.appendChild(tag);7767controller.onCreateLabel(tag, node);7768this.labels[node.id] = tag;7769}7770this.placeLabel(tag, node, controller);7771}7772});7773777477757776Graph.Geom = new Class({77777778initialize: function(viz) {7779this.viz = viz;7780this.config = viz.config;7781this.node = viz.config.Node;7782this.edge = viz.config.Edge;7783},7784/*7785Applies a translation to the tree.77867787Parameters:77887789pos - A <Complex> number specifying translation vector.7790prop - A <Graph.Node> position property ('pos', 'start' or 'end').77917792Example:77937794(start code js)7795st.geom.translate(new Complex(300, 100), 'end');7796(end code)7797*/7798translate: function(pos, prop) {7799prop = $.splat(prop);7800this.viz.graph.eachNode(function(elem) {7801$.each(prop, function(p) { elem.getPos(p).$add(pos); });7802});7803},7804/*7805Hides levels of the tree until it properly fits in canvas.7806*/7807setRightLevelToShow: function(node, canvas, callback) {7808var level = this.getRightLevelToShow(node, canvas),7809fx = this.viz.labels,7810opt = $.merge({7811execShow:true,7812execHide:true,7813onHide: $.empty,7814onShow: $.empty7815}, callback || {});7816node.eachLevel(0, this.config.levelsToShow, function(n) {7817var d = n._depth - node._depth;7818if(d > level) {7819opt.onHide(n);7820if(opt.execHide) {7821n.drawn = false;7822n.exist = false;7823fx.hideLabel(n, false);7824}7825} else {7826opt.onShow(n);7827if(opt.execShow) {7828n.exist = true;7829}7830}7831});7832node.drawn= true;7833},7834/*7835Returns the right level to show for the current tree in order to fit in canvas.7836*/7837getRightLevelToShow: function(node, canvas) {7838var config = this.config;7839var level = config.levelsToShow;7840var constrained = config.constrained;7841if(!constrained) return level;7842while(!this.treeFitsInCanvas(node, canvas, level) && level > 1) { level-- ; }7843return level;7844}7845});78467847/*7848* File: Loader.js7849*7850*/78517852/*7853Object: Loader78547855Provides methods for loading and serving JSON data.7856*/7857var Loader = {7858construct: function(json) {7859var isGraph = ($.type(json) == 'array');7860var ans = new Graph(this.graphOptions, this.config.Node, this.config.Edge, this.config.Label);7861if(!isGraph)7862//make tree7863(function (ans, json) {7864ans.addNode(json);7865if(json.children) {7866for(var i=0, ch = json.children; i<ch.length; i++) {7867ans.addAdjacence(json, ch[i]);7868arguments.callee(ans, ch[i]);7869}7870}7871})(ans, json);7872else7873//make graph7874(function (ans, json) {7875var getNode = function(id) {7876for(var i=0, l=json.length; i<l; i++) {7877if(json[i].id == id) {7878return json[i];7879}7880}7881// The node was not defined in the JSON7882// Let's create it7883var newNode = {7884"id" : id,7885"name" : id7886};7887return ans.addNode(newNode);7888};78897890for(var i=0, l=json.length; i<l; i++) {7891ans.addNode(json[i]);7892var adj = json[i].adjacencies;7893if (adj) {7894for(var j=0, lj=adj.length; j<lj; j++) {7895var node = adj[j], data = {};7896if(typeof adj[j] != 'string') {7897data = $.merge(node.data, {});7898node = node.nodeTo;7899}7900ans.addAdjacence(json[i], getNode(node), data);7901}7902}7903}7904})(ans, json);79057906return ans;7907},79087909/*7910Method: loadJSON79117912Loads a JSON structure to the visualization. The JSON structure can be a JSON *tree* or *graph* structure.79137914A JSON tree or graph structure consists of nodes, each having as properties79157916id - (string) A unique identifier for the node7917name - (string) A node's name7918data - (object) The data optional property contains a hash (i.e {})7919where you can store all the information you want about this node.79207921For JSON *Tree* structures, there's an extra optional property *children* of type Array which contains the node's children.79227923Example:79247925(start code js)7926var json = {7927"id": "aUniqueIdentifier",7928"name": "usually a nodes name",7929"data": {7930"some key": "some value",7931"some other key": "some other value"7932},7933"children": [ *other nodes or empty* ]7934};7935(end code)79367937JSON *Graph* structures consist of an array of nodes, each specifying the nodes to which the current node is connected.7938For JSON *Graph* structures, the *children* property is replaced by the *adjacencies* property.79397940There are two types of *Graph* structures, *simple* and *extended* graph structures.79417942For *simple* Graph structures, the adjacencies property contains an array of strings, each specifying the7943id of the node connected to the main node.79447945Example:79467947(start code js)7948var json = [7949{7950"id": "aUniqueIdentifier",7951"name": "usually a nodes name",7952"data": {7953"some key": "some value",7954"some other key": "some other value"7955},7956"adjacencies": ["anotherUniqueIdentifier", "yetAnotherUniqueIdentifier", 'etc']7957},79587959'other nodes go here...'7960];7961(end code)79627963For *extended Graph structures*, the adjacencies property contains an array of Adjacency objects that have as properties79647965nodeTo - (string) The other node connected by this adjacency.7966data - (object) A data property, where we can store custom key/value information.79677968Example:79697970(start code js)7971var json = [7972{7973"id": "aUniqueIdentifier",7974"name": "usually a nodes name",7975"data": {7976"some key": "some value",7977"some other key": "some other value"7978},7979"adjacencies": [7980{7981nodeTo:"aNodeId",7982data: {} //put whatever you want here7983},7984'other adjacencies go here...'7985},79867987'other nodes go here...'7988];7989(end code)79907991About the data property:79927993As described before, you can store custom data in the *data* property of JSON *nodes* and *adjacencies*.7994You can use almost any string as key for the data object. Some keys though are reserved by the toolkit, and7995have special meanings. This is the case for keys starting with a dollar sign, for example, *$width*.79967997For JSON *node* objects, adding dollar prefixed properties that match the names of the options defined in7998<Options.Node> will override the general value for that option with that particular value. For this to work7999however, you do have to set *overridable = true* in <Options.Node>.80008001The same thing is true for JSON adjacencies. Dollar prefixed data properties will alter values set in <Options.Edge>8002if <Options.Edge> has *overridable = true*.80038004When loading JSON data into TreeMaps, the *data* property must contain a value for the *$area* key,8005since this is the value which will be taken into account when creating the layout.8006The same thing goes for the *$color* parameter.80078008In JSON Nodes you can use also *$label-* prefixed properties to refer to <Options.Label> properties. For example,8009*$label-size* will refer to <Options.Label> size property. Also, in JSON nodes and adjacencies you can set8010canvas specific properties individually by using the *$canvas-* prefix. For example, *$canvas-shadowBlur* will refer8011to the *shadowBlur* property.80128013These properties can also be accessed after loading the JSON data from <Graph.Nodes> and <Graph.Adjacences>8014by using <Accessors>. For more information take a look at the <Graph> and <Accessors> documentation.80158016Finally, these properties can also be used to create advanced animations like with <Options.NodeStyles>. For more8017information about creating animations please take a look at the <Graph.Plot> and <Graph.Plot.animate> documentation.80188019loadJSON Parameters:80208021json - A JSON Tree or Graph structure.8022i - For Graph structures only. Sets the indexed node as root for the visualization.80238024*/8025loadJSON: function(json, i) {8026this.json = json;8027//if they're canvas labels erase them.8028if(this.labels && this.labels.clearLabels) {8029this.labels.clearLabels(true);8030}8031this.graph = this.construct(json);8032if($.type(json) != 'array'){8033this.root = json.id;8034} else {8035this.root = json[i? i : 0].id;8036}8037},80388039/*8040Method: toJSON80418042Returns a JSON tree/graph structure from the visualization's <Graph>.8043See <Loader.loadJSON> for the graph formats available.80448045See also:80468047<Loader.loadJSON>80488049Parameters:80508051type - (string) Default's "tree". The type of the JSON structure to be returned.8052Possible options are "tree" or "graph".8053*/8054toJSON: function(type) {8055type = type || "tree";8056if(type == 'tree') {8057var ans = {};8058var rootNode = this.graph.getNode(this.root);8059var ans = (function recTree(node) {8060var ans = {};8061ans.id = node.id;8062ans.name = node.name;8063ans.data = node.data;8064var ch =[];8065node.eachSubnode(function(n) {8066ch.push(recTree(n));8067});8068ans.children = ch;8069return ans;8070})(rootNode);8071return ans;8072} else {8073var ans = [];8074var T = !!this.graph.getNode(this.root).visited;8075this.graph.eachNode(function(node) {8076var ansNode = {};8077ansNode.id = node.id;8078ansNode.name = node.name;8079ansNode.data = node.data;8080var adjs = [];8081node.eachAdjacency(function(adj) {8082var nodeTo = adj.nodeTo;8083if(!!nodeTo.visited === T) {8084var ansAdj = {};8085ansAdj.nodeTo = nodeTo.id;8086ansAdj.data = adj.data;8087adjs.push(ansAdj);8088}8089});8090ansNode.adjacencies = adjs;8091ans.push(ansNode);8092node.visited = !T;8093});8094return ans;8095}8096}8097};8098809981008101/*8102* File: Layouts.js8103*8104* Implements base Tree and Graph layouts.8105*8106* Description:8107*8108* Implements base Tree and Graph layouts like Radial, Tree, etc.8109*8110*/81118112/*8113* Object: Layouts8114*8115* Parent object for common layouts.8116*8117*/8118var Layouts = $jit.Layouts = {};811981208121//Some util shared layout functions are defined here.8122var NodeDim = {8123label: null,81248125compute: function(graph, prop, opt) {8126this.initializeLabel(opt);8127var label = this.label, style = label.style;8128graph.eachNode(function(n) {8129var autoWidth = n.getData('autoWidth'),8130autoHeight = n.getData('autoHeight');8131if(autoWidth || autoHeight) {8132//delete dimensions since these are8133//going to be overridden now.8134delete n.data.$width;8135delete n.data.$height;8136delete n.data.$dim;81378138var width = n.getData('width'),8139height = n.getData('height');8140//reset label dimensions8141style.width = autoWidth? 'auto' : width + 'px';8142style.height = autoHeight? 'auto' : height + 'px';81438144//TODO(nico) should let the user choose what to insert here.8145label.innerHTML = n.name;81468147var offsetWidth = label.offsetWidth,8148offsetHeight = label.offsetHeight;8149var type = n.getData('type');8150if($.indexOf(['circle', 'square', 'triangle', 'star'], type) === -1) {8151n.setData('width', offsetWidth);8152n.setData('height', offsetHeight);8153} else {8154var dim = offsetWidth > offsetHeight? offsetWidth : offsetHeight;8155n.setData('width', dim);8156n.setData('height', dim);8157n.setData('dim', dim);8158}8159}8160});8161},81628163initializeLabel: function(opt) {8164if(!this.label) {8165this.label = document.createElement('div');8166document.body.appendChild(this.label);8167}8168this.setLabelStyles(opt);8169},81708171setLabelStyles: function(opt) {8172$.extend(this.label.style, {8173'visibility': 'hidden',8174'position': 'absolute',8175'width': 'auto',8176'height': 'auto'8177});8178this.label.className = 'jit-autoadjust-label';8179}8180};818181828183/*8184* Class: Layouts.Tree8185*8186* Implements a Tree Layout.8187*8188* Implemented By:8189*8190* <ST>8191*8192* Inspired by:8193*8194* Drawing Trees (Andrew J. Kennedy) <http://research.microsoft.com/en-us/um/people/akenn/fun/drawingtrees.pdf>8195*8196*/8197Layouts.Tree = (function() {8198//Layout functions8199var slice = Array.prototype.slice;82008201/*8202Calculates the max width and height nodes for a tree level8203*/8204function getBoundaries(graph, config, level, orn, prop) {8205var dim = config.Node;8206var multitree = config.multitree;8207if (dim.overridable) {8208var w = -1, h = -1;8209graph.eachNode(function(n) {8210if (n._depth == level8211&& (!multitree || ('$orn' in n.data) && n.data.$orn == orn)) {8212var dw = n.getData('width', prop);8213var dh = n.getData('height', prop);8214w = (w < dw) ? dw : w;8215h = (h < dh) ? dh : h;8216}8217});8218return {8219'width' : w < 0 ? dim.width : w,8220'height' : h < 0 ? dim.height : h8221};8222} else {8223return dim;8224}8225}822682278228function movetree(node, prop, val, orn) {8229var p = (orn == "left" || orn == "right") ? "y" : "x";8230node.getPos(prop)[p] += val;8231}823282338234function moveextent(extent, val) {8235var ans = [];8236$.each(extent, function(elem) {8237elem = slice.call(elem);8238elem[0] += val;8239elem[1] += val;8240ans.push(elem);8241});8242return ans;8243}824482458246function merge(ps, qs) {8247if (ps.length == 0)8248return qs;8249if (qs.length == 0)8250return ps;8251var p = ps.shift(), q = qs.shift();8252return [ [ p[0], q[1] ] ].concat(merge(ps, qs));8253}825482558256function mergelist(ls, def) {8257def = def || [];8258if (ls.length == 0)8259return def;8260var ps = ls.pop();8261return mergelist(ls, merge(ps, def));8262}826382648265function fit(ext1, ext2, subtreeOffset, siblingOffset, i) {8266if (ext1.length <= i || ext2.length <= i)8267return 0;82688269var p = ext1[i][1], q = ext2[i][0];8270return Math.max(fit(ext1, ext2, subtreeOffset, siblingOffset, ++i)8271+ subtreeOffset, p - q + siblingOffset);8272}827382748275function fitlistl(es, subtreeOffset, siblingOffset) {8276function $fitlistl(acc, es, i) {8277if (es.length <= i)8278return [];8279var e = es[i], ans = fit(acc, e, subtreeOffset, siblingOffset, 0);8280return [ ans ].concat($fitlistl(merge(acc, moveextent(e, ans)), es, ++i));8281}8282;8283return $fitlistl( [], es, 0);8284}828582868287function fitlistr(es, subtreeOffset, siblingOffset) {8288function $fitlistr(acc, es, i) {8289if (es.length <= i)8290return [];8291var e = es[i], ans = -fit(e, acc, subtreeOffset, siblingOffset, 0);8292return [ ans ].concat($fitlistr(merge(moveextent(e, ans), acc), es, ++i));8293}8294;8295es = slice.call(es);8296var ans = $fitlistr( [], es.reverse(), 0);8297return ans.reverse();8298}829983008301function fitlist(es, subtreeOffset, siblingOffset, align) {8302var esl = fitlistl(es, subtreeOffset, siblingOffset), esr = fitlistr(es,8303subtreeOffset, siblingOffset);83048305if (align == "left")8306esr = esl;8307else if (align == "right")8308esl = esr;83098310for ( var i = 0, ans = []; i < esl.length; i++) {8311ans[i] = (esl[i] + esr[i]) / 2;8312}8313return ans;8314}831583168317function design(graph, node, prop, config, orn) {8318var multitree = config.multitree;8319var auxp = [ 'x', 'y' ], auxs = [ 'width', 'height' ];8320var ind = +(orn == "left" || orn == "right");8321var p = auxp[ind], notp = auxp[1 - ind];83228323var cnode = config.Node;8324var s = auxs[ind], nots = auxs[1 - ind];83258326var siblingOffset = config.siblingOffset;8327var subtreeOffset = config.subtreeOffset;8328var align = config.align;83298330function $design(node, maxsize, acum) {8331var sval = node.getData(s, prop);8332var notsval = maxsize8333|| (node.getData(nots, prop));83348335var trees = [], extents = [], chmaxsize = false;8336var chacum = notsval + config.levelDistance;8337node.eachSubnode(function(n) {8338if (n.exist8339&& (!multitree || ('$orn' in n.data) && n.data.$orn == orn)) {83408341if (!chmaxsize)8342chmaxsize = getBoundaries(graph, config, n._depth, orn, prop);83438344var s = $design(n, chmaxsize[nots], acum + chacum);8345trees.push(s.tree);8346extents.push(s.extent);8347}8348});8349var positions = fitlist(extents, subtreeOffset, siblingOffset, align);8350for ( var i = 0, ptrees = [], pextents = []; i < trees.length; i++) {8351movetree(trees[i], prop, positions[i], orn);8352pextents.push(moveextent(extents[i], positions[i]));8353}8354var resultextent = [ [ -sval / 2, sval / 2 ] ]8355.concat(mergelist(pextents));8356node.getPos(prop)[p] = 0;83578358if (orn == "top" || orn == "left") {8359node.getPos(prop)[notp] = acum;8360} else {8361node.getPos(prop)[notp] = -acum;8362}83638364return {8365tree : node,8366extent : resultextent8367};8368}83698370$design(node, false, 0);8371}837283738374return new Class({8375/*8376Method: compute83778378Computes nodes' positions.83798380*/8381compute : function(property, computeLevels) {8382var prop = property || 'start';8383var node = this.graph.getNode(this.root);8384$.extend(node, {8385'drawn' : true,8386'exist' : true,8387'selected' : true8388});8389NodeDim.compute(this.graph, prop, this.config);8390if (!!computeLevels || !("_depth" in node)) {8391this.graph.computeLevels(this.root, 0, "ignore");8392}83938394this.computePositions(node, prop);8395},83968397computePositions : function(node, prop) {8398var config = this.config;8399var multitree = config.multitree;8400var align = config.align;8401var indent = align !== 'center' && config.indent;8402var orn = config.orientation;8403var orns = multitree ? [ 'top', 'right', 'bottom', 'left' ] : [ orn ];8404var that = this;8405$.each(orns, function(orn) {8406//calculate layout8407design(that.graph, node, prop, that.config, orn, prop);8408var i = [ 'x', 'y' ][+(orn == "left" || orn == "right")];8409//absolutize8410(function red(node) {8411node.eachSubnode(function(n) {8412if (n.exist8413&& (!multitree || ('$orn' in n.data) && n.data.$orn == orn)) {84148415n.getPos(prop)[i] += node.getPos(prop)[i];8416if (indent) {8417n.getPos(prop)[i] += align == 'left' ? indent : -indent;8418}8419red(n);8420}8421});8422})(node);8423});8424}8425});84268427})();84288429/*8430* File: Spacetree.js8431*/84328433/*8434Class: ST84358436A Tree layout with advanced contraction and expansion animations.84378438Inspired by:84398440SpaceTree: Supporting Exploration in Large Node Link Tree, Design Evolution and Empirical Evaluation (Catherine Plaisant, Jesse Grosjean, Benjamin B. Bederson)8441<http://hcil.cs.umd.edu/trs/2002-05/2002-05.pdf>84428443Drawing Trees (Andrew J. Kennedy) <http://research.microsoft.com/en-us/um/people/akenn/fun/drawingtrees.pdf>84448445Note:84468447This visualization was built and engineered from scratch, taking only the papers as inspiration, and only shares some features with the visualization described in those papers.84488449Implements:84508451All <Loader> methods84528453Constructor Options:84548455Inherits options from84568457- <Options.Canvas>8458- <Options.Controller>8459- <Options.Tree>8460- <Options.Node>8461- <Options.Edge>8462- <Options.Label>8463- <Options.Events>8464- <Options.Tips>8465- <Options.NodeStyles>8466- <Options.Navigation>84678468Additionally, there are other parameters and some default values changed84698470constrained - (boolean) Default's *true*. Whether to show the entire tree when loaded or just the number of levels specified by _levelsToShow_.8471levelsToShow - (number) Default's *2*. The number of levels to show for a subtree. This number is relative to the selected node.8472levelDistance - (number) Default's *30*. The distance between two consecutive levels of the tree.8473Node.type - Described in <Options.Node>. Default's set to *rectangle*.8474offsetX - (number) Default's *0*. The x-offset distance from the selected node to the center of the canvas.8475offsetY - (number) Default's *0*. The y-offset distance from the selected node to the center of the canvas.8476duration - Described in <Options.Fx>. It's default value has been changed to *700*.84778478Instance Properties:84798480canvas - Access a <Canvas> instance.8481graph - Access a <Graph> instance.8482op - Access a <ST.Op> instance.8483fx - Access a <ST.Plot> instance.8484labels - Access a <ST.Label> interface implementation.84858486*/84878488$jit.ST= (function() {8489// Define some private methods first...8490// Nodes in path8491var nodesInPath = [];8492// Nodes to contract8493function getNodesToHide(node) {8494node = node || this.clickedNode;8495if(!this.config.constrained) {8496return [];8497}8498var Geom = this.geom;8499var graph = this.graph;8500var canvas = this.canvas;8501var level = node._depth, nodeArray = [];8502graph.eachNode(function(n) {8503if(n.exist && !n.selected) {8504if(n.isDescendantOf(node.id)) {8505if(n._depth <= level) nodeArray.push(n);8506} else {8507nodeArray.push(n);8508}8509}8510});8511var leafLevel = Geom.getRightLevelToShow(node, canvas);8512node.eachLevel(leafLevel, leafLevel, function(n) {8513if(n.exist && !n.selected) nodeArray.push(n);8514});85158516for (var i = 0; i < nodesInPath.length; i++) {8517var n = this.graph.getNode(nodesInPath[i]);8518if(!n.isDescendantOf(node.id)) {8519nodeArray.push(n);8520}8521}8522return nodeArray;8523};8524// Nodes to expand8525function getNodesToShow(node) {8526var nodeArray = [], config = this.config;8527node = node || this.clickedNode;8528this.clickedNode.eachLevel(0, config.levelsToShow, function(n) {8529if(config.multitree && !('$orn' in n.data)8530&& n.anySubnode(function(ch){ return ch.exist && !ch.drawn; })) {8531nodeArray.push(n);8532} else if(n.drawn && !n.anySubnode("drawn")) {8533nodeArray.push(n);8534}8535});8536return nodeArray;8537};8538// Now define the actual class.8539return new Class({85408541Implements: [Loader, Extras, Layouts.Tree],85428543initialize: function(controller) {8544var $ST = $jit.ST;85458546var config= {8547levelsToShow: 2,8548levelDistance: 30,8549constrained: true,8550Node: {8551type: 'rectangle'8552},8553duration: 700,8554offsetX: 0,8555offsetY: 08556};85578558this.controller = this.config = $.merge(8559Options("Canvas", "Fx", "Tree", "Node", "Edge", "Controller",8560"Tips", "NodeStyles", "Events", "Navigation", "Label"), config, controller);85618562var canvasConfig = this.config;8563if(canvasConfig.useCanvas) {8564this.canvas = canvasConfig.useCanvas;8565this.config.labelContainer = this.canvas.id + '-label';8566} else {8567if(canvasConfig.background) {8568canvasConfig.background = $.merge({8569type: 'Circles'8570}, canvasConfig.background);8571}8572this.canvas = new Canvas(this, canvasConfig);8573this.config.labelContainer = (typeof canvasConfig.injectInto == 'string'? canvasConfig.injectInto : canvasConfig.injectInto.id) + '-label';8574}85758576this.graphOptions = {8577'klass': Complex8578};8579this.graph = new Graph(this.graphOptions, this.config.Node, this.config.Edge);8580this.labels = new $ST.Label[canvasConfig.Label.type](this);8581this.fx = new $ST.Plot(this, $ST);8582this.op = new $ST.Op(this);8583this.group = new $ST.Group(this);8584this.geom = new $ST.Geom(this);8585this.clickedNode= null;8586// initialize extras8587this.initializeExtras();8588},85898590/*8591Method: plot85928593Plots the <ST>. This is a shortcut to *fx.plot*.85948595*/8596plot: function() { this.fx.plot(this.controller); },859785988599/*8600Method: switchPosition86018602Switches the tree orientation.86038604Parameters:86058606pos - (string) The new tree orientation. Possible values are "top", "left", "right" and "bottom".8607method - (string) Set this to "animate" if you want to animate the tree when switching its position. You can also set this parameter to "replot" to just replot the subtree.8608onComplete - (optional|object) This callback is called once the "switching" animation is complete.86098610Example:86118612(start code js)8613st.switchPosition("right", "animate", {8614onComplete: function() {8615alert('completed!');8616}8617});8618(end code)8619*/8620switchPosition: function(pos, method, onComplete) {8621var Geom = this.geom, Plot = this.fx, that = this;8622if(!Plot.busy) {8623Plot.busy = true;8624this.contract({8625onComplete: function() {8626Geom.switchOrientation(pos);8627that.compute('end', false);8628Plot.busy = false;8629if(method == 'animate') {8630that.onClick(that.clickedNode.id, onComplete);8631} else if(method == 'replot') {8632that.select(that.clickedNode.id, onComplete);8633}8634}8635}, pos);8636}8637},86388639/*8640Method: switchAlignment86418642Switches the tree alignment.86438644Parameters:86458646align - (string) The new tree alignment. Possible values are "left", "center" and "right".8647method - (string) Set this to "animate" if you want to animate the tree after aligning its position. You can also set this parameter to "replot" to just replot the subtree.8648onComplete - (optional|object) This callback is called once the "switching" animation is complete.86498650Example:86518652(start code js)8653st.switchAlignment("right", "animate", {8654onComplete: function() {8655alert('completed!');8656}8657});8658(end code)8659*/8660switchAlignment: function(align, method, onComplete) {8661this.config.align = align;8662if(method == 'animate') {8663this.select(this.clickedNode.id, onComplete);8664} else if(method == 'replot') {8665this.onClick(this.clickedNode.id, onComplete);8666}8667},86688669/*8670Method: addNodeInPath86718672Adds a node to the current path as selected node. The selected node will be visible (as in non-collapsed) at all times.867386748675Parameters:86768677id - (string) A <Graph.Node> id.86788679Example:86808681(start code js)8682st.addNodeInPath("nodeId");8683(end code)8684*/8685addNodeInPath: function(id) {8686nodesInPath.push(id);8687this.select((this.clickedNode && this.clickedNode.id) || this.root);8688},86898690/*8691Method: clearNodesInPath86928693Removes all nodes tagged as selected by the <ST.addNodeInPath> method.86948695See also:86968697<ST.addNodeInPath>86988699Example:87008701(start code js)8702st.clearNodesInPath();8703(end code)8704*/8705clearNodesInPath: function(id) {8706nodesInPath.length = 0;8707this.select((this.clickedNode && this.clickedNode.id) || this.root);8708},87098710/*8711Method: refresh87128713Computes positions and plots the tree.87148715*/8716refresh: function() {8717this.reposition();8718this.select((this.clickedNode && this.clickedNode.id) || this.root);8719},87208721reposition: function() {8722this.graph.computeLevels(this.root, 0, "ignore");8723this.geom.setRightLevelToShow(this.clickedNode, this.canvas);8724this.graph.eachNode(function(n) {8725if(n.exist) n.drawn = true;8726});8727this.compute('end');8728},87298730requestNodes: function(node, onComplete) {8731var handler = $.merge(this.controller, onComplete),8732lev = this.config.levelsToShow;8733if(handler.request) {8734var leaves = [], d = node._depth;8735node.eachLevel(0, lev, function(n) {8736if(n.drawn &&8737!n.anySubnode()) {8738leaves.push(n);8739n._level = lev - (n._depth - d);8740}8741});8742this.group.requestNodes(leaves, handler);8743}8744else8745handler.onComplete();8746},87478748contract: function(onComplete, switched) {8749var orn = this.config.orientation;8750var Geom = this.geom, Group = this.group;8751if(switched) Geom.switchOrientation(switched);8752var nodes = getNodesToHide.call(this);8753if(switched) Geom.switchOrientation(orn);8754Group.contract(nodes, $.merge(this.controller, onComplete));8755},87568757move: function(node, onComplete) {8758this.compute('end', false);8759var move = onComplete.Move, offset = {8760'x': move.offsetX,8761'y': move.offsetY8762};8763if(move.enable) {8764this.geom.translate(node.endPos.add(offset).$scale(-1), "end");8765}8766this.fx.animate($.merge(this.controller, { modes: ['linear'] }, onComplete));8767},87688769expand: function (node, onComplete) {8770var nodeArray = getNodesToShow.call(this, node);8771this.group.expand(nodeArray, $.merge(this.controller, onComplete));8772},87738774selectPath: function(node) {8775var that = this;8776this.graph.eachNode(function(n) { n.selected = false; });8777function path(node) {8778if(node == null || node.selected) return;8779node.selected = true;8780$.each(that.group.getSiblings([node])[node.id],8781function(n) {8782n.exist = true;8783n.drawn = true;8784});8785var parents = node.getParents();8786parents = (parents.length > 0)? parents[0] : null;8787path(parents);8788};8789for(var i=0, ns = [node.id].concat(nodesInPath); i < ns.length; i++) {8790path(this.graph.getNode(ns[i]));8791}8792},87938794/*8795Method: setRoot87968797Switches the current root node. Changes the topology of the Tree.87988799Parameters:8800id - (string) The id of the node to be set as root.8801method - (string) Set this to "animate" if you want to animate the tree after adding the subtree. You can also set this parameter to "replot" to just replot the subtree.8802onComplete - (optional|object) An action to perform after the animation (if any).88038804Example:88058806(start code js)8807st.setRoot('nodeId', 'animate', {8808onComplete: function() {8809alert('complete!');8810}8811});8812(end code)8813*/8814setRoot: function(id, method, onComplete) {8815if(this.busy) return;8816this.busy = true;8817var that = this, canvas = this.canvas;8818var rootNode = this.graph.getNode(this.root);8819var clickedNode = this.graph.getNode(id);8820function $setRoot() {8821if(this.config.multitree && clickedNode.data.$orn) {8822var orn = clickedNode.data.$orn;8823var opp = {8824'left': 'right',8825'right': 'left',8826'top': 'bottom',8827'bottom': 'top'8828}[orn];8829rootNode.data.$orn = opp;8830(function tag(rootNode) {8831rootNode.eachSubnode(function(n) {8832if(n.id != id) {8833n.data.$orn = opp;8834tag(n);8835}8836});8837})(rootNode);8838delete clickedNode.data.$orn;8839}8840this.root = id;8841this.clickedNode = clickedNode;8842this.graph.computeLevels(this.root, 0, "ignore");8843this.geom.setRightLevelToShow(clickedNode, canvas, {8844execHide: false,8845onShow: function(node) {8846if(!node.drawn) {8847node.drawn = true;8848node.setData('alpha', 1, 'end');8849node.setData('alpha', 0);8850node.pos.setc(clickedNode.pos.x, clickedNode.pos.y);8851}8852}8853});8854this.compute('end');8855this.busy = true;8856this.fx.animate({8857modes: ['linear', 'node-property:alpha'],8858onComplete: function() {8859that.busy = false;8860that.onClick(id, {8861onComplete: function() {8862onComplete && onComplete.onComplete();8863}8864});8865}8866});8867}88688869// delete previous orientations (if any)8870delete rootNode.data.$orns;88718872if(method == 'animate') {8873$setRoot.call(this);8874that.selectPath(clickedNode);8875} else if(method == 'replot') {8876$setRoot.call(this);8877this.select(this.root);8878}8879},88808881/*8882Method: addSubtree88838884Adds a subtree.88858886Parameters:8887subtree - (object) A JSON Tree object. See also <Loader.loadJSON>.8888method - (string) Set this to "animate" if you want to animate the tree after adding the subtree. You can also set this parameter to "replot" to just replot the subtree.8889onComplete - (optional|object) An action to perform after the animation (if any).88908891Example:88928893(start code js)8894st.addSubtree(json, 'animate', {8895onComplete: function() {8896alert('complete!');8897}8898});8899(end code)8900*/8901addSubtree: function(subtree, method, onComplete) {8902if(method == 'replot') {8903this.op.sum(subtree, $.extend({ type: 'replot' }, onComplete || {}));8904} else if (method == 'animate') {8905this.op.sum(subtree, $.extend({ type: 'fade:seq' }, onComplete || {}));8906}8907},89088909/*8910Method: removeSubtree89118912Removes a subtree.89138914Parameters:8915id - (string) The _id_ of the subtree to be removed.8916removeRoot - (boolean) Default's *false*. Remove the root of the subtree or only its subnodes.8917method - (string) Set this to "animate" if you want to animate the tree after removing the subtree. You can also set this parameter to "replot" to just replot the subtree.8918onComplete - (optional|object) An action to perform after the animation (if any).89198920Example:89218922(start code js)8923st.removeSubtree('idOfSubtreeToBeRemoved', false, 'animate', {8924onComplete: function() {8925alert('complete!');8926}8927});8928(end code)89298930*/8931removeSubtree: function(id, removeRoot, method, onComplete) {8932var node = this.graph.getNode(id), subids = [];8933node.eachLevel(+!removeRoot, false, function(n) {8934subids.push(n.id);8935});8936if(method == 'replot') {8937this.op.removeNode(subids, $.extend({ type: 'replot' }, onComplete || {}));8938} else if (method == 'animate') {8939this.op.removeNode(subids, $.extend({ type: 'fade:seq'}, onComplete || {}));8940}8941},89428943/*8944Method: select89458946Selects a node in the <ST> without performing an animation. Useful when selecting8947nodes which are currently hidden or deep inside the tree.89488949Parameters:8950id - (string) The id of the node to select.8951onComplete - (optional|object) an onComplete callback.89528953Example:8954(start code js)8955st.select('mynodeid', {8956onComplete: function() {8957alert('complete!');8958}8959});8960(end code)8961*/8962select: function(id, onComplete) {8963var group = this.group, geom = this.geom;8964var node= this.graph.getNode(id), canvas = this.canvas;8965var root = this.graph.getNode(this.root);8966var complete = $.merge(this.controller, onComplete);8967var that = this;89688969complete.onBeforeCompute(node);8970this.selectPath(node);8971this.clickedNode= node;8972this.requestNodes(node, {8973onComplete: function(){8974group.hide(group.prepare(getNodesToHide.call(that)), complete);8975geom.setRightLevelToShow(node, canvas);8976that.compute("current");8977that.graph.eachNode(function(n) {8978var pos = n.pos.getc(true);8979n.startPos.setc(pos.x, pos.y);8980n.endPos.setc(pos.x, pos.y);8981n.visited = false;8982});8983var offset = { x: complete.offsetX, y: complete.offsetY };8984that.geom.translate(node.endPos.add(offset).$scale(-1), ["start", "current", "end"]);8985group.show(getNodesToShow.call(that));8986that.plot();8987complete.onAfterCompute(that.clickedNode);8988complete.onComplete();8989}8990});8991},89928993/*8994Method: onClick89958996Animates the <ST> to center the node specified by *id*.89978998Parameters:89999000id - (string) A node id.9001options - (optional|object) A group of options and callbacks described below.9002onComplete - (object) An object callback called when the animation finishes.9003Move - (object) An object that has as properties _offsetX_ or _offsetY_ for adding some offset position to the centered node.90049005Example:90069007(start code js)9008st.onClick('mynodeid', {9009Move: {9010enable: true,9011offsetX: 30,9012offsetY: 59013},9014onComplete: function() {9015alert('yay!');9016}9017});9018(end code)90199020*/9021onClick: function (id, options) {9022var canvas = this.canvas, that = this, Geom = this.geom, config = this.config;9023var innerController = {9024Move: {9025enable: true,9026offsetX: config.offsetX || 0,9027offsetY: config.offsetY || 09028},9029setRightLevelToShowConfig: false,9030onBeforeRequest: $.empty,9031onBeforeContract: $.empty,9032onBeforeMove: $.empty,9033onBeforeExpand: $.empty9034};9035var complete = $.merge(this.controller, innerController, options);90369037if(!this.busy) {9038this.busy = true;9039var node = this.graph.getNode(id);9040this.selectPath(node, this.clickedNode);9041this.clickedNode = node;9042complete.onBeforeCompute(node);9043complete.onBeforeRequest(node);9044this.requestNodes(node, {9045onComplete: function() {9046complete.onBeforeContract(node);9047that.contract({9048onComplete: function() {9049Geom.setRightLevelToShow(node, canvas, complete.setRightLevelToShowConfig);9050complete.onBeforeMove(node);9051that.move(node, {9052Move: complete.Move,9053onComplete: function() {9054complete.onBeforeExpand(node);9055that.expand(node, {9056onComplete: function() {9057that.busy = false;9058complete.onAfterCompute(id);9059complete.onComplete();9060}9061}); // expand9062}9063}); // move9064}9065});// contract9066}9067});// request9068}9069}9070});90719072})();90739074$jit.ST.$extend = true;90759076/*9077Class: ST.Op90789079Custom extension of <Graph.Op>.90809081Extends:90829083All <Graph.Op> methods90849085See also:90869087<Graph.Op>90889089*/9090$jit.ST.Op = new Class({90919092Implements: Graph.Op90939094});90959096/*90979098Performs operations on group of nodes.90999100*/9101$jit.ST.Group = new Class({91029103initialize: function(viz) {9104this.viz = viz;9105this.canvas = viz.canvas;9106this.config = viz.config;9107this.animation = new Animation;9108this.nodes = null;9109},91109111/*91129113Calls the request method on the controller to request a subtree for each node.9114*/9115requestNodes: function(nodes, controller) {9116var counter = 0, len = nodes.length, nodeSelected = {};9117var complete = function() { controller.onComplete(); };9118var viz = this.viz;9119if(len == 0) complete();9120for(var i=0; i<len; i++) {9121nodeSelected[nodes[i].id] = nodes[i];9122controller.request(nodes[i].id, nodes[i]._level, {9123onComplete: function(nodeId, data) {9124if(data && data.children) {9125data.id = nodeId;9126viz.op.sum(data, { type: 'nothing' });9127}9128if(++counter == len) {9129viz.graph.computeLevels(viz.root, 0);9130complete();9131}9132}9133});9134}9135},91369137/*91389139Collapses group of nodes.9140*/9141contract: function(nodes, controller) {9142var viz = this.viz;9143var that = this;91449145nodes = this.prepare(nodes);9146this.animation.setOptions($.merge(controller, {9147$animating: false,9148compute: function(delta) {9149if(delta == 1) delta = 0.99;9150that.plotStep(1 - delta, controller, this.$animating);9151this.$animating = 'contract';9152},91539154complete: function() {9155that.hide(nodes, controller);9156}9157})).start();9158},91599160hide: function(nodes, controller) {9161var viz = this.viz;9162for(var i=0; i<nodes.length; i++) {9163// TODO nodes are requested on demand, but not9164// deleted when hidden. Would that be a good feature?9165// Currently that feature is buggy, so I'll turn it off9166// Actually this feature is buggy because trimming should take9167// place onAfterCompute and not right after collapsing nodes.9168if (true || !controller || !controller.request) {9169nodes[i].eachLevel(1, false, function(elem){9170if (elem.exist) {9171$.extend(elem, {9172'drawn': false,9173'exist': false9174});9175}9176});9177} else {9178var ids = [];9179nodes[i].eachLevel(1, false, function(n) {9180ids.push(n.id);9181});9182viz.op.removeNode(ids, { 'type': 'nothing' });9183viz.labels.clearLabels();9184}9185}9186controller.onComplete();9187},918891899190/*9191Expands group of nodes.9192*/9193expand: function(nodes, controller) {9194var that = this;9195this.show(nodes);9196this.animation.setOptions($.merge(controller, {9197$animating: false,9198compute: function(delta) {9199that.plotStep(delta, controller, this.$animating);9200this.$animating = 'expand';9201},92029203complete: function() {9204that.plotStep(undefined, controller, false);9205controller.onComplete();9206}9207})).start();92089209},92109211show: function(nodes) {9212var config = this.config;9213this.prepare(nodes);9214$.each(nodes, function(n) {9215// check for root nodes if multitree9216if(config.multitree && !('$orn' in n.data)) {9217delete n.data.$orns;9218var orns = ' ';9219n.eachSubnode(function(ch) {9220if(('$orn' in ch.data)9221&& orns.indexOf(ch.data.$orn) < 09222&& ch.exist && !ch.drawn) {9223orns += ch.data.$orn + ' ';9224}9225});9226n.data.$orns = orns;9227}9228n.eachLevel(0, config.levelsToShow, function(n) {9229if(n.exist) n.drawn = true;9230});9231});9232},92339234prepare: function(nodes) {9235this.nodes = this.getNodesWithChildren(nodes);9236return this.nodes;9237},92389239/*9240Filters an array of nodes leaving only nodes with children.9241*/9242getNodesWithChildren: function(nodes) {9243var ans = [], config = this.config, root = this.viz.root;9244nodes.sort(function(a, b) { return (a._depth <= b._depth) - (a._depth >= b._depth); });9245for(var i=0; i<nodes.length; i++) {9246if(nodes[i].anySubnode("exist")) {9247for (var j = i+1, desc = false; !desc && j < nodes.length; j++) {9248if(!config.multitree || '$orn' in nodes[j].data) {9249desc = desc || nodes[i].isDescendantOf(nodes[j].id);9250}9251}9252if(!desc) ans.push(nodes[i]);9253}9254}9255return ans;9256},92579258plotStep: function(delta, controller, animating) {9259var viz = this.viz,9260config = this.config,9261canvas = viz.canvas,9262ctx = canvas.getCtx(),9263nodes = this.nodes;9264var i, node;9265// hide nodes that are meant to be collapsed/expanded9266var nds = {};9267for(i=0; i<nodes.length; i++) {9268node = nodes[i];9269nds[node.id] = [];9270var root = config.multitree && !('$orn' in node.data);9271var orns = root && node.data.$orns;9272node.eachSubgraph(function(n) {9273// Cleanup9274// special check for root node subnodes when9275// multitree is checked.9276if(root && orns && orns.indexOf(n.data.$orn) > 09277&& n.drawn) {9278n.drawn = false;9279nds[node.id].push(n);9280} else if((!root || !orns) && n.drawn) {9281n.drawn = false;9282nds[node.id].push(n);9283}9284});9285node.drawn = true;9286}9287// plot the whole (non-scaled) tree9288if(nodes.length > 0) viz.fx.plot();9289// show nodes that were previously hidden9290for(i in nds) {9291$.each(nds[i], function(n) { n.drawn = true; });9292}9293// plot each scaled subtree9294for(i=0; i<nodes.length; i++) {9295node = nodes[i];9296ctx.save();9297viz.fx.plotSubtree(node, controller, delta, animating);9298ctx.restore();9299}9300},93019302getSiblings: function(nodes) {9303var siblings = {};9304$.each(nodes, function(n) {9305var par = n.getParents();9306if (par.length == 0) {9307siblings[n.id] = [n];9308} else {9309var ans = [];9310par[0].eachSubnode(function(sn) {9311ans.push(sn);9312});9313siblings[n.id] = ans;9314}9315});9316return siblings;9317}9318});93199320/*9321ST.Geom93229323Performs low level geometrical computations.93249325Access:93269327This instance can be accessed with the _geom_ parameter of the st instance created.93289329Example:93309331(start code js)9332var st = new ST(canvas, config);9333st.geom.translate //or can also call any other <ST.Geom> method9334(end code)93359336*/93379338$jit.ST.Geom = new Class({9339Implements: Graph.Geom,9340/*9341Changes the tree current orientation to the one specified.93429343You should usually use <ST.switchPosition> instead.9344*/9345switchOrientation: function(orn) {9346this.config.orientation = orn;9347},93489349/*9350Makes a value dispatch according to the current layout9351Works like a CSS property, either _top-right-bottom-left_ or _top|bottom - left|right_.9352*/9353dispatch: function() {9354// TODO(nico) should store Array.prototype.slice.call somewhere.9355var args = Array.prototype.slice.call(arguments);9356var s = args.shift(), len = args.length;9357var val = function(a) { return typeof a == 'function'? a() : a; };9358if(len == 2) {9359return (s == "top" || s == "bottom")? val(args[0]) : val(args[1]);9360} else if(len == 4) {9361switch(s) {9362case "top": return val(args[0]);9363case "right": return val(args[1]);9364case "bottom": return val(args[2]);9365case "left": return val(args[3]);9366}9367}9368return undefined;9369},93709371/*9372Returns label height or with, depending on the tree current orientation.9373*/9374getSize: function(n, invert) {9375var data = n.data, config = this.config;9376var siblingOffset = config.siblingOffset;9377var s = (config.multitree9378&& ('$orn' in data)9379&& data.$orn) || config.orientation;9380var w = n.getData('width') + siblingOffset;9381var h = n.getData('height') + siblingOffset;9382if(!invert)9383return this.dispatch(s, h, w);9384else9385return this.dispatch(s, w, h);9386},93879388/*9389Calculates a subtree base size. This is an utility function used by _getBaseSize_9390*/9391getTreeBaseSize: function(node, level, leaf) {9392var size = this.getSize(node, true), baseHeight = 0, that = this;9393if(leaf(level, node)) return size;9394if(level === 0) return 0;9395node.eachSubnode(function(elem) {9396baseHeight += that.getTreeBaseSize(elem, level -1, leaf);9397});9398return (size > baseHeight? size : baseHeight) + this.config.subtreeOffset;9399},940094019402/*9403getEdge94049405Returns a Complex instance with the begin or end position of the edge to be plotted.94069407Parameters:94089409node - A <Graph.Node> that is connected to this edge.9410type - Returns the begin or end edge position. Possible values are 'begin' or 'end'.94119412Returns:94139414A <Complex> number specifying the begin or end position.9415*/9416getEdge: function(node, type, s) {9417var $C = function(a, b) {9418return function(){9419return node.pos.add(new Complex(a, b));9420};9421};9422var dim = this.node;9423var w = node.getData('width');9424var h = node.getData('height');94259426if(type == 'begin') {9427if(dim.align == "center") {9428return this.dispatch(s, $C(0, h/2), $C(-w/2, 0),9429$C(0, -h/2),$C(w/2, 0));9430} else if(dim.align == "left") {9431return this.dispatch(s, $C(0, h), $C(0, 0),9432$C(0, 0), $C(w, 0));9433} else if(dim.align == "right") {9434return this.dispatch(s, $C(0, 0), $C(-w, 0),9435$C(0, -h),$C(0, 0));9436} else throw "align: not implemented";943794389439} else if(type == 'end') {9440if(dim.align == "center") {9441return this.dispatch(s, $C(0, -h/2), $C(w/2, 0),9442$C(0, h/2), $C(-w/2, 0));9443} else if(dim.align == "left") {9444return this.dispatch(s, $C(0, 0), $C(w, 0),9445$C(0, h), $C(0, 0));9446} else if(dim.align == "right") {9447return this.dispatch(s, $C(0, -h),$C(0, 0),9448$C(0, 0), $C(-w, 0));9449} else throw "align: not implemented";9450}9451},94529453/*9454Adjusts the tree position due to canvas scaling or translation.9455*/9456getScaledTreePosition: function(node, scale) {9457var dim = this.node;9458var w = node.getData('width');9459var h = node.getData('height');9460var s = (this.config.multitree9461&& ('$orn' in node.data)9462&& node.data.$orn) || this.config.orientation;94639464var $C = function(a, b) {9465return function(){9466return node.pos.add(new Complex(a, b)).$scale(1 - scale);9467};9468};9469if(dim.align == "left") {9470return this.dispatch(s, $C(0, h), $C(0, 0),9471$C(0, 0), $C(w, 0));9472} else if(dim.align == "center") {9473return this.dispatch(s, $C(0, h / 2), $C(-w / 2, 0),9474$C(0, -h / 2),$C(w / 2, 0));9475} else if(dim.align == "right") {9476return this.dispatch(s, $C(0, 0), $C(-w, 0),9477$C(0, -h),$C(0, 0));9478} else throw "align: not implemented";9479},94809481/*9482treeFitsInCanvas94839484Returns a Boolean if the current subtree fits in canvas.94859486Parameters:94879488node - A <Graph.Node> which is the current root of the subtree.9489canvas - The <Canvas> object.9490level - The depth of the subtree to be considered.9491*/9492treeFitsInCanvas: function(node, canvas, level) {9493var csize = canvas.getSize();9494var s = (this.config.multitree9495&& ('$orn' in node.data)9496&& node.data.$orn) || this.config.orientation;94979498var size = this.dispatch(s, csize.width, csize.height);9499var baseSize = this.getTreeBaseSize(node, level, function(level, node) {9500return level === 0 || !node.anySubnode();9501});9502return (baseSize < size);9503}9504});95059506/*9507Class: ST.Plot95089509Custom extension of <Graph.Plot>.95109511Extends:95129513All <Graph.Plot> methods95149515See also:95169517<Graph.Plot>95189519*/9520$jit.ST.Plot = new Class({95219522Implements: Graph.Plot,95239524/*9525Plots a subtree from the spacetree.9526*/9527plotSubtree: function(node, opt, scale, animating) {9528var viz = this.viz, canvas = viz.canvas, config = viz.config;9529scale = Math.min(Math.max(0.001, scale), 1);9530if(scale >= 0) {9531node.drawn = false;9532var ctx = canvas.getCtx();9533var diff = viz.geom.getScaledTreePosition(node, scale);9534ctx.translate(diff.x, diff.y);9535ctx.scale(scale, scale);9536}9537this.plotTree(node, $.merge(opt, {9538'withLabels': true,9539'hideLabels': !!scale,9540'plotSubtree': function(n, ch) {9541var root = config.multitree && !('$orn' in node.data);9542var orns = root && node.getData('orns');9543return !root || orns.indexOf(node.getData('orn')) > -1;9544}9545}), animating);9546if(scale >= 0) node.drawn = true;9547},95489549/*9550Method: getAlignedPos95519552Returns a *x, y* object with the position of the top/left corner of a <ST> node.95539554Parameters:95559556pos - (object) A <Graph.Node> position.9557width - (number) The width of the node.9558height - (number) The height of the node.95599560*/9561getAlignedPos: function(pos, width, height) {9562var nconfig = this.node;9563var square, orn;9564if(nconfig.align == "center") {9565square = {9566x: pos.x - width / 2,9567y: pos.y - height / 29568};9569} else if (nconfig.align == "left") {9570orn = this.config.orientation;9571if(orn == "bottom" || orn == "top") {9572square = {9573x: pos.x - width / 2,9574y: pos.y9575};9576} else {9577square = {9578x: pos.x,9579y: pos.y - height / 29580};9581}9582} else if(nconfig.align == "right") {9583orn = this.config.orientation;9584if(orn == "bottom" || orn == "top") {9585square = {9586x: pos.x - width / 2,9587y: pos.y - height9588};9589} else {9590square = {9591x: pos.x - width,9592y: pos.y - height / 29593};9594}9595} else throw "align: not implemented";95969597return square;9598},95999600getOrientation: function(adj) {9601var config = this.config;9602var orn = config.orientation;96039604if(config.multitree) {9605var nodeFrom = adj.nodeFrom;9606var nodeTo = adj.nodeTo;9607orn = (('$orn' in nodeFrom.data)9608&& nodeFrom.data.$orn)9609|| (('$orn' in nodeTo.data)9610&& nodeTo.data.$orn);9611}96129613return orn;9614}9615});96169617/*9618Class: ST.Label96199620Custom extension of <Graph.Label>.9621Contains custom <Graph.Label.SVG>, <Graph.Label.HTML> and <Graph.Label.Native> extensions.96229623Extends:96249625All <Graph.Label> methods and subclasses.96269627See also:96289629<Graph.Label>, <Graph.Label.Native>, <Graph.Label.HTML>, <Graph.Label.SVG>.9630*/9631$jit.ST.Label = {};96329633/*9634ST.Label.Native96359636Custom extension of <Graph.Label.Native>.96379638Extends:96399640All <Graph.Label.Native> methods96419642See also:96439644<Graph.Label.Native>9645*/9646$jit.ST.Label.Native = new Class({9647Implements: Graph.Label.Native,96489649renderLabel: function(canvas, node, controller) {9650var ctx = canvas.getCtx(),9651coord = node.pos.getc(true),9652width = node.getData('width'),9653height = node.getData('height'),9654pos = this.viz.fx.getAlignedPos(coord, width, height);9655ctx.fillText(node.name, pos.x + width / 2, pos.y + height / 2);9656}9657});96589659$jit.ST.Label.DOM = new Class({9660Implements: Graph.Label.DOM,96619662/*9663placeLabel96649665Overrides abstract method placeLabel in <Graph.Plot>.96669667Parameters:96689669tag - A DOM label element.9670node - A <Graph.Node>.9671controller - A configuration/controller object passed to the visualization.96729673*/9674placeLabel: function(tag, node, controller) {9675var pos = node.pos.getc(true),9676config = this.viz.config,9677dim = config.Node,9678canvas = this.viz.canvas,9679w = node.getData('width'),9680h = node.getData('height'),9681radius = canvas.getSize(),9682labelPos, orn;96839684var ox = canvas.translateOffsetX,9685oy = canvas.translateOffsetY,9686sx = canvas.scaleOffsetX,9687sy = canvas.scaleOffsetY,9688posx = pos.x * sx + ox,9689posy = pos.y * sy + oy;96909691if(dim.align == "center") {9692labelPos= {9693x: Math.round(posx - w / 2 + radius.width/2),9694y: Math.round(posy - h / 2 + radius.height/2)9695};9696} else if (dim.align == "left") {9697orn = config.orientation;9698if(orn == "bottom" || orn == "top") {9699labelPos= {9700x: Math.round(posx - w / 2 + radius.width/2),9701y: Math.round(posy + radius.height/2)9702};9703} else {9704labelPos= {9705x: Math.round(posx + radius.width/2),9706y: Math.round(posy - h / 2 + radius.height/2)9707};9708}9709} else if(dim.align == "right") {9710orn = config.orientation;9711if(orn == "bottom" || orn == "top") {9712labelPos= {9713x: Math.round(posx - w / 2 + radius.width/2),9714y: Math.round(posy - h + radius.height/2)9715};9716} else {9717labelPos= {9718x: Math.round(posx - w + radius.width/2),9719y: Math.round(posy - h / 2 + radius.height/2)9720};9721}9722} else throw "align: not implemented";97239724var style = tag.style;9725style.left = labelPos.x + 'px';9726style.top = labelPos.y + 'px';9727style.display = this.fitsInCanvas(labelPos, canvas)? '' : 'none';9728controller.onPlaceLabel(tag, node);9729}9730});97319732/*9733ST.Label.SVG97349735Custom extension of <Graph.Label.SVG>.97369737Extends:97389739All <Graph.Label.SVG> methods97409741See also:97429743<Graph.Label.SVG>9744*/9745$jit.ST.Label.SVG = new Class({9746Implements: [$jit.ST.Label.DOM, Graph.Label.SVG],97479748initialize: function(viz) {9749this.viz = viz;9750}9751});97529753/*9754ST.Label.HTML97559756Custom extension of <Graph.Label.HTML>.97579758Extends:97599760All <Graph.Label.HTML> methods.97619762See also:97639764<Graph.Label.HTML>97659766*/9767$jit.ST.Label.HTML = new Class({9768Implements: [$jit.ST.Label.DOM, Graph.Label.HTML],97699770initialize: function(viz) {9771this.viz = viz;9772}9773});977497759776/*9777Class: ST.Plot.NodeTypes97789779This class contains a list of <Graph.Node> built-in types.9780Node types implemented are 'none', 'circle', 'rectangle', 'ellipse' and 'square'.97819782You can add your custom node types, customizing your visualization to the extreme.97839784Example:97859786(start code js)9787ST.Plot.NodeTypes.implement({9788'mySpecialType': {9789'render': function(node, canvas) {9790//print your custom node to canvas9791},9792//optional9793'contains': function(node, pos) {9794//return true if pos is inside the node or false otherwise9795}9796}9797});9798(end code)97999800*/9801$jit.ST.Plot.NodeTypes = new Class({9802'none': {9803'render': $.empty,9804'contains': $.lambda(false)9805},9806'circle': {9807'render': function(node, canvas) {9808var dim = node.getData('dim'),9809pos = this.getAlignedPos(node.pos.getc(true), dim, dim),9810dim2 = dim/2;9811this.nodeHelper.circle.render('fill', {x:pos.x+dim2, y:pos.y+dim2}, dim2, canvas);9812},9813'contains': function(node, pos) {9814var dim = node.getData('dim'),9815npos = this.getAlignedPos(node.pos.getc(true), dim, dim),9816dim2 = dim/2;9817this.nodeHelper.circle.contains({x:npos.x+dim2, y:npos.y+dim2}, pos, dim2);9818}9819},9820'square': {9821'render': function(node, canvas) {9822var dim = node.getData('dim'),9823dim2 = dim/2,9824pos = this.getAlignedPos(node.pos.getc(true), dim, dim);9825this.nodeHelper.square.render('fill', {x:pos.x+dim2, y:pos.y+dim2}, dim2, canvas);9826},9827'contains': function(node, pos) {9828var dim = node.getData('dim'),9829npos = this.getAlignedPos(node.pos.getc(true), dim, dim),9830dim2 = dim/2;9831this.nodeHelper.square.contains({x:npos.x+dim2, y:npos.y+dim2}, pos, dim2);9832}9833},9834'ellipse': {9835'render': function(node, canvas) {9836var width = node.getData('width'),9837height = node.getData('height'),9838pos = this.getAlignedPos(node.pos.getc(true), width, height);9839this.nodeHelper.ellipse.render('fill', {x:pos.x+width/2, y:pos.y+height/2}, width, height, canvas);9840},9841'contains': function(node, pos) {9842var width = node.getData('width'),9843height = node.getData('height'),9844npos = this.getAlignedPos(node.pos.getc(true), width, height);9845this.nodeHelper.ellipse.contains({x:npos.x+width/2, y:npos.y+height/2}, pos, width, height);9846}9847},9848'rectangle': {9849'render': function(node, canvas) {9850var width = node.getData('width'),9851height = node.getData('height'),9852pos = this.getAlignedPos(node.pos.getc(true), width, height);9853this.nodeHelper.rectangle.render('fill', {x:pos.x+width/2, y:pos.y+height/2}, width, height, canvas);9854},9855'contains': function(node, pos) {9856var width = node.getData('width'),9857height = node.getData('height'),9858npos = this.getAlignedPos(node.pos.getc(true), width, height);9859this.nodeHelper.rectangle.contains({x:npos.x+width/2, y:npos.y+height/2}, pos, width, height);9860}9861}9862});98639864/*9865Class: ST.Plot.EdgeTypes98669867This class contains a list of <Graph.Adjacence> built-in types.9868Edge types implemented are 'none', 'line', 'arrow', 'quadratic:begin', 'quadratic:end', 'bezier'.98699870You can add your custom edge types, customizing your visualization to the extreme.98719872Example:98739874(start code js)9875ST.Plot.EdgeTypes.implement({9876'mySpecialType': {9877'render': function(adj, canvas) {9878//print your custom edge to canvas9879},9880//optional9881'contains': function(adj, pos) {9882//return true if pos is inside the arc or false otherwise9883}9884}9885});9886(end code)98879888*/9889$jit.ST.Plot.EdgeTypes = new Class({9890'none': $.empty,9891'line': {9892'render': function(adj, canvas) {9893var orn = this.getOrientation(adj),9894nodeFrom = adj.nodeFrom,9895nodeTo = adj.nodeTo,9896rel = nodeFrom._depth < nodeTo._depth,9897from = this.viz.geom.getEdge(rel? nodeFrom:nodeTo, 'begin', orn),9898to = this.viz.geom.getEdge(rel? nodeTo:nodeFrom, 'end', orn);9899this.edgeHelper.line.render(from, to, canvas);9900},9901'contains': function(adj, pos) {9902var orn = this.getOrientation(adj),9903nodeFrom = adj.nodeFrom,9904nodeTo = adj.nodeTo,9905rel = nodeFrom._depth < nodeTo._depth,9906from = this.viz.geom.getEdge(rel? nodeFrom:nodeTo, 'begin', orn),9907to = this.viz.geom.getEdge(rel? nodeTo:nodeFrom, 'end', orn);9908return this.edgeHelper.line.contains(from, to, pos, this.edge.epsilon);9909}9910},9911'arrow': {9912'render': function(adj, canvas) {9913var orn = this.getOrientation(adj),9914node = adj.nodeFrom,9915child = adj.nodeTo,9916dim = adj.getData('dim'),9917from = this.viz.geom.getEdge(node, 'begin', orn),9918to = this.viz.geom.getEdge(child, 'end', orn),9919direction = adj.data.$direction,9920inv = (direction && direction.length>1 && direction[0] != node.id);9921this.edgeHelper.arrow.render(from, to, dim, inv, canvas);9922},9923'contains': function(adj, pos) {9924var orn = this.getOrientation(adj),9925nodeFrom = adj.nodeFrom,9926nodeTo = adj.nodeTo,9927rel = nodeFrom._depth < nodeTo._depth,9928from = this.viz.geom.getEdge(rel? nodeFrom:nodeTo, 'begin', orn),9929to = this.viz.geom.getEdge(rel? nodeTo:nodeFrom, 'end', orn);9930return this.edgeHelper.arrow.contains(from, to, pos, this.edge.epsilon);9931}9932},9933'quadratic:begin': {9934'render': function(adj, canvas) {9935var orn = this.getOrientation(adj);9936var nodeFrom = adj.nodeFrom,9937nodeTo = adj.nodeTo,9938rel = nodeFrom._depth < nodeTo._depth,9939begin = this.viz.geom.getEdge(rel? nodeFrom:nodeTo, 'begin', orn),9940end = this.viz.geom.getEdge(rel? nodeTo:nodeFrom, 'end', orn),9941dim = adj.getData('dim'),9942ctx = canvas.getCtx();9943ctx.beginPath();9944ctx.moveTo(begin.x, begin.y);9945switch(orn) {9946case "left":9947ctx.quadraticCurveTo(begin.x + dim, begin.y, end.x, end.y);9948break;9949case "right":9950ctx.quadraticCurveTo(begin.x - dim, begin.y, end.x, end.y);9951break;9952case "top":9953ctx.quadraticCurveTo(begin.x, begin.y + dim, end.x, end.y);9954break;9955case "bottom":9956ctx.quadraticCurveTo(begin.x, begin.y - dim, end.x, end.y);9957break;9958}9959ctx.stroke();9960}9961},9962'quadratic:end': {9963'render': function(adj, canvas) {9964var orn = this.getOrientation(adj);9965var nodeFrom = adj.nodeFrom,9966nodeTo = adj.nodeTo,9967rel = nodeFrom._depth < nodeTo._depth,9968begin = this.viz.geom.getEdge(rel? nodeFrom:nodeTo, 'begin', orn),9969end = this.viz.geom.getEdge(rel? nodeTo:nodeFrom, 'end', orn),9970dim = adj.getData('dim'),9971ctx = canvas.getCtx();9972ctx.beginPath();9973ctx.moveTo(begin.x, begin.y);9974switch(orn) {9975case "left":9976ctx.quadraticCurveTo(end.x - dim, end.y, end.x, end.y);9977break;9978case "right":9979ctx.quadraticCurveTo(end.x + dim, end.y, end.x, end.y);9980break;9981case "top":9982ctx.quadraticCurveTo(end.x, end.y - dim, end.x, end.y);9983break;9984case "bottom":9985ctx.quadraticCurveTo(end.x, end.y + dim, end.x, end.y);9986break;9987}9988ctx.stroke();9989}9990},9991'bezier': {9992'render': function(adj, canvas) {9993var orn = this.getOrientation(adj),9994nodeFrom = adj.nodeFrom,9995nodeTo = adj.nodeTo,9996rel = nodeFrom._depth < nodeTo._depth,9997begin = this.viz.geom.getEdge(rel? nodeFrom:nodeTo, 'begin', orn),9998end = this.viz.geom.getEdge(rel? nodeTo:nodeFrom, 'end', orn),9999dim = adj.getData('dim'),10000ctx = canvas.getCtx();10001ctx.beginPath();10002ctx.moveTo(begin.x, begin.y);10003switch(orn) {10004case "left":10005ctx.bezierCurveTo(begin.x + dim, begin.y, end.x - dim, end.y, end.x, end.y);10006break;10007case "right":10008ctx.bezierCurveTo(begin.x - dim, begin.y, end.x + dim, end.y, end.x, end.y);10009break;10010case "top":10011ctx.bezierCurveTo(begin.x, begin.y + dim, end.x, end.y - dim, end.x, end.y);10012break;10013case "bottom":10014ctx.bezierCurveTo(begin.x, begin.y - dim, end.x, end.y + dim, end.x, end.y);10015break;10016}10017ctx.stroke();10018}10019}10020});10021100221002310024/*10025* File: AreaChart.js10026*10027*/1002810029$jit.ST.Plot.NodeTypes.implement({10030'areachart-stacked' : {10031'render' : function(node, canvas) {10032var pos = node.pos.getc(true),10033width = node.getData('width'),10034height = node.getData('height'),10035algnPos = this.getAlignedPos(pos, width, height),10036x = algnPos.x, y = algnPos.y,10037stringArray = node.getData('stringArray'),10038dimArray = node.getData('dimArray'),10039valArray = node.getData('valueArray'),10040valLeft = $.reduce(valArray, function(x, y) { return x + y[0]; }, 0),10041valRight = $.reduce(valArray, function(x, y) { return x + y[1]; }, 0),10042colorArray = node.getData('colorArray'),10043colorLength = colorArray.length,10044config = node.getData('config'),10045gradient = node.getData('gradient'),10046showLabels = config.showLabels,10047aggregates = config.showAggregates,10048label = config.Label,10049prev = node.getData('prev');1005010051var ctx = canvas.getCtx(), border = node.getData('border');10052if (colorArray && dimArray && stringArray) {10053for (var i=0, l=dimArray.length, acumLeft=0, acumRight=0, valAcum=0; i<l; i++) {10054ctx.fillStyle = ctx.strokeStyle = colorArray[i % colorLength];10055ctx.save();10056if(gradient && (dimArray[i][0] > 0 || dimArray[i][1] > 0)) {10057var h1 = acumLeft + dimArray[i][0],10058h2 = acumRight + dimArray[i][1],10059alpha = Math.atan((h2 - h1) / width),10060delta = 55;10061var linear = ctx.createLinearGradient(x + width/2,10062y - (h1 + h2)/2,10063x + width/2 + delta * Math.sin(alpha),10064y - (h1 + h2)/2 + delta * Math.cos(alpha));10065var color = $.rgbToHex($.map($.hexToRgb(colorArray[i % colorLength].slice(1)),10066function(v) { return (v * 0.85) >> 0; }));10067linear.addColorStop(0, colorArray[i % colorLength]);10068linear.addColorStop(1, color);10069ctx.fillStyle = linear;10070}10071ctx.beginPath();10072ctx.moveTo(x, y - acumLeft);10073ctx.lineTo(x + width, y - acumRight);10074ctx.lineTo(x + width, y - acumRight - dimArray[i][1]);10075ctx.lineTo(x, y - acumLeft - dimArray[i][0]);10076ctx.lineTo(x, y - acumLeft);10077ctx.fill();10078ctx.restore();10079if(border) {10080var strong = border.name == stringArray[i];10081var perc = strong? 0.7 : 0.8;10082var color = $.rgbToHex($.map($.hexToRgb(colorArray[i % colorLength].slice(1)),10083function(v) { return (v * perc) >> 0; }));10084ctx.strokeStyle = color;10085ctx.lineWidth = strong? 4 : 1;10086ctx.save();10087ctx.beginPath();10088if(border.index === 0) {10089ctx.moveTo(x, y - acumLeft);10090ctx.lineTo(x, y - acumLeft - dimArray[i][0]);10091} else {10092ctx.moveTo(x + width, y - acumRight);10093ctx.lineTo(x + width, y - acumRight - dimArray[i][1]);10094}10095ctx.stroke();10096ctx.restore();10097}10098acumLeft += (dimArray[i][0] || 0);10099acumRight += (dimArray[i][1] || 0);1010010101if(dimArray[i][0] > 0)10102valAcum += (valArray[i][0] || 0);10103}10104if(prev && label.type == 'Native') {10105ctx.save();10106ctx.beginPath();10107ctx.fillStyle = ctx.strokeStyle = label.color;10108ctx.font = label.style + ' ' + label.size + 'px ' + label.family;10109ctx.textAlign = 'center';10110ctx.textBaseline = 'middle';10111var aggValue = aggregates(node.name, valLeft, valRight, node, valAcum);10112if(aggValue !== false) {10113ctx.fillText(aggValue !== true? aggValue : valAcum, x, y - acumLeft - config.labelOffset - label.size/2, width);10114}10115if(showLabels(node.name, valLeft, valRight, node)) {10116ctx.fillText(node.name, x, y + label.size/2 + config.labelOffset);10117}10118ctx.restore();10119}10120}10121},10122'contains': function(node, mpos) {10123var pos = node.pos.getc(true),10124width = node.getData('width'),10125height = node.getData('height'),10126algnPos = this.getAlignedPos(pos, width, height),10127x = algnPos.x, y = algnPos.y,10128dimArray = node.getData('dimArray'),10129rx = mpos.x - x;10130//bounding box check10131if(mpos.x < x || mpos.x > x + width10132|| mpos.y > y || mpos.y < y - height) {10133return false;10134}10135//deep check10136for(var i=0, l=dimArray.length, lAcum=y, rAcum=y; i<l; i++) {10137var dimi = dimArray[i];10138lAcum -= dimi[0];10139rAcum -= dimi[1];10140var intersec = lAcum + (rAcum - lAcum) * rx / width;10141if(mpos.y >= intersec) {10142var index = +(rx > width/2);10143return {10144'name': node.getData('stringArray')[i],10145'color': node.getData('colorArray')[i],10146'value': node.getData('valueArray')[i][index],10147'index': index10148};10149}10150}10151return false;10152}10153}10154});1015510156/*10157Class: AreaChart1015810159A visualization that displays stacked area charts.1016010161Constructor Options:1016210163See <Options.AreaChart>.1016410165*/10166$jit.AreaChart = new Class({10167st: null,10168colors: ["#416D9C", "#70A35E", "#EBB056", "#C74243", "#83548B", "#909291", "#557EAA"],10169selected: {},10170busy: false,1017110172initialize: function(opt) {10173this.controller = this.config =10174$.merge(Options("Canvas", "Margin", "Label", "AreaChart"), {10175Label: { type: 'Native' }10176}, opt);10177//set functions for showLabels and showAggregates10178var showLabels = this.config.showLabels,10179typeLabels = $.type(showLabels),10180showAggregates = this.config.showAggregates,10181typeAggregates = $.type(showAggregates);10182this.config.showLabels = typeLabels == 'function'? showLabels : $.lambda(showLabels);10183this.config.showAggregates = typeAggregates == 'function'? showAggregates : $.lambda(showAggregates);1018410185this.initializeViz();10186},1018710188initializeViz: function() {10189var config = this.config,10190that = this,10191nodeType = config.type.split(":")[0],10192nodeLabels = {};1019310194var delegate = new $jit.ST({10195injectInto: config.injectInto,10196width: config.width,10197height: config.height,10198orientation: "bottom",10199levelDistance: 0,10200siblingOffset: 0,10201subtreeOffset: 0,10202withLabels: config.Label.type != 'Native',10203useCanvas: config.useCanvas,10204Label: {10205type: config.Label.type10206},10207Node: {10208overridable: true,10209type: 'areachart-' + nodeType,10210align: 'left',10211width: 1,10212height: 110213},10214Edge: {10215type: 'none'10216},10217Tips: {10218enable: config.Tips.enable,10219type: 'Native',10220force: true,10221onShow: function(tip, node, contains) {10222var elem = contains;10223config.Tips.onShow(tip, elem, node);10224}10225},10226Events: {10227enable: true,10228type: 'Native',10229onClick: function(node, eventInfo, evt) {10230if(!config.filterOnClick && !config.Events.enable) return;10231var elem = eventInfo.getContains();10232if(elem) config.filterOnClick && that.filter(elem.name);10233config.Events.enable && config.Events.onClick(elem, eventInfo, evt);10234},10235onRightClick: function(node, eventInfo, evt) {10236if(!config.restoreOnRightClick) return;10237that.restore();10238},10239onMouseMove: function(node, eventInfo, evt) {10240if(!config.selectOnHover) return;10241if(node) {10242var elem = eventInfo.getContains();10243that.select(node.id, elem.name, elem.index);10244} else {10245that.select(false, false, false);10246}10247}10248},10249onCreateLabel: function(domElement, node) {10250var labelConf = config.Label,10251valueArray = node.getData('valueArray'),10252acumLeft = $.reduce(valueArray, function(x, y) { return x + y[0]; }, 0),10253acumRight = $.reduce(valueArray, function(x, y) { return x + y[1]; }, 0);10254if(node.getData('prev')) {10255var nlbs = {10256wrapper: document.createElement('div'),10257aggregate: document.createElement('div'),10258label: document.createElement('div')10259};10260var wrapper = nlbs.wrapper,10261label = nlbs.label,10262aggregate = nlbs.aggregate,10263wrapperStyle = wrapper.style,10264labelStyle = label.style,10265aggregateStyle = aggregate.style;10266//store node labels10267nodeLabels[node.id] = nlbs;10268//append labels10269wrapper.appendChild(label);10270wrapper.appendChild(aggregate);10271if(!config.showLabels(node.name, acumLeft, acumRight, node)) {10272label.style.display = 'none';10273}10274if(!config.showAggregates(node.name, acumLeft, acumRight, node)) {10275aggregate.style.display = 'none';10276}10277wrapperStyle.position = 'relative';10278wrapperStyle.overflow = 'visible';10279wrapperStyle.fontSize = labelConf.size + 'px';10280wrapperStyle.fontFamily = labelConf.family;10281wrapperStyle.color = labelConf.color;10282wrapperStyle.textAlign = 'center';10283aggregateStyle.position = labelStyle.position = 'absolute';1028410285domElement.style.width = node.getData('width') + 'px';10286domElement.style.height = node.getData('height') + 'px';10287label.innerHTML = node.name;1028810289domElement.appendChild(wrapper);10290}10291},10292onPlaceLabel: function(domElement, node) {10293if(!node.getData('prev')) return;10294var labels = nodeLabels[node.id],10295wrapperStyle = labels.wrapper.style,10296labelStyle = labels.label.style,10297aggregateStyle = labels.aggregate.style,10298width = node.getData('width'),10299height = node.getData('height'),10300dimArray = node.getData('dimArray'),10301valArray = node.getData('valueArray'),10302acumLeft = $.reduce(valArray, function(x, y) { return x + y[0]; }, 0),10303acumRight = $.reduce(valArray, function(x, y) { return x + y[1]; }, 0),10304font = parseInt(wrapperStyle.fontSize, 10),10305domStyle = domElement.style;1030610307if(dimArray && valArray) {10308if(config.showLabels(node.name, acumLeft, acumRight, node)) {10309labelStyle.display = '';10310} else {10311labelStyle.display = 'none';10312}10313var aggValue = config.showAggregates(node.name, acumLeft, acumRight, node);10314if(aggValue !== false) {10315aggregateStyle.display = '';10316} else {10317aggregateStyle.display = 'none';10318}10319wrapperStyle.width = aggregateStyle.width = labelStyle.width = domElement.style.width = width + 'px';10320aggregateStyle.left = labelStyle.left = -width/2 + 'px';10321for(var i=0, l=valArray.length, acum=0, leftAcum=0; i<l; i++) {10322if(dimArray[i][0] > 0) {10323acum+= valArray[i][0];10324leftAcum+= dimArray[i][0];10325}10326}10327aggregateStyle.top = (-font - config.labelOffset) + 'px';10328labelStyle.top = (config.labelOffset + leftAcum) + 'px';10329domElement.style.top = parseInt(domElement.style.top, 10) - leftAcum + 'px';10330domElement.style.height = wrapperStyle.height = leftAcum + 'px';10331labels.aggregate.innerHTML = aggValue !== true? aggValue : acum;10332}10333}10334});1033510336var size = delegate.canvas.getSize(),10337margin = config.Margin;10338delegate.config.offsetY = -size.height/2 + margin.bottom10339+ (config.showLabels && (config.labelOffset + config.Label.size));10340delegate.config.offsetX = (margin.right - margin.left)/2;10341this.delegate = delegate;10342this.canvas = this.delegate.canvas;10343},1034410345/*10346Method: loadJSON1034710348Loads JSON data into the visualization.1034910350Parameters:1035110352json - The JSON data format. This format is described in <http://blog.thejit.org/2010/04/24/new-javascript-infovis-toolkit-visualizations/#json-data-format>.1035310354Example:10355(start code js)10356var areaChart = new $jit.AreaChart(options);10357areaChart.loadJSON(json);10358(end code)10359*/10360loadJSON: function(json) {10361var prefix = $.time(),10362ch = [],10363delegate = this.delegate,10364name = $.splat(json.label),10365color = $.splat(json.color || this.colors),10366config = this.config,10367gradient = !!config.type.split(":")[1],10368animate = config.animate;1036910370for(var i=0, values=json.values, l=values.length; i<l-1; i++) {10371var val = values[i], prev = values[i-1], next = values[i+1];10372var valLeft = $.splat(values[i].values), valRight = $.splat(values[i+1].values);10373var valArray = $.zip(valLeft, valRight);10374var acumLeft = 0, acumRight = 0;10375ch.push({10376'id': prefix + val.label,10377'name': val.label,10378'data': {10379'value': valArray,10380'$valueArray': valArray,10381'$colorArray': color,10382'$stringArray': name,10383'$next': next.label,10384'$prev': prev? prev.label:false,10385'$config': config,10386'$gradient': gradient10387},10388'children': []10389});10390}10391var root = {10392'id': prefix + '$root',10393'name': '',10394'data': {10395'$type': 'none',10396'$width': 1,10397'$height': 110398},10399'children': ch10400};10401delegate.loadJSON(root);1040210403this.normalizeDims();10404delegate.compute();10405delegate.select(delegate.root);10406if(animate) {10407delegate.fx.animate({10408modes: ['node-property:height:dimArray'],10409duration:150010410});10411}10412},1041310414/*10415Method: updateJSON1041610417Use this method when updating values for the current JSON data. If the items specified by the JSON data already exist in the graph then their values will be updated.1041810419Parameters:1042010421json - (object) JSON data to be updated. The JSON format corresponds to the one described in <AreaChart.loadJSON>.10422onComplete - (object) A callback object to be called when the animation transition when updating the data end.1042310424Example:1042510426(start code js)10427areaChart.updateJSON(json, {10428onComplete: function() {10429alert('update complete!');10430}10431});10432(end code)10433*/10434updateJSON: function(json, onComplete) {10435if(this.busy) return;10436this.busy = true;1043710438var delegate = this.delegate,10439graph = delegate.graph,10440labels = json.label && $.splat(json.label),10441values = json.values,10442animate = this.config.animate,10443that = this,10444hashValues = {};1044510446//convert the whole thing into a hash10447for (var i = 0, l = values.length; i < l; i++) {10448hashValues[values[i].label] = values[i];10449}1045010451graph.eachNode(function(n) {10452var v = hashValues[n.name],10453stringArray = n.getData('stringArray'),10454valArray = n.getData('valueArray'),10455next = n.getData('next');1045610457if (v) {10458v.values = $.splat(v.values);10459$.each(valArray, function(a, i) {10460a[0] = v.values[i];10461if(labels) stringArray[i] = labels[i];10462});10463n.setData('valueArray', valArray);10464}1046510466if(next) {10467v = hashValues[next];10468if(v) {10469$.each(valArray, function(a, i) {10470a[1] = v.values[i];10471});10472}10473}10474});10475this.normalizeDims();10476delegate.compute();10477delegate.select(delegate.root);10478if(animate) {10479delegate.fx.animate({10480modes: ['node-property:height:dimArray'],10481duration:1500,10482onComplete: function() {10483that.busy = false;10484onComplete && onComplete.onComplete();10485}10486});10487}10488},1048910490/*10491Method: filter1049210493Filter selected stacks, collapsing all other stacks. You can filter multiple stacks at the same time.1049410495Parameters:1049610497filters - (array) An array of strings with the name of the stacks to be filtered.10498callback - (object) An object with an *onComplete* callback method.1049910500Example:1050110502(start code js)10503areaChart.filter(['label A', 'label C'], {10504onComplete: function() {10505console.log('done!');10506}10507});10508(end code)1050910510See also:1051110512<AreaChart.restore>.10513*/10514filter: function(filters, callback) {10515if(this.busy) return;10516this.busy = true;10517if(this.config.Tips.enable) this.delegate.tips.hide();10518this.select(false, false, false);10519var args = $.splat(filters);10520var rt = this.delegate.graph.getNode(this.delegate.root);10521var that = this;10522this.normalizeDims();10523rt.eachAdjacency(function(adj) {10524var n = adj.nodeTo,10525dimArray = n.getData('dimArray', 'end'),10526stringArray = n.getData('stringArray');10527n.setData('dimArray', $.map(dimArray, function(d, i) {10528return ($.indexOf(args, stringArray[i]) > -1)? d:[0, 0];10529}), 'end');10530});10531this.delegate.fx.animate({10532modes: ['node-property:dimArray'],10533duration:1500,10534onComplete: function() {10535that.busy = false;10536callback && callback.onComplete();10537}10538});10539},1054010541/*10542Method: restore1054310544Sets all stacks that could have been filtered visible.1054510546Example:1054710548(start code js)10549areaChart.restore();10550(end code)1055110552See also:1055310554<AreaChart.filter>.10555*/10556restore: function(callback) {10557if(this.busy) return;10558this.busy = true;10559if(this.config.Tips.enable) this.delegate.tips.hide();10560this.select(false, false, false);10561this.normalizeDims();10562var that = this;10563this.delegate.fx.animate({10564modes: ['node-property:height:dimArray'],10565duration:1500,10566onComplete: function() {10567that.busy = false;10568callback && callback.onComplete();10569}10570});10571},10572//adds the little brown bar when hovering the node10573select: function(id, name, index) {10574if(!this.config.selectOnHover) return;10575var s = this.selected;10576if(s.id != id || s.name != name10577|| s.index != index) {10578s.id = id;10579s.name = name;10580s.index = index;10581this.delegate.graph.eachNode(function(n) {10582n.setData('border', false);10583});10584if(id) {10585var n = this.delegate.graph.getNode(id);10586n.setData('border', s);10587var link = index === 0? 'prev':'next';10588link = n.getData(link);10589if(link) {10590n = this.delegate.graph.getByName(link);10591if(n) {10592n.setData('border', {10593name: name,10594index: 1-index10595});10596}10597}10598}10599this.delegate.plot();10600}10601},1060210603/*10604Method: getLegend1060510606Returns an object containing as keys the legend names and as values hex strings with color values.1060710608Example:1060910610(start code js)10611var legend = areaChart.getLegend();10612(end code)10613*/10614getLegend: function() {10615var legend = {};10616var n;10617this.delegate.graph.getNode(this.delegate.root).eachAdjacency(function(adj) {10618n = adj.nodeTo;10619});10620var colors = n.getData('colorArray'),10621len = colors.length;10622$.each(n.getData('stringArray'), function(s, i) {10623legend[s] = colors[i % len];10624});10625return legend;10626},1062710628/*10629Method: getMaxValue1063010631Returns the maximum accumulated value for the stacks. This method is used for normalizing the graph heights according to the canvas height.1063210633Example:1063410635(start code js)10636var ans = areaChart.getMaxValue();10637(end code)1063810639In some cases it could be useful to override this method to normalize heights for a group of AreaCharts, like when doing small multiples.1064010641Example:1064210643(start code js)10644//will return 100 for all AreaChart instances,10645//displaying all of them with the same scale10646$jit.AreaChart.implement({10647'getMaxValue': function() {10648return 100;10649}10650});10651(end code)1065210653*/10654getMaxValue: function() {10655var maxValue = 0;10656this.delegate.graph.eachNode(function(n) {10657var valArray = n.getData('valueArray'),10658acumLeft = 0, acumRight = 0;10659$.each(valArray, function(v) {10660acumLeft += +v[0];10661acumRight += +v[1];10662});10663var acum = acumRight>acumLeft? acumRight:acumLeft;10664maxValue = maxValue>acum? maxValue:acum;10665});10666return maxValue;10667},1066810669normalizeDims: function() {10670//number of elements10671var root = this.delegate.graph.getNode(this.delegate.root), l=0;10672root.eachAdjacency(function() {10673l++;10674});10675var maxValue = this.getMaxValue() || 1,10676size = this.delegate.canvas.getSize(),10677config = this.config,10678margin = config.Margin,10679labelOffset = config.labelOffset + config.Label.size,10680fixedDim = (size.width - (margin.left + margin.right)) / l,10681animate = config.animate,10682height = size.height - (margin.top + margin.bottom) - (config.showAggregates && labelOffset)10683- (config.showLabels && labelOffset);10684this.delegate.graph.eachNode(function(n) {10685var acumLeft = 0, acumRight = 0, animateValue = [];10686$.each(n.getData('valueArray'), function(v) {10687acumLeft += +v[0];10688acumRight += +v[1];10689animateValue.push([0, 0]);10690});10691var acum = acumRight>acumLeft? acumRight:acumLeft;10692n.setData('width', fixedDim);10693if(animate) {10694n.setData('height', acum * height / maxValue, 'end');10695n.setData('dimArray', $.map(n.getData('valueArray'), function(n) {10696return [n[0] * height / maxValue, n[1] * height / maxValue];10697}), 'end');10698var dimArray = n.getData('dimArray');10699if(!dimArray) {10700n.setData('dimArray', animateValue);10701}10702} else {10703n.setData('height', acum * height / maxValue);10704n.setData('dimArray', $.map(n.getData('valueArray'), function(n) {10705return [n[0] * height / maxValue, n[1] * height / maxValue];10706}));10707}10708});10709}10710});107111071210713/*10714* File: Options.BarChart.js10715*10716*/1071710718/*10719Object: Options.BarChart1072010721<BarChart> options.10722Other options included in the BarChart are <Options.Canvas>, <Options.Label>, <Options.Margin>, <Options.Tips> and <Options.Events>.1072310724Syntax:1072510726(start code js)1072710728Options.BarChart = {10729animate: true,10730labelOffset: 3,10731barsOffset: 0,10732type: 'stacked',10733hoveredColor: '#9fd4ff',10734orientation: 'horizontal',10735showAggregates: true,10736showLabels: true10737};1073810739(end code)1074010741Example:1074210743(start code js)1074410745var barChart = new $jit.BarChart({10746animate: true,10747barsOffset: 10,10748type: 'stacked:gradient'10749});1075010751(end code)1075210753Parameters:1075410755animate - (boolean) Default's *true*. Whether to add animated transitions when filtering/restoring stacks.10756offset - (number) Default's *25*. Adds margin between the visualization and the canvas.10757labelOffset - (number) Default's *3*. Adds margin between the label and the default place where it should be drawn.10758barsOffset - (number) Default's *0*. Separation between bars.10759type - (string) Default's *'stacked'*. Stack or grouped styles. Posible values are 'stacked', 'grouped', 'stacked:gradient', 'grouped:gradient' to add gradients.10760hoveredColor - (boolean|string) Default's *'#9fd4ff'*. Sets the selected color for a hovered bar stack.10761orientation - (string) Default's 'horizontal'. Sets the direction of the bars. Possible options are 'vertical' or 'horizontal'.10762showAggregates - (boolean, function) Default's *true*. Display the sum the values of each bar. Can also be a function that returns *true* or *false* to display the value of the bar or not. That same function can also return a string with the formatted data to be added.10763showLabels - (boolean, function) Default's *true*. Display the name of the slots. Can also be a function that returns *true* or *false* for each bar to decide whether to show the label or not.1076410765*/1076610767Options.BarChart = {10768$extend: true,1076910770animate: true,10771type: 'stacked', //stacked, grouped, : gradient10772labelOffset: 3, //label offset10773barsOffset: 0, //distance between bars10774hoveredColor: '#9fd4ff',10775orientation: 'horizontal',10776showAggregates: true,10777showLabels: true,10778Tips: {10779enable: false,10780onShow: $.empty,10781onHide: $.empty10782},10783Events: {10784enable: false,10785onClick: $.empty10786}10787};1078810789/*10790* File: BarChart.js10791*10792*/1079310794$jit.ST.Plot.NodeTypes.implement({10795'barchart-stacked' : {10796'render' : function(node, canvas) {10797var pos = node.pos.getc(true),10798width = node.getData('width'),10799height = node.getData('height'),10800algnPos = this.getAlignedPos(pos, width, height),10801x = algnPos.x, y = algnPos.y,10802dimArray = node.getData('dimArray'),10803valueArray = node.getData('valueArray'),10804colorArray = node.getData('colorArray'),10805colorLength = colorArray.length,10806stringArray = node.getData('stringArray');1080710808var ctx = canvas.getCtx(),10809opt = {},10810border = node.getData('border'),10811gradient = node.getData('gradient'),10812config = node.getData('config'),10813horz = config.orientation == 'horizontal',10814aggregates = config.showAggregates,10815showLabels = config.showLabels,10816label = config.Label;1081710818if (colorArray && dimArray && stringArray) {10819for (var i=0, l=dimArray.length, acum=0, valAcum=0; i<l; i++) {10820ctx.fillStyle = ctx.strokeStyle = colorArray[i % colorLength];10821if(gradient) {10822var linear;10823if(horz) {10824linear = ctx.createLinearGradient(x + acum + dimArray[i]/2, y,10825x + acum + dimArray[i]/2, y + height);10826} else {10827linear = ctx.createLinearGradient(x, y - acum - dimArray[i]/2,10828x + width, y - acum- dimArray[i]/2);10829}10830var color = $.rgbToHex($.map($.hexToRgb(colorArray[i % colorLength].slice(1)),10831function(v) { return (v * 0.5) >> 0; }));10832linear.addColorStop(0, color);10833linear.addColorStop(0.5, colorArray[i % colorLength]);10834linear.addColorStop(1, color);10835ctx.fillStyle = linear;10836}10837if(horz) {10838ctx.fillRect(x + acum, y, dimArray[i], height);10839} else {10840ctx.fillRect(x, y - acum - dimArray[i], width, dimArray[i]);10841}10842if(border && border.name == stringArray[i]) {10843opt.acum = acum;10844opt.dimValue = dimArray[i];10845}10846acum += (dimArray[i] || 0);10847valAcum += (valueArray[i] || 0);10848}10849if(border) {10850ctx.save();10851ctx.lineWidth = 2;10852ctx.strokeStyle = border.color;10853if(horz) {10854ctx.strokeRect(x + opt.acum + 1, y + 1, opt.dimValue -2, height - 2);10855} else {10856ctx.strokeRect(x + 1, y - opt.acum - opt.dimValue + 1, width -2, opt.dimValue -2);10857}10858ctx.restore();10859}10860if(label.type == 'Native') {10861ctx.save();10862ctx.fillStyle = ctx.strokeStyle = label.color;10863ctx.font = label.style + ' ' + label.size + 'px ' + label.family;10864ctx.textBaseline = 'middle';10865var aggValue = aggregates(node.name, valAcum, node);10866if(aggValue !== false) {10867aggValue = aggValue !== true? aggValue : valAcum;10868if(horz) {10869ctx.textAlign = 'right';10870ctx.fillText(aggValue, x + acum - config.labelOffset, y + height/2);10871} else {10872ctx.textAlign = 'center';10873ctx.fillText(aggValue, x + width/2, y - height - label.size/2 - config.labelOffset);10874}10875}10876if(showLabels(node.name, valAcum, node)) {10877if(horz) {10878ctx.textAlign = 'center';10879ctx.translate(x - config.labelOffset - label.size/2, y + height/2);10880ctx.rotate(Math.PI / 2);10881ctx.fillText(node.name, 0, 0);10882} else {10883ctx.textAlign = 'center';10884ctx.fillText(node.name, x + width/2, y + label.size/2 + config.labelOffset);10885}10886}10887ctx.restore();10888}10889}10890},10891'contains': function(node, mpos) {10892var pos = node.pos.getc(true),10893width = node.getData('width'),10894height = node.getData('height'),10895algnPos = this.getAlignedPos(pos, width, height),10896x = algnPos.x, y = algnPos.y,10897dimArray = node.getData('dimArray'),10898config = node.getData('config'),10899rx = mpos.x - x,10900horz = config.orientation == 'horizontal';10901//bounding box check10902if(horz) {10903if(mpos.x < x || mpos.x > x + width10904|| mpos.y > y + height || mpos.y < y) {10905return false;10906}10907} else {10908if(mpos.x < x || mpos.x > x + width10909|| mpos.y > y || mpos.y < y - height) {10910return false;10911}10912}10913//deep check10914for(var i=0, l=dimArray.length, acum=(horz? x:y); i<l; i++) {10915var dimi = dimArray[i];10916if(horz) {10917acum += dimi;10918var intersec = acum;10919if(mpos.x <= intersec) {10920return {10921'name': node.getData('stringArray')[i],10922'color': node.getData('colorArray')[i],10923'value': node.getData('valueArray')[i],10924'label': node.name10925};10926}10927} else {10928acum -= dimi;10929var intersec = acum;10930if(mpos.y >= intersec) {10931return {10932'name': node.getData('stringArray')[i],10933'color': node.getData('colorArray')[i],10934'value': node.getData('valueArray')[i],10935'label': node.name10936};10937}10938}10939}10940return false;10941}10942},10943'barchart-grouped' : {10944'render' : function(node, canvas) {10945var pos = node.pos.getc(true),10946width = node.getData('width'),10947height = node.getData('height'),10948algnPos = this.getAlignedPos(pos, width, height),10949x = algnPos.x, y = algnPos.y,10950dimArray = node.getData('dimArray'),10951valueArray = node.getData('valueArray'),10952valueLength = valueArray.length,10953colorArray = node.getData('colorArray'),10954colorLength = colorArray.length,10955stringArray = node.getData('stringArray');1095610957var ctx = canvas.getCtx(),10958opt = {},10959border = node.getData('border'),10960gradient = node.getData('gradient'),10961config = node.getData('config'),10962horz = config.orientation == 'horizontal',10963aggregates = config.showAggregates,10964showLabels = config.showLabels,10965label = config.Label,10966fixedDim = (horz? height : width) / valueLength;1096710968if (colorArray && dimArray && stringArray) {10969for (var i=0, l=valueLength, acum=0, valAcum=0; i<l; i++) {10970ctx.fillStyle = ctx.strokeStyle = colorArray[i % colorLength];10971if(gradient) {10972var linear;10973if(horz) {10974linear = ctx.createLinearGradient(x + dimArray[i]/2, y + fixedDim * i,10975x + dimArray[i]/2, y + fixedDim * (i + 1));10976} else {10977linear = ctx.createLinearGradient(x + fixedDim * i, y - dimArray[i]/2,10978x + fixedDim * (i + 1), y - dimArray[i]/2);10979}10980var color = $.rgbToHex($.map($.hexToRgb(colorArray[i % colorLength].slice(1)),10981function(v) { return (v * 0.5) >> 0; }));10982linear.addColorStop(0, color);10983linear.addColorStop(0.5, colorArray[i % colorLength]);10984linear.addColorStop(1, color);10985ctx.fillStyle = linear;10986}10987if(horz) {10988ctx.fillRect(x, y + fixedDim * i, dimArray[i], fixedDim);10989} else {10990ctx.fillRect(x + fixedDim * i, y - dimArray[i], fixedDim, dimArray[i]);10991}10992if(border && border.name == stringArray[i]) {10993opt.acum = fixedDim * i;10994opt.dimValue = dimArray[i];10995}10996acum += (dimArray[i] || 0);10997valAcum += (valueArray[i] || 0);10998}10999if(border) {11000ctx.save();11001ctx.lineWidth = 2;11002ctx.strokeStyle = border.color;11003if(horz) {11004ctx.strokeRect(x + 1, y + opt.acum + 1, opt.dimValue -2, fixedDim - 2);11005} else {11006ctx.strokeRect(x + opt.acum + 1, y - opt.dimValue + 1, fixedDim -2, opt.dimValue -2);11007}11008ctx.restore();11009}11010if(label.type == 'Native') {11011ctx.save();11012ctx.fillStyle = ctx.strokeStyle = label.color;11013ctx.font = label.style + ' ' + label.size + 'px ' + label.family;11014ctx.textBaseline = 'middle';11015var aggValue = aggregates(node.name, valAcum, node);11016if(aggValue !== false) {11017aggValue = aggValue !== true? aggValue : valAcum;11018if(horz) {11019ctx.textAlign = 'right';11020ctx.fillText(aggValue, x + Math.max.apply(null, dimArray) - config.labelOffset, y + height/2);11021} else {11022ctx.textAlign = 'center';11023ctx.fillText(aggValue, x + width/2, y - Math.max.apply(null, dimArray) - label.size/2 - config.labelOffset);11024}11025}11026if(showLabels(node.name, valAcum, node)) {11027if(horz) {11028ctx.textAlign = 'center';11029ctx.translate(x - config.labelOffset - label.size/2, y + height/2);11030ctx.rotate(Math.PI / 2);11031ctx.fillText(node.name, 0, 0);11032} else {11033ctx.textAlign = 'center';11034ctx.fillText(node.name, x + width/2, y + label.size/2 + config.labelOffset);11035}11036}11037ctx.restore();11038}11039}11040},11041'contains': function(node, mpos) {11042var pos = node.pos.getc(true),11043width = node.getData('width'),11044height = node.getData('height'),11045algnPos = this.getAlignedPos(pos, width, height),11046x = algnPos.x, y = algnPos.y,11047dimArray = node.getData('dimArray'),11048len = dimArray.length,11049config = node.getData('config'),11050rx = mpos.x - x,11051horz = config.orientation == 'horizontal',11052fixedDim = (horz? height : width) / len;11053//bounding box check11054if(horz) {11055if(mpos.x < x || mpos.x > x + width11056|| mpos.y > y + height || mpos.y < y) {11057return false;11058}11059} else {11060if(mpos.x < x || mpos.x > x + width11061|| mpos.y > y || mpos.y < y - height) {11062return false;11063}11064}11065//deep check11066for(var i=0, l=dimArray.length; i<l; i++) {11067var dimi = dimArray[i];11068if(horz) {11069var limit = y + fixedDim * i;11070if(mpos.x <= x+ dimi && mpos.y >= limit && mpos.y <= limit + fixedDim) {11071return {11072'name': node.getData('stringArray')[i],11073'color': node.getData('colorArray')[i],11074'value': node.getData('valueArray')[i],11075'label': node.name11076};11077}11078} else {11079var limit = x + fixedDim * i;11080if(mpos.x >= limit && mpos.x <= limit + fixedDim && mpos.y >= y - dimi) {11081return {11082'name': node.getData('stringArray')[i],11083'color': node.getData('colorArray')[i],11084'value': node.getData('valueArray')[i],11085'label': node.name11086};11087}11088}11089}11090return false;11091}11092}11093});1109411095/*11096Class: BarChart1109711098A visualization that displays stacked bar charts.1109911100Constructor Options:1110111102See <Options.BarChart>.1110311104*/11105$jit.BarChart = new Class({11106st: null,11107colors: ["#416D9C", "#70A35E", "#EBB056", "#C74243", "#83548B", "#909291", "#557EAA"],11108selected: {},11109busy: false,1111011111initialize: function(opt) {11112this.controller = this.config =11113$.merge(Options("Canvas", "Margin", "Label", "BarChart"), {11114Label: { type: 'Native' }11115}, opt);11116//set functions for showLabels and showAggregates11117var showLabels = this.config.showLabels,11118typeLabels = $.type(showLabels),11119showAggregates = this.config.showAggregates,11120typeAggregates = $.type(showAggregates);11121this.config.showLabels = typeLabels == 'function'? showLabels : $.lambda(showLabels);11122this.config.showAggregates = typeAggregates == 'function'? showAggregates : $.lambda(showAggregates);1112311124this.initializeViz();11125},1112611127initializeViz: function() {11128var config = this.config, that = this;11129var nodeType = config.type.split(":")[0],11130horz = config.orientation == 'horizontal',11131nodeLabels = {};1113211133var delegate = new $jit.ST({11134injectInto: config.injectInto,11135width: config.width,11136height: config.height,11137orientation: horz? 'left' : 'bottom',11138levelDistance: 0,11139siblingOffset: config.barsOffset,11140subtreeOffset: 0,11141withLabels: config.Label.type != 'Native',11142useCanvas: config.useCanvas,11143Label: {11144type: config.Label.type11145},11146Node: {11147overridable: true,11148type: 'barchart-' + nodeType,11149align: 'left',11150width: 1,11151height: 111152},11153Edge: {11154type: 'none'11155},11156Tips: {11157enable: config.Tips.enable,11158type: 'Native',11159force: true,11160onShow: function(tip, node, contains) {11161var elem = contains;11162config.Tips.onShow(tip, elem, node);11163}11164},11165Events: {11166enable: true,11167type: 'Native',11168onClick: function(node, eventInfo, evt) {11169if(!config.Events.enable) return;11170var elem = eventInfo.getContains();11171config.Events.onClick(elem, eventInfo, evt);11172},11173onMouseMove: function(node, eventInfo, evt) {11174if(!config.hoveredColor) return;11175if(node) {11176var elem = eventInfo.getContains();11177that.select(node.id, elem.name, elem.index);11178} else {11179that.select(false, false, false);11180}11181}11182},11183onCreateLabel: function(domElement, node) {11184var labelConf = config.Label,11185valueArray = node.getData('valueArray'),11186acum = $.reduce(valueArray, function(x, y) { return x + y; }, 0);11187var nlbs = {11188wrapper: document.createElement('div'),11189aggregate: document.createElement('div'),11190label: document.createElement('div')11191};11192var wrapper = nlbs.wrapper,11193label = nlbs.label,11194aggregate = nlbs.aggregate,11195wrapperStyle = wrapper.style,11196labelStyle = label.style,11197aggregateStyle = aggregate.style;11198//store node labels11199nodeLabels[node.id] = nlbs;11200//append labels11201wrapper.appendChild(label);11202wrapper.appendChild(aggregate);11203if(!config.showLabels(node.name, acum, node)) {11204labelStyle.display = 'none';11205}11206if(!config.showAggregates(node.name, acum, node)) {11207aggregateStyle.display = 'none';11208}11209wrapperStyle.position = 'relative';11210wrapperStyle.overflow = 'visible';11211wrapperStyle.fontSize = labelConf.size + 'px';11212wrapperStyle.fontFamily = labelConf.family;11213wrapperStyle.color = labelConf.color;11214wrapperStyle.textAlign = 'center';11215aggregateStyle.position = labelStyle.position = 'absolute';1121611217domElement.style.width = node.getData('width') + 'px';11218domElement.style.height = node.getData('height') + 'px';11219aggregateStyle.left = labelStyle.left = '0px';1122011221label.innerHTML = node.name;1122211223domElement.appendChild(wrapper);11224},11225onPlaceLabel: function(domElement, node) {11226if(!nodeLabels[node.id]) return;11227var labels = nodeLabels[node.id],11228wrapperStyle = labels.wrapper.style,11229labelStyle = labels.label.style,11230aggregateStyle = labels.aggregate.style,11231grouped = config.type.split(':')[0] == 'grouped',11232horz = config.orientation == 'horizontal',11233dimArray = node.getData('dimArray'),11234valArray = node.getData('valueArray'),11235width = (grouped && horz)? Math.max.apply(null, dimArray) : node.getData('width'),11236height = (grouped && !horz)? Math.max.apply(null, dimArray) : node.getData('height'),11237font = parseInt(wrapperStyle.fontSize, 10),11238domStyle = domElement.style;112391124011241if(dimArray && valArray) {11242wrapperStyle.width = aggregateStyle.width = labelStyle.width = domElement.style.width = width + 'px';11243for(var i=0, l=valArray.length, acum=0; i<l; i++) {11244if(dimArray[i] > 0) {11245acum+= valArray[i];11246}11247}11248if(config.showLabels(node.name, acum, node)) {11249labelStyle.display = '';11250} else {11251labelStyle.display = 'none';11252}11253var aggValue = config.showAggregates(node.name, acum, node);11254if(aggValue !== false) {11255aggregateStyle.display = '';11256} else {11257aggregateStyle.display = 'none';11258}11259if(config.orientation == 'horizontal') {11260aggregateStyle.textAlign = 'right';11261labelStyle.textAlign = 'left';11262labelStyle.textIndex = aggregateStyle.textIndent = config.labelOffset + 'px';11263aggregateStyle.top = labelStyle.top = (height-font)/2 + 'px';11264domElement.style.height = wrapperStyle.height = height + 'px';11265} else {11266aggregateStyle.top = (-font - config.labelOffset) + 'px';11267labelStyle.top = (config.labelOffset + height) + 'px';11268domElement.style.top = parseInt(domElement.style.top, 10) - height + 'px';11269domElement.style.height = wrapperStyle.height = height + 'px';11270}11271labels.aggregate.innerHTML = aggValue !== true? aggValue : acum;11272}11273}11274});1127511276var size = delegate.canvas.getSize(),11277margin = config.Margin;11278if(horz) {11279delegate.config.offsetX = size.width/2 - margin.left11280- (config.showLabels && (config.labelOffset + config.Label.size));11281delegate.config.offsetY = (margin.bottom - margin.top)/2;11282} else {11283delegate.config.offsetY = -size.height/2 + margin.bottom11284+ (config.showLabels && (config.labelOffset + config.Label.size));11285delegate.config.offsetX = (margin.right - margin.left)/2;11286}11287this.delegate = delegate;11288this.canvas = this.delegate.canvas;11289},1129011291/*11292Method: loadJSON1129311294Loads JSON data into the visualization.1129511296Parameters:1129711298json - The JSON data format. This format is described in <http://blog.thejit.org/2010/04/24/new-javascript-infovis-toolkit-visualizations/#json-data-format>.1129911300Example:11301(start code js)11302var barChart = new $jit.BarChart(options);11303barChart.loadJSON(json);11304(end code)11305*/11306loadJSON: function(json) {11307if(this.busy) return;11308this.busy = true;1130911310var prefix = $.time(),11311ch = [],11312delegate = this.delegate,11313name = $.splat(json.label),11314color = $.splat(json.color || this.colors),11315config = this.config,11316gradient = !!config.type.split(":")[1],11317animate = config.animate,11318horz = config.orientation == 'horizontal',11319that = this;1132011321for(var i=0, values=json.values, l=values.length; i<l; i++) {11322var val = values[i]11323var valArray = $.splat(values[i].values);11324var acum = 0;11325ch.push({11326'id': prefix + val.label,11327'name': val.label,11328'data': {11329'value': valArray,11330'$valueArray': valArray,11331'$colorArray': color,11332'$stringArray': name,11333'$gradient': gradient,11334'$config': config11335},11336'children': []11337});11338}11339var root = {11340'id': prefix + '$root',11341'name': '',11342'data': {11343'$type': 'none',11344'$width': 1,11345'$height': 111346},11347'children': ch11348};11349delegate.loadJSON(root);1135011351this.normalizeDims();11352delegate.compute();11353delegate.select(delegate.root);11354if(animate) {11355if(horz) {11356delegate.fx.animate({11357modes: ['node-property:width:dimArray'],11358duration:1500,11359onComplete: function() {11360that.busy = false;11361}11362});11363} else {11364delegate.fx.animate({11365modes: ['node-property:height:dimArray'],11366duration:1500,11367onComplete: function() {11368that.busy = false;11369}11370});11371}11372} else {11373this.busy = false;11374}11375},1137611377/*11378Method: updateJSON1137911380Use this method when updating values for the current JSON data. If the items specified by the JSON data already exist in the graph then their values will be updated.1138111382Parameters:1138311384json - (object) JSON data to be updated. The JSON format corresponds to the one described in <BarChart.loadJSON>.11385onComplete - (object) A callback object to be called when the animation transition when updating the data end.1138611387Example:1138811389(start code js)11390barChart.updateJSON(json, {11391onComplete: function() {11392alert('update complete!');11393}11394});11395(end code)11396*/11397updateJSON: function(json, onComplete) {11398if(this.busy) return;11399this.busy = true;11400this.select(false, false, false);11401var delegate = this.delegate;11402var graph = delegate.graph;11403var values = json.values;11404var animate = this.config.animate;11405var that = this;11406var horz = this.config.orientation == 'horizontal';11407$.each(values, function(v) {11408var n = graph.getByName(v.label);11409if(n) {11410n.setData('valueArray', $.splat(v.values));11411if(json.label) {11412n.setData('stringArray', $.splat(json.label));11413}11414}11415});11416this.normalizeDims();11417delegate.compute();11418delegate.select(delegate.root);11419if(animate) {11420if(horz) {11421delegate.fx.animate({11422modes: ['node-property:width:dimArray'],11423duration:1500,11424onComplete: function() {11425that.busy = false;11426onComplete && onComplete.onComplete();11427}11428});11429} else {11430delegate.fx.animate({11431modes: ['node-property:height:dimArray'],11432duration:1500,11433onComplete: function() {11434that.busy = false;11435onComplete && onComplete.onComplete();11436}11437});11438}11439}11440},1144111442//adds the little brown bar when hovering the node11443select: function(id, name) {11444if(!this.config.hoveredColor) return;11445var s = this.selected;11446if(s.id != id || s.name != name) {11447s.id = id;11448s.name = name;11449s.color = this.config.hoveredColor;11450this.delegate.graph.eachNode(function(n) {11451if(id == n.id) {11452n.setData('border', s);11453} else {11454n.setData('border', false);11455}11456});11457this.delegate.plot();11458}11459},1146011461/*11462Method: getLegend1146311464Returns an object containing as keys the legend names and as values hex strings with color values.1146511466Example:1146711468(start code js)11469var legend = barChart.getLegend();11470(end code)11471*/11472getLegend: function() {11473var legend = {};11474var n;11475this.delegate.graph.getNode(this.delegate.root).eachAdjacency(function(adj) {11476n = adj.nodeTo;11477});11478var colors = n.getData('colorArray'),11479len = colors.length;11480$.each(n.getData('stringArray'), function(s, i) {11481legend[s] = colors[i % len];11482});11483return legend;11484},1148511486/*11487Method: getMaxValue1148811489Returns the maximum accumulated value for the stacks. This method is used for normalizing the graph heights according to the canvas height.1149011491Example:1149211493(start code js)11494var ans = barChart.getMaxValue();11495(end code)1149611497In some cases it could be useful to override this method to normalize heights for a group of BarCharts, like when doing small multiples.1149811499Example:1150011501(start code js)11502//will return 100 for all BarChart instances,11503//displaying all of them with the same scale11504$jit.BarChart.implement({11505'getMaxValue': function() {11506return 100;11507}11508});11509(end code)1151011511*/11512getMaxValue: function() {11513var maxValue = 0, stacked = this.config.type.split(':')[0] == 'stacked';11514this.delegate.graph.eachNode(function(n) {11515var valArray = n.getData('valueArray'),11516acum = 0;11517if(!valArray) return;11518if(stacked) {11519$.each(valArray, function(v) {11520acum += +v;11521});11522} else {11523acum = Math.max.apply(null, valArray);11524}11525maxValue = maxValue>acum? maxValue:acum;11526});11527return maxValue;11528},1152911530setBarType: function(type) {11531this.config.type = type;11532this.delegate.config.Node.type = 'barchart-' + type.split(':')[0];11533},1153411535normalizeDims: function() {11536//number of elements11537var root = this.delegate.graph.getNode(this.delegate.root), l=0;11538root.eachAdjacency(function() {11539l++;11540});11541var maxValue = this.getMaxValue() || 1,11542size = this.delegate.canvas.getSize(),11543config = this.config,11544margin = config.Margin,11545marginWidth = margin.left + margin.right,11546marginHeight = margin.top + margin.bottom,11547horz = config.orientation == 'horizontal',11548fixedDim = (size[horz? 'height':'width'] - (horz? marginHeight:marginWidth) - (l -1) * config.barsOffset) / l,11549animate = config.animate,11550height = size[horz? 'width':'height'] - (horz? marginWidth:marginHeight)11551- (!horz && config.showAggregates && (config.Label.size + config.labelOffset))11552- (config.showLabels && (config.Label.size + config.labelOffset)),11553dim1 = horz? 'height':'width',11554dim2 = horz? 'width':'height';11555this.delegate.graph.eachNode(function(n) {11556var acum = 0, animateValue = [];11557$.each(n.getData('valueArray'), function(v) {11558acum += +v;11559animateValue.push(0);11560});11561n.setData(dim1, fixedDim);11562if(animate) {11563n.setData(dim2, acum * height / maxValue, 'end');11564n.setData('dimArray', $.map(n.getData('valueArray'), function(n) {11565return n * height / maxValue;11566}), 'end');11567var dimArray = n.getData('dimArray');11568if(!dimArray) {11569n.setData('dimArray', animateValue);11570}11571} else {11572n.setData(dim2, acum * height / maxValue);11573n.setData('dimArray', $.map(n.getData('valueArray'), function(n) {11574return n * height / maxValue;11575}));11576}11577});11578}11579});115801158111582/*11583* File: Options.PieChart.js11584*11585*/11586/*11587Object: Options.PieChart1158811589<PieChart> options.11590Other options included in the PieChart are <Options.Canvas>, <Options.Label>, <Options.Tips> and <Options.Events>.1159111592Syntax:1159311594(start code js)1159511596Options.PieChart = {11597animate: true,11598offset: 25,11599sliceOffset:0,11600labelOffset: 3,11601type: 'stacked',11602hoveredColor: '#9fd4ff',11603showLabels: true,11604resizeLabels: false,11605updateHeights: false11606};1160711608(end code)1160911610Example:1161111612(start code js)1161311614var pie = new $jit.PieChart({11615animate: true,11616sliceOffset: 5,11617type: 'stacked:gradient'11618});1161911620(end code)1162111622Parameters:1162311624animate - (boolean) Default's *true*. Whether to add animated transitions when plotting/updating the visualization.11625offset - (number) Default's *25*. Adds margin between the visualization and the canvas.11626sliceOffset - (number) Default's *0*. Separation between the center of the canvas and each pie slice.11627labelOffset - (number) Default's *3*. Adds margin between the label and the default place where it should be drawn.11628type - (string) Default's *'stacked'*. Stack style. Posible values are 'stacked', 'stacked:gradient' to add gradients.11629hoveredColor - (boolean|string) Default's *'#9fd4ff'*. Sets the selected color for a hovered pie stack.11630showLabels - (boolean) Default's *true*. Display the name of the slots.11631resizeLabels - (boolean|number) Default's *false*. Resize the pie labels according to their stacked values. Set a number for *resizeLabels* to set a font size minimum.11632updateHeights - (boolean) Default's *false*. Only for mono-valued (most common) pie charts. Resize the height of the pie slices according to their current values.1163311634*/11635Options.PieChart = {11636$extend: true,1163711638animate: true,11639offset: 25, // page offset11640sliceOffset:0,11641labelOffset: 3, // label offset11642type: 'stacked', // gradient11643hoveredColor: '#9fd4ff',11644Events: {11645enable: false,11646onClick: $.empty11647},11648Tips: {11649enable: false,11650onShow: $.empty,11651onHide: $.empty11652},11653showLabels: true,11654resizeLabels: false,1165511656//only valid for mono-valued datasets11657updateHeights: false11658};1165911660/*11661* Class: Layouts.Radial11662*11663* Implements a Radial Layout.11664*11665* Implemented By:11666*11667* <RGraph>, <Hypertree>11668*11669*/11670Layouts.Radial = new Class({1167111672/*11673* Method: compute11674*11675* Computes nodes' positions.11676*11677* Parameters:11678*11679* property - _optional_ A <Graph.Node> position property to store the new11680* positions. Possible values are 'pos', 'end' or 'start'.11681*11682*/11683compute : function(property) {11684var prop = $.splat(property || [ 'current', 'start', 'end' ]);11685NodeDim.compute(this.graph, prop, this.config);11686this.graph.computeLevels(this.root, 0, "ignore");11687var lengthFunc = this.createLevelDistanceFunc();11688this.computeAngularWidths(prop);11689this.computePositions(prop, lengthFunc);11690},1169111692/*11693* computePositions11694*11695* Performs the main algorithm for computing node positions.11696*/11697computePositions : function(property, getLength) {11698var propArray = property;11699var graph = this.graph;11700var root = graph.getNode(this.root);11701var parent = this.parent;11702var config = this.config;1170311704for ( var i=0, l=propArray.length; i < l; i++) {11705var pi = propArray[i];11706root.setPos($P(0, 0), pi);11707root.setData('span', Math.PI * 2, pi);11708}1170911710root.angleSpan = {11711begin : 0,11712end : 2 * Math.PI11713};1171411715graph.eachBFS(this.root, function(elem) {11716var angleSpan = elem.angleSpan.end - elem.angleSpan.begin;11717var angleInit = elem.angleSpan.begin;11718var len = getLength(elem);11719//Calculate the sum of all angular widths11720var totalAngularWidths = 0, subnodes = [], maxDim = {};11721elem.eachSubnode(function(sib) {11722totalAngularWidths += sib._treeAngularWidth;11723//get max dim11724for ( var i=0, l=propArray.length; i < l; i++) {11725var pi = propArray[i], dim = sib.getData('dim', pi);11726maxDim[pi] = (pi in maxDim)? (dim > maxDim[pi]? dim : maxDim[pi]) : dim;11727}11728subnodes.push(sib);11729}, "ignore");11730//Maintain children order11731//Second constraint for <http://bailando.sims.berkeley.edu/papers/infovis01.htm>11732if (parent && parent.id == elem.id && subnodes.length > 011733&& subnodes[0].dist) {11734subnodes.sort(function(a, b) {11735return (a.dist >= b.dist) - (a.dist <= b.dist);11736});11737}11738//Calculate nodes positions.11739for (var k = 0, ls=subnodes.length; k < ls; k++) {11740var child = subnodes[k];11741if (!child._flag) {11742var angleProportion = child._treeAngularWidth / totalAngularWidths * angleSpan;11743var theta = angleInit + angleProportion / 2;1174411745for ( var i=0, l=propArray.length; i < l; i++) {11746var pi = propArray[i];11747child.setPos($P(theta, len), pi);11748child.setData('span', angleProportion, pi);11749child.setData('dim-quotient', child.getData('dim', pi) / maxDim[pi], pi);11750}1175111752child.angleSpan = {11753begin : angleInit,11754end : angleInit + angleProportion11755};11756angleInit += angleProportion;11757}11758}11759}, "ignore");11760},1176111762/*11763* Method: setAngularWidthForNodes11764*11765* Sets nodes angular widths.11766*/11767setAngularWidthForNodes : function(prop) {11768this.graph.eachBFS(this.root, function(elem, i) {11769var diamValue = elem.getData('angularWidth', prop[0]) || 5;11770elem._angularWidth = diamValue / i;11771}, "ignore");11772},1177311774/*11775* Method: setSubtreesAngularWidth11776*11777* Sets subtrees angular widths.11778*/11779setSubtreesAngularWidth : function() {11780var that = this;11781this.graph.eachNode(function(elem) {11782that.setSubtreeAngularWidth(elem);11783}, "ignore");11784},1178511786/*11787* Method: setSubtreeAngularWidth11788*11789* Sets the angular width for a subtree.11790*/11791setSubtreeAngularWidth : function(elem) {11792var that = this, nodeAW = elem._angularWidth, sumAW = 0;11793elem.eachSubnode(function(child) {11794that.setSubtreeAngularWidth(child);11795sumAW += child._treeAngularWidth;11796}, "ignore");11797elem._treeAngularWidth = Math.max(nodeAW, sumAW);11798},1179911800/*11801* Method: computeAngularWidths11802*11803* Computes nodes and subtrees angular widths.11804*/11805computeAngularWidths : function(prop) {11806this.setAngularWidthForNodes(prop);11807this.setSubtreesAngularWidth();11808}1180911810});118111181211813/*11814* File: Sunburst.js11815*/1181611817/*11818Class: Sunburst1181911820A radial space filling tree visualization.1182111822Inspired by:1182311824Sunburst <http://www.cc.gatech.edu/gvu/ii/sunburst/>.1182511826Note:1182711828This visualization was built and engineered from scratch, taking only the paper as inspiration, and only shares some features with the visualization described in the paper.1182911830Implements:1183111832All <Loader> methods1183311834Constructor Options:1183511836Inherits options from1183711838- <Options.Canvas>11839- <Options.Controller>11840- <Options.Node>11841- <Options.Edge>11842- <Options.Label>11843- <Options.Events>11844- <Options.Tips>11845- <Options.NodeStyles>11846- <Options.Navigation>1184711848Additionally, there are other parameters and some default values changed1184911850interpolation - (string) Default's *linear*. Describes the way nodes are interpolated. Possible values are 'linear' and 'polar'.11851levelDistance - (number) Default's *100*. The distance between levels of the tree.11852Node.type - Described in <Options.Node>. Default's to *multipie*.11853Node.height - Described in <Options.Node>. Default's *0*.11854Edge.type - Described in <Options.Edge>. Default's *none*.11855Label.textAlign - Described in <Options.Label>. Default's *start*.11856Label.textBaseline - Described in <Options.Label>. Default's *middle*.1185711858Instance Properties:1185911860canvas - Access a <Canvas> instance.11861graph - Access a <Graph> instance.11862op - Access a <Sunburst.Op> instance.11863fx - Access a <Sunburst.Plot> instance.11864labels - Access a <Sunburst.Label> interface implementation.1186511866*/1186711868$jit.Sunburst = new Class({1186911870Implements: [ Loader, Extras, Layouts.Radial ],1187111872initialize: function(controller) {11873var $Sunburst = $jit.Sunburst;1187411875var config = {11876interpolation: 'linear',11877levelDistance: 100,11878Node: {11879'type': 'multipie',11880'height':011881},11882Edge: {11883'type': 'none'11884},11885Label: {11886textAlign: 'start',11887textBaseline: 'middle'11888}11889};1189011891this.controller = this.config = $.merge(Options("Canvas", "Node", "Edge",11892"Fx", "Tips", "NodeStyles", "Events", "Navigation", "Controller", "Label"), config, controller);1189311894var canvasConfig = this.config;11895if(canvasConfig.useCanvas) {11896this.canvas = canvasConfig.useCanvas;11897this.config.labelContainer = this.canvas.id + '-label';11898} else {11899if(canvasConfig.background) {11900canvasConfig.background = $.merge({11901type: 'Circles'11902}, canvasConfig.background);11903}11904this.canvas = new Canvas(this, canvasConfig);11905this.config.labelContainer = (typeof canvasConfig.injectInto == 'string'? canvasConfig.injectInto : canvasConfig.injectInto.id) + '-label';11906}1190711908this.graphOptions = {11909'klass': Polar,11910'Node': {11911'selected': false,11912'exist': true,11913'drawn': true11914}11915};11916this.graph = new Graph(this.graphOptions, this.config.Node,11917this.config.Edge);11918this.labels = new $Sunburst.Label[canvasConfig.Label.type](this);11919this.fx = new $Sunburst.Plot(this, $Sunburst);11920this.op = new $Sunburst.Op(this);11921this.json = null;11922this.root = null;11923this.rotated = null;11924this.busy = false;11925// initialize extras11926this.initializeExtras();11927},1192811929/*1193011931createLevelDistanceFunc1193211933Returns the levelDistance function used for calculating a node distance11934to its origin. This function returns a function that is computed11935per level and not per node, such that all nodes with the same depth will have the11936same distance to the origin. The resulting function gets the11937parent node as parameter and returns a float.1193811939*/11940createLevelDistanceFunc: function() {11941var ld = this.config.levelDistance;11942return function(elem) {11943return (elem._depth + 1) * ld;11944};11945},1194611947/*11948Method: refresh1194911950Computes positions and plots the tree.1195111952*/11953refresh: function() {11954this.compute();11955this.plot();11956},1195711958/*11959reposition1196011961An alias for computing new positions to _endPos_1196211963See also:1196411965<Sunburst.compute>1196611967*/11968reposition: function() {11969this.compute('end');11970},1197111972/*11973Method: rotate1197411975Rotates the graph so that the selected node is horizontal on the right.1197611977Parameters:1197811979node - (object) A <Graph.Node>.11980method - (string) Whether to perform an animation or just replot the graph. Possible values are "replot" or "animate".11981opt - (object) Configuration options merged with this visualization configuration options.1198211983See also:1198411985<Sunburst.rotateAngle>1198611987*/11988rotate: function(node, method, opt) {11989var theta = node.getPos(opt.property || 'current').getp(true).theta;11990this.rotated = node;11991this.rotateAngle(-theta, method, opt);11992},1199311994/*11995Method: rotateAngle1199611997Rotates the graph of an angle theta.1199811999Parameters:1200012001node - (object) A <Graph.Node>.12002method - (string) Whether to perform an animation or just replot the graph. Possible values are "replot" or "animate".12003opt - (object) Configuration options merged with this visualization configuration options.1200412005See also:1200612007<Sunburst.rotate>1200812009*/12010rotateAngle: function(theta, method, opt) {12011var that = this;12012var options = $.merge(this.config, opt || {}, {12013modes: [ 'polar' ]12014});12015var prop = opt.property || (method === "animate" ? 'end' : 'current');12016if(method === 'animate') {12017this.fx.animation.pause();12018}12019this.graph.eachNode(function(n) {12020var p = n.getPos(prop);12021p.theta += theta;12022if (p.theta < 0) {12023p.theta += Math.PI * 2;12024}12025});12026if (method == 'animate') {12027this.fx.animate(options);12028} else if (method == 'replot') {12029this.fx.plot();12030this.busy = false;12031}12032},1203312034/*12035Method: plot1203612037Plots the Sunburst. This is a shortcut to *fx.plot*.12038*/12039plot: function() {12040this.fx.plot();12041}12042});1204312044$jit.Sunburst.$extend = true;1204512046(function(Sunburst) {1204712048/*12049Class: Sunburst.Op1205012051Custom extension of <Graph.Op>.1205212053Extends:1205412055All <Graph.Op> methods1205612057See also:1205812059<Graph.Op>1206012061*/12062Sunburst.Op = new Class( {1206312064Implements: Graph.Op1206512066});1206712068/*12069Class: Sunburst.Plot1207012071Custom extension of <Graph.Plot>.1207212073Extends:1207412075All <Graph.Plot> methods1207612077See also:1207812079<Graph.Plot>1208012081*/12082Sunburst.Plot = new Class( {1208312084Implements: Graph.Plot1208512086});1208712088/*12089Class: Sunburst.Label1209012091Custom extension of <Graph.Label>.12092Contains custom <Graph.Label.SVG>, <Graph.Label.HTML> and <Graph.Label.Native> extensions.1209312094Extends:1209512096All <Graph.Label> methods and subclasses.1209712098See also:1209912100<Graph.Label>, <Graph.Label.Native>, <Graph.Label.HTML>, <Graph.Label.SVG>.1210112102*/12103Sunburst.Label = {};1210412105/*12106Sunburst.Label.Native1210712108Custom extension of <Graph.Label.Native>.1210912110Extends:1211112112All <Graph.Label.Native> methods1211312114See also:1211512116<Graph.Label.Native>12117*/12118Sunburst.Label.Native = new Class( {12119Implements: Graph.Label.Native,1212012121initialize: function(viz) {12122this.viz = viz;12123this.label = viz.config.Label;12124this.config = viz.config;12125},1212612127renderLabel: function(canvas, node, controller) {12128var span = node.getData('span');12129if(span < Math.PI /2 && Math.tan(span) *12130this.config.levelDistance * node._depth < 10) {12131return;12132}12133var ctx = canvas.getCtx();12134var measure = ctx.measureText(node.name);12135if (node.id == this.viz.root) {12136var x = -measure.width / 2, y = 0, thetap = 0;12137var ld = 0;12138} else {12139var indent = 5;12140var ld = controller.levelDistance - indent;12141var clone = node.pos.clone();12142clone.rho += indent;12143var p = clone.getp(true);12144var ct = clone.getc(true);12145var x = ct.x, y = ct.y;12146// get angle in degrees12147var pi = Math.PI;12148var cond = (p.theta > pi / 2 && p.theta < 3 * pi / 2);12149var thetap = cond ? p.theta + pi : p.theta;12150if (cond) {12151x -= Math.abs(Math.cos(p.theta) * measure.width);12152y += Math.sin(p.theta) * measure.width;12153} else if (node.id == this.viz.root) {12154x -= measure.width / 2;12155}12156}12157ctx.save();12158ctx.translate(x, y);12159ctx.rotate(thetap);12160ctx.fillText(node.name, 0, 0);12161ctx.restore();12162}12163});1216412165/*12166Sunburst.Label.SVG1216712168Custom extension of <Graph.Label.SVG>.1216912170Extends:1217112172All <Graph.Label.SVG> methods1217312174See also:1217512176<Graph.Label.SVG>1217712178*/12179Sunburst.Label.SVG = new Class( {12180Implements: Graph.Label.SVG,1218112182initialize: function(viz) {12183this.viz = viz;12184},1218512186/*12187placeLabel1218812189Overrides abstract method placeLabel in <Graph.Plot>.1219012191Parameters:1219212193tag - A DOM label element.12194node - A <Graph.Node>.12195controller - A configuration/controller object passed to the visualization.1219612197*/12198placeLabel: function(tag, node, controller) {12199var pos = node.pos.getc(true), viz = this.viz, canvas = this.viz.canvas;12200var radius = canvas.getSize();12201var labelPos = {12202x: Math.round(pos.x + radius.width / 2),12203y: Math.round(pos.y + radius.height / 2)12204};12205tag.setAttribute('x', labelPos.x);12206tag.setAttribute('y', labelPos.y);1220712208var bb = tag.getBBox();12209if (bb) {12210// center the label12211var x = tag.getAttribute('x');12212var y = tag.getAttribute('y');12213// get polar coordinates12214var p = node.pos.getp(true);12215// get angle in degrees12216var pi = Math.PI;12217var cond = (p.theta > pi / 2 && p.theta < 3 * pi / 2);12218if (cond) {12219tag.setAttribute('x', x - bb.width);12220tag.setAttribute('y', y - bb.height);12221} else if (node.id == viz.root) {12222tag.setAttribute('x', x - bb.width / 2);12223}1222412225var thetap = cond ? p.theta + pi : p.theta;12226if(node._depth)12227tag.setAttribute('transform', 'rotate(' + thetap * 360 / (2 * pi) + ' ' + x12228+ ' ' + y + ')');12229}1223012231controller.onPlaceLabel(tag, node);12232}12233});1223412235/*12236Sunburst.Label.HTML1223712238Custom extension of <Graph.Label.HTML>.1223912240Extends:1224112242All <Graph.Label.HTML> methods.1224312244See also:1224512246<Graph.Label.HTML>1224712248*/12249Sunburst.Label.HTML = new Class( {12250Implements: Graph.Label.HTML,1225112252initialize: function(viz) {12253this.viz = viz;12254},12255/*12256placeLabel1225712258Overrides abstract method placeLabel in <Graph.Plot>.1225912260Parameters:1226112262tag - A DOM label element.12263node - A <Graph.Node>.12264controller - A configuration/controller object passed to the visualization.1226512266*/12267placeLabel: function(tag, node, controller) {12268var pos = node.pos.clone(),12269canvas = this.viz.canvas,12270height = node.getData('height'),12271ldist = ((height || node._depth == 0)? height : this.viz.config.levelDistance) /2,12272radius = canvas.getSize();12273pos.rho += ldist;12274pos = pos.getc(true);1227512276var labelPos = {12277x: Math.round(pos.x + radius.width / 2),12278y: Math.round(pos.y + radius.height / 2)12279};1228012281var style = tag.style;12282style.left = labelPos.x + 'px';12283style.top = labelPos.y + 'px';12284style.display = this.fitsInCanvas(labelPos, canvas) ? '' : 'none';1228512286controller.onPlaceLabel(tag, node);12287}12288});1228912290/*12291Class: Sunburst.Plot.NodeTypes1229212293This class contains a list of <Graph.Node> built-in types.12294Node types implemented are 'none', 'pie', 'multipie', 'gradient-pie' and 'gradient-multipie'.1229512296You can add your custom node types, customizing your visualization to the extreme.1229712298Example:1229912300(start code js)12301Sunburst.Plot.NodeTypes.implement({12302'mySpecialType': {12303'render': function(node, canvas) {12304//print your custom node to canvas12305},12306//optional12307'contains': function(node, pos) {12308//return true if pos is inside the node or false otherwise12309}12310}12311});12312(end code)1231312314*/12315Sunburst.Plot.NodeTypes = new Class( {12316'none': {12317'render': $.empty,12318'contains': $.lambda(false),12319'anglecontains': function(node, pos) {12320var span = node.getData('span') / 2, theta = node.pos.theta;12321var begin = theta - span, end = theta + span;12322if (begin < 0)12323begin += Math.PI * 2;12324var atan = Math.atan2(pos.y, pos.x);12325if (atan < 0)12326atan += Math.PI * 2;12327if (begin > end) {12328return (atan > begin && atan <= Math.PI * 2) || atan < end;12329} else {12330return atan > begin && atan < end;12331}12332}12333},1233412335'pie': {12336'render': function(node, canvas) {12337var span = node.getData('span') / 2, theta = node.pos.theta;12338var begin = theta - span, end = theta + span;12339var polarNode = node.pos.getp(true);12340var polar = new Polar(polarNode.rho, begin);12341var p1coord = polar.getc(true);12342polar.theta = end;12343var p2coord = polar.getc(true);1234412345var ctx = canvas.getCtx();12346ctx.beginPath();12347ctx.moveTo(0, 0);12348ctx.lineTo(p1coord.x, p1coord.y);12349ctx.moveTo(0, 0);12350ctx.lineTo(p2coord.x, p2coord.y);12351ctx.moveTo(0, 0);12352ctx.arc(0, 0, polarNode.rho * node.getData('dim-quotient'), begin, end,12353false);12354ctx.fill();12355},12356'contains': function(node, pos) {12357if (this.nodeTypes['none'].anglecontains.call(this, node, pos)) {12358var rho = Math.sqrt(pos.x * pos.x + pos.y * pos.y);12359var ld = this.config.levelDistance, d = node._depth;12360return (rho <= ld * d);12361}12362return false;12363}12364},12365'multipie': {12366'render': function(node, canvas) {12367var height = node.getData('height');12368var ldist = height? height : this.config.levelDistance;12369var span = node.getData('span') / 2, theta = node.pos.theta;12370var begin = theta - span, end = theta + span;12371var polarNode = node.pos.getp(true);1237212373var polar = new Polar(polarNode.rho, begin);12374var p1coord = polar.getc(true);1237512376polar.theta = end;12377var p2coord = polar.getc(true);1237812379polar.rho += ldist;12380var p3coord = polar.getc(true);1238112382polar.theta = begin;12383var p4coord = polar.getc(true);1238412385var ctx = canvas.getCtx();12386ctx.moveTo(0, 0);12387ctx.beginPath();12388ctx.arc(0, 0, polarNode.rho, begin, end, false);12389ctx.arc(0, 0, polarNode.rho + ldist, end, begin, true);12390ctx.moveTo(p1coord.x, p1coord.y);12391ctx.lineTo(p4coord.x, p4coord.y);12392ctx.moveTo(p2coord.x, p2coord.y);12393ctx.lineTo(p3coord.x, p3coord.y);12394ctx.fill();1239512396if (node.collapsed) {12397ctx.save();12398ctx.lineWidth = 2;12399ctx.moveTo(0, 0);12400ctx.beginPath();12401ctx.arc(0, 0, polarNode.rho + ldist + 5, end - 0.01, begin + 0.01,12402true);12403ctx.stroke();12404ctx.restore();12405}12406},12407'contains': function(node, pos) {12408if (this.nodeTypes['none'].anglecontains.call(this, node, pos)) {12409var rho = Math.sqrt(pos.x * pos.x + pos.y * pos.y);12410var height = node.getData('height');12411var ldist = height? height : this.config.levelDistance;12412var ld = this.config.levelDistance, d = node._depth;12413return (rho >= ld * d) && (rho <= (ld * d + ldist));12414}12415return false;12416}12417},1241812419'gradient-multipie': {12420'render': function(node, canvas) {12421var ctx = canvas.getCtx();12422var height = node.getData('height');12423var ldist = height? height : this.config.levelDistance;12424var radialGradient = ctx.createRadialGradient(0, 0, node.getPos().rho,124250, 0, node.getPos().rho + ldist);1242612427var colorArray = $.hexToRgb(node.getData('color')), ans = [];12428$.each(colorArray, function(i) {12429ans.push(parseInt(i * 0.5, 10));12430});12431var endColor = $.rgbToHex(ans);12432radialGradient.addColorStop(0, endColor);12433radialGradient.addColorStop(1, node.getData('color'));12434ctx.fillStyle = radialGradient;12435this.nodeTypes['multipie'].render.call(this, node, canvas);12436},12437'contains': function(node, pos) {12438return this.nodeTypes['multipie'].contains.call(this, node, pos);12439}12440},1244112442'gradient-pie': {12443'render': function(node, canvas) {12444var ctx = canvas.getCtx();12445var radialGradient = ctx.createRadialGradient(0, 0, 0, 0, 0, node12446.getPos().rho);1244712448var colorArray = $.hexToRgb(node.getData('color')), ans = [];12449$.each(colorArray, function(i) {12450ans.push(parseInt(i * 0.5, 10));12451});12452var endColor = $.rgbToHex(ans);12453radialGradient.addColorStop(1, endColor);12454radialGradient.addColorStop(0, node.getData('color'));12455ctx.fillStyle = radialGradient;12456this.nodeTypes['pie'].render.call(this, node, canvas);12457},12458'contains': function(node, pos) {12459return this.nodeTypes['pie'].contains.call(this, node, pos);12460}12461}12462});1246312464/*12465Class: Sunburst.Plot.EdgeTypes1246612467This class contains a list of <Graph.Adjacence> built-in types.12468Edge types implemented are 'none', 'line' and 'arrow'.1246912470You can add your custom edge types, customizing your visualization to the extreme.1247112472Example:1247312474(start code js)12475Sunburst.Plot.EdgeTypes.implement({12476'mySpecialType': {12477'render': function(adj, canvas) {12478//print your custom edge to canvas12479},12480//optional12481'contains': function(adj, pos) {12482//return true if pos is inside the arc or false otherwise12483}12484}12485});12486(end code)1248712488*/12489Sunburst.Plot.EdgeTypes = new Class({12490'none': $.empty,12491'line': {12492'render': function(adj, canvas) {12493var from = adj.nodeFrom.pos.getc(true),12494to = adj.nodeTo.pos.getc(true);12495this.edgeHelper.line.render(from, to, canvas);12496},12497'contains': function(adj, pos) {12498var from = adj.nodeFrom.pos.getc(true),12499to = adj.nodeTo.pos.getc(true);12500return this.edgeHelper.line.contains(from, to, pos, this.edge.epsilon);12501}12502},12503'arrow': {12504'render': function(adj, canvas) {12505var from = adj.nodeFrom.pos.getc(true),12506to = adj.nodeTo.pos.getc(true),12507dim = adj.getData('dim'),12508direction = adj.data.$direction,12509inv = (direction && direction.length>1 && direction[0] != adj.nodeFrom.id);12510this.edgeHelper.arrow.render(from, to, dim, inv, canvas);12511},12512'contains': function(adj, pos) {12513var from = adj.nodeFrom.pos.getc(true),12514to = adj.nodeTo.pos.getc(true);12515return this.edgeHelper.arrow.contains(from, to, pos, this.edge.epsilon);12516}12517},12518'hyperline': {12519'render': function(adj, canvas) {12520var from = adj.nodeFrom.pos.getc(),12521to = adj.nodeTo.pos.getc(),12522dim = Math.max(from.norm(), to.norm());12523this.edgeHelper.hyperline.render(from.$scale(1/dim), to.$scale(1/dim), dim, canvas);12524},12525'contains': $.lambda(false) //Implement this!12526}12527});1252812529})($jit.Sunburst);125301253112532/*12533* File: PieChart.js12534*12535*/1253612537$jit.Sunburst.Plot.NodeTypes.implement({12538'piechart-stacked' : {12539'render' : function(node, canvas) {12540var pos = node.pos.getp(true),12541dimArray = node.getData('dimArray'),12542valueArray = node.getData('valueArray'),12543colorArray = node.getData('colorArray'),12544colorLength = colorArray.length,12545stringArray = node.getData('stringArray'),12546span = node.getData('span') / 2,12547theta = node.pos.theta,12548begin = theta - span,12549end = theta + span,12550polar = new Polar;1255112552var ctx = canvas.getCtx(),12553opt = {},12554gradient = node.getData('gradient'),12555border = node.getData('border'),12556config = node.getData('config'),12557showLabels = config.showLabels,12558resizeLabels = config.resizeLabels,12559label = config.Label;1256012561var xpos = config.sliceOffset * Math.cos((begin + end) /2);12562var ypos = config.sliceOffset * Math.sin((begin + end) /2);1256312564if (colorArray && dimArray && stringArray) {12565for (var i=0, l=dimArray.length, acum=0, valAcum=0; i<l; i++) {12566var dimi = dimArray[i], colori = colorArray[i % colorLength];12567if(dimi <= 0) continue;12568ctx.fillStyle = ctx.strokeStyle = colori;12569if(gradient && dimi) {12570var radialGradient = ctx.createRadialGradient(xpos, ypos, acum + config.sliceOffset,12571xpos, ypos, acum + dimi + config.sliceOffset);12572var colorRgb = $.hexToRgb(colori),12573ans = $.map(colorRgb, function(i) { return (i * 0.8) >> 0; }),12574endColor = $.rgbToHex(ans);1257512576radialGradient.addColorStop(0, colori);12577radialGradient.addColorStop(0.5, colori);12578radialGradient.addColorStop(1, endColor);12579ctx.fillStyle = radialGradient;12580}1258112582polar.rho = acum + config.sliceOffset;12583polar.theta = begin;12584var p1coord = polar.getc(true);12585polar.theta = end;12586var p2coord = polar.getc(true);12587polar.rho += dimi;12588var p3coord = polar.getc(true);12589polar.theta = begin;12590var p4coord = polar.getc(true);1259112592ctx.beginPath();12593//fixing FF arc method + fill12594ctx.arc(xpos, ypos, acum + .01, begin, end, false);12595ctx.arc(xpos, ypos, acum + dimi + .01, end, begin, true);12596ctx.fill();12597if(border && border.name == stringArray[i]) {12598opt.acum = acum;12599opt.dimValue = dimArray[i];12600opt.begin = begin;12601opt.end = end;12602}12603acum += (dimi || 0);12604valAcum += (valueArray[i] || 0);12605}12606if(border) {12607ctx.save();12608ctx.globalCompositeOperation = "source-over";12609ctx.lineWidth = 2;12610ctx.strokeStyle = border.color;12611var s = begin < end? 1 : -1;12612ctx.beginPath();12613//fixing FF arc method + fill12614ctx.arc(xpos, ypos, opt.acum + .01 + 1, opt.begin, opt.end, false);12615ctx.arc(xpos, ypos, opt.acum + opt.dimValue + .01 - 1, opt.end, opt.begin, true);12616ctx.closePath();12617ctx.stroke();12618ctx.restore();12619}12620if(showLabels && label.type == 'Native') {12621ctx.save();12622ctx.fillStyle = ctx.strokeStyle = label.color;12623var scale = resizeLabels? node.getData('normalizedDim') : 1,12624fontSize = (label.size * scale) >> 0;12625fontSize = fontSize < +resizeLabels? +resizeLabels : fontSize;1262612627ctx.font = label.style + ' ' + fontSize + 'px ' + label.family;12628ctx.textBaseline = 'middle';12629ctx.textAlign = 'center';1263012631polar.rho = acum + config.labelOffset + config.sliceOffset;12632polar.theta = node.pos.theta;12633var cart = polar.getc(true);1263412635ctx.fillText(node.name, cart.x, cart.y);12636ctx.restore();12637}12638}12639},12640'contains': function(node, pos) {12641if (this.nodeTypes['none'].anglecontains.call(this, node, pos)) {12642var rho = Math.sqrt(pos.x * pos.x + pos.y * pos.y);12643var ld = this.config.levelDistance, d = node._depth;12644var config = node.getData('config');12645if(rho <=ld * d + config.sliceOffset) {12646var dimArray = node.getData('dimArray');12647for(var i=0,l=dimArray.length,acum=config.sliceOffset; i<l; i++) {12648var dimi = dimArray[i];12649if(rho >= acum && rho <= acum + dimi) {12650return {12651name: node.getData('stringArray')[i],12652color: node.getData('colorArray')[i],12653value: node.getData('valueArray')[i],12654label: node.name12655};12656}12657acum += dimi;12658}12659}12660return false;1266112662}12663return false;12664}12665}12666});1266712668/*12669Class: PieChart1267012671A visualization that displays stacked bar charts.1267212673Constructor Options:1267412675See <Options.PieChart>.1267612677*/12678$jit.PieChart = new Class({12679sb: null,12680colors: ["#416D9C", "#70A35E", "#EBB056", "#C74243", "#83548B", "#909291", "#557EAA"],12681selected: {},12682busy: false,1268312684initialize: function(opt) {12685this.controller = this.config =12686$.merge(Options("Canvas", "PieChart", "Label"), {12687Label: { type: 'Native' }12688}, opt);12689this.initializeViz();12690},1269112692initializeViz: function() {12693var config = this.config, that = this;12694var nodeType = config.type.split(":")[0];12695var delegate = new $jit.Sunburst({12696injectInto: config.injectInto,12697width: config.width,12698height: config.height,12699useCanvas: config.useCanvas,12700withLabels: config.Label.type != 'Native',12701Label: {12702type: config.Label.type12703},12704Node: {12705overridable: true,12706type: 'piechart-' + nodeType,12707width: 1,12708height: 112709},12710Edge: {12711type: 'none'12712},12713Tips: {12714enable: config.Tips.enable,12715type: 'Native',12716force: true,12717onShow: function(tip, node, contains) {12718var elem = contains;12719config.Tips.onShow(tip, elem, node);12720}12721},12722Events: {12723enable: true,12724type: 'Native',12725onClick: function(node, eventInfo, evt) {12726if(!config.Events.enable) return;12727var elem = eventInfo.getContains();12728config.Events.onClick(elem, eventInfo, evt);12729},12730onMouseMove: function(node, eventInfo, evt) {12731if(!config.hoveredColor) return;12732if(node) {12733var elem = eventInfo.getContains();12734that.select(node.id, elem.name, elem.index);12735} else {12736that.select(false, false, false);12737}12738}12739},12740onCreateLabel: function(domElement, node) {12741var labelConf = config.Label;12742if(config.showLabels) {12743var style = domElement.style;12744style.fontSize = labelConf.size + 'px';12745style.fontFamily = labelConf.family;12746style.color = labelConf.color;12747style.textAlign = 'center';12748domElement.innerHTML = node.name;12749}12750},12751onPlaceLabel: function(domElement, node) {12752if(!config.showLabels) return;12753var pos = node.pos.getp(true),12754dimArray = node.getData('dimArray'),12755span = node.getData('span') / 2,12756theta = node.pos.theta,12757begin = theta - span,12758end = theta + span,12759polar = new Polar;1276012761var showLabels = config.showLabels,12762resizeLabels = config.resizeLabels,12763label = config.Label;1276412765if (dimArray) {12766for (var i=0, l=dimArray.length, acum=0; i<l; i++) {12767acum += dimArray[i];12768}12769var scale = resizeLabels? node.getData('normalizedDim') : 1,12770fontSize = (label.size * scale) >> 0;12771fontSize = fontSize < +resizeLabels? +resizeLabels : fontSize;12772domElement.style.fontSize = fontSize + 'px';12773polar.rho = acum + config.labelOffset + config.sliceOffset;12774polar.theta = (begin + end) / 2;12775var pos = polar.getc(true);12776var radius = that.canvas.getSize();12777var labelPos = {12778x: Math.round(pos.x + radius.width / 2),12779y: Math.round(pos.y + radius.height / 2)12780};12781domElement.style.left = labelPos.x + 'px';12782domElement.style.top = labelPos.y + 'px';12783}12784}12785});1278612787var size = delegate.canvas.getSize(),12788min = Math.min;12789delegate.config.levelDistance = min(size.width, size.height)/212790- config.offset - config.sliceOffset;12791this.delegate = delegate;12792this.canvas = this.delegate.canvas;12793this.canvas.getCtx().globalCompositeOperation = 'lighter';12794},1279512796/*12797Method: loadJSON1279812799Loads JSON data into the visualization.1280012801Parameters:1280212803json - The JSON data format. This format is described in <http://blog.thejit.org/2010/04/24/new-javascript-infovis-toolkit-visualizations/#json-data-format>.1280412805Example:12806(start code js)12807var pieChart = new $jit.PieChart(options);12808pieChart.loadJSON(json);12809(end code)12810*/12811loadJSON: function(json) {12812var prefix = $.time(),12813ch = [],12814delegate = this.delegate,12815name = $.splat(json.label),12816nameLength = name.length,12817color = $.splat(json.color || this.colors),12818colorLength = color.length,12819config = this.config,12820gradient = !!config.type.split(":")[1],12821animate = config.animate,12822mono = nameLength == 1;1282312824for(var i=0, values=json.values, l=values.length; i<l; i++) {12825var val = values[i];12826var valArray = $.splat(val.values);12827ch.push({12828'id': prefix + val.label,12829'name': val.label,12830'data': {12831'value': valArray,12832'$valueArray': valArray,12833'$colorArray': mono? $.splat(color[i % colorLength]) : color,12834'$stringArray': name,12835'$gradient': gradient,12836'$config': config,12837'$angularWidth': $.reduce(valArray, function(x,y){return x+y;})12838},12839'children': []12840});12841}12842var root = {12843'id': prefix + '$root',12844'name': '',12845'data': {12846'$type': 'none',12847'$width': 1,12848'$height': 112849},12850'children': ch12851};12852delegate.loadJSON(root);1285312854this.normalizeDims();12855delegate.refresh();12856if(animate) {12857delegate.fx.animate({12858modes: ['node-property:dimArray'],12859duration:150012860});12861}12862},1286312864/*12865Method: updateJSON1286612867Use this method when updating values for the current JSON data. If the items specified by the JSON data already exist in the graph then their values will be updated.1286812869Parameters:1287012871json - (object) JSON data to be updated. The JSON format corresponds to the one described in <PieChart.loadJSON>.12872onComplete - (object) A callback object to be called when the animation transition when updating the data end.1287312874Example:1287512876(start code js)12877pieChart.updateJSON(json, {12878onComplete: function() {12879alert('update complete!');12880}12881});12882(end code)12883*/12884updateJSON: function(json, onComplete) {12885if(this.busy) return;12886this.busy = true;1288712888var delegate = this.delegate;12889var graph = delegate.graph;12890var values = json.values;12891var animate = this.config.animate;12892var that = this;12893$.each(values, function(v) {12894var n = graph.getByName(v.label),12895vals = $.splat(v.values);12896if(n) {12897n.setData('valueArray', vals);12898n.setData('angularWidth', $.reduce(vals, function(x,y){return x+y;}));12899if(json.label) {12900n.setData('stringArray', $.splat(json.label));12901}12902}12903});12904this.normalizeDims();12905if(animate) {12906delegate.compute('end');12907delegate.fx.animate({12908modes: ['node-property:dimArray:span', 'linear'],12909duration:1500,12910onComplete: function() {12911that.busy = false;12912onComplete && onComplete.onComplete();12913}12914});12915} else {12916delegate.refresh();12917}12918},1291912920//adds the little brown bar when hovering the node12921select: function(id, name) {12922if(!this.config.hoveredColor) return;12923var s = this.selected;12924if(s.id != id || s.name != name) {12925s.id = id;12926s.name = name;12927s.color = this.config.hoveredColor;12928this.delegate.graph.eachNode(function(n) {12929if(id == n.id) {12930n.setData('border', s);12931} else {12932n.setData('border', false);12933}12934});12935this.delegate.plot();12936}12937},1293812939/*12940Method: getLegend1294112942Returns an object containing as keys the legend names and as values hex strings with color values.1294312944Example:1294512946(start code js)12947var legend = pieChart.getLegend();12948(end code)12949*/12950getLegend: function() {12951var legend = {};12952var n;12953this.delegate.graph.getNode(this.delegate.root).eachAdjacency(function(adj) {12954n = adj.nodeTo;12955});12956var colors = n.getData('colorArray'),12957len = colors.length;12958$.each(n.getData('stringArray'), function(s, i) {12959legend[s] = colors[i % len];12960});12961return legend;12962},1296312964/*12965Method: getMaxValue1296612967Returns the maximum accumulated value for the stacks. This method is used for normalizing the graph heights according to the canvas height.1296812969Example:1297012971(start code js)12972var ans = pieChart.getMaxValue();12973(end code)1297412975In some cases it could be useful to override this method to normalize heights for a group of PieCharts, like when doing small multiples.1297612977Example:1297812979(start code js)12980//will return 100 for all PieChart instances,12981//displaying all of them with the same scale12982$jit.PieChart.implement({12983'getMaxValue': function() {12984return 100;12985}12986});12987(end code)1298812989*/12990getMaxValue: function() {12991var maxValue = 0;12992this.delegate.graph.eachNode(function(n) {12993var valArray = n.getData('valueArray'),12994acum = 0;12995$.each(valArray, function(v) {12996acum += +v;12997});12998maxValue = maxValue>acum? maxValue:acum;12999});13000return maxValue;13001},1300213003normalizeDims: function() {13004//number of elements13005var root = this.delegate.graph.getNode(this.delegate.root), l=0;13006root.eachAdjacency(function() {13007l++;13008});13009var maxValue = this.getMaxValue() || 1,13010config = this.config,13011animate = config.animate,13012rho = this.delegate.config.levelDistance;13013this.delegate.graph.eachNode(function(n) {13014var acum = 0, animateValue = [];13015$.each(n.getData('valueArray'), function(v) {13016acum += +v;13017animateValue.push(1);13018});13019var stat = (animateValue.length == 1) && !config.updateHeights;13020if(animate) {13021n.setData('dimArray', $.map(n.getData('valueArray'), function(n) {13022return stat? rho: (n * rho / maxValue);13023}), 'end');13024var dimArray = n.getData('dimArray');13025if(!dimArray) {13026n.setData('dimArray', animateValue);13027}13028} else {13029n.setData('dimArray', $.map(n.getData('valueArray'), function(n) {13030return stat? rho : (n * rho / maxValue);13031}));13032}13033n.setData('normalizedDim', acum / maxValue);13034});13035}13036});130371303813039/*13040* Class: Layouts.TM13041*13042* Implements TreeMaps layouts (SliceAndDice, Squarified, Strip).13043*13044* Implemented By:13045*13046* <TM>13047*13048*/13049Layouts.TM = {};1305013051Layouts.TM.SliceAndDice = new Class({13052compute: function(prop) {13053var root = this.graph.getNode(this.clickedNode && this.clickedNode.id || this.root);13054this.controller.onBeforeCompute(root);13055var size = this.canvas.getSize(),13056config = this.config,13057width = size.width,13058height = size.height;13059this.graph.computeLevels(this.root, 0, "ignore");13060//set root position and dimensions13061root.getPos(prop).setc(-width/2, -height/2);13062root.setData('width', width, prop);13063root.setData('height', height + config.titleHeight, prop);13064this.computePositions(root, root, this.layout.orientation, prop);13065this.controller.onAfterCompute(root);13066},1306713068computePositions: function(par, ch, orn, prop) {13069//compute children areas13070var totalArea = 0;13071par.eachSubnode(function(n) {13072totalArea += n.getData('area', prop);13073});1307413075var config = this.config,13076offst = config.offset,13077width = par.getData('width', prop),13078height = Math.max(par.getData('height', prop) - config.titleHeight, 0),13079fact = par == ch? 1 : (ch.getData('area', prop) / totalArea);1308013081var otherSize, size, dim, pos, pos2, posth, pos2th;13082var horizontal = (orn == "h");13083if(horizontal) {13084orn = 'v';13085otherSize = height;13086size = width * fact;13087dim = 'height';13088pos = 'y';13089pos2 = 'x';13090posth = config.titleHeight;13091pos2th = 0;13092} else {13093orn = 'h';13094otherSize = height * fact;13095size = width;13096dim = 'width';13097pos = 'x';13098pos2 = 'y';13099posth = 0;13100pos2th = config.titleHeight;13101}13102var cpos = ch.getPos(prop);13103ch.setData('width', size, prop);13104ch.setData('height', otherSize, prop);13105var offsetSize = 0, tm = this;13106ch.eachSubnode(function(n) {13107var p = n.getPos(prop);13108p[pos] = offsetSize + cpos[pos] + posth;13109p[pos2] = cpos[pos2] + pos2th;13110tm.computePositions(ch, n, orn, prop);13111offsetSize += n.getData(dim, prop);13112});13113}1311413115});1311613117Layouts.TM.Area = {13118/*13119Method: compute1312013121Called by loadJSON to calculate recursively all node positions and lay out the tree.1312213123Parameters:1312413125json - A JSON tree. See also <Loader.loadJSON>.13126coord - A coordinates object specifying width, height, left and top style properties.13127*/13128compute: function(prop) {13129prop = prop || "current";13130var root = this.graph.getNode(this.clickedNode && this.clickedNode.id || this.root);13131this.controller.onBeforeCompute(root);13132var config = this.config,13133size = this.canvas.getSize(),13134width = size.width,13135height = size.height,13136offst = config.offset,13137offwdth = width - offst,13138offhght = height - offst;13139this.graph.computeLevels(this.root, 0, "ignore");13140//set root position and dimensions13141root.getPos(prop).setc(-width/2, -height/2);13142root.setData('width', width, prop);13143root.setData('height', height, prop);13144//create a coordinates object13145var coord = {13146'top': -height/2 + config.titleHeight,13147'left': -width/2,13148'width': offwdth,13149'height': offhght - config.titleHeight13150};13151this.computePositions(root, coord, prop);13152this.controller.onAfterCompute(root);13153},1315413155/*13156Method: computeDim1315713158Computes dimensions and positions of a group of nodes13159according to a custom layout row condition.1316013161Parameters:1316213163tail - An array of nodes.13164initElem - An array of nodes (containing the initial node to be laid).13165w - A fixed dimension where nodes will be layed out.13166coord - A coordinates object specifying width, height, left and top style properties.13167comp - A custom comparison function13168*/13169computeDim: function(tail, initElem, w, coord, comp, prop) {13170if(tail.length + initElem.length == 1) {13171var l = (tail.length == 1)? tail : initElem;13172this.layoutLast(l, w, coord, prop);13173return;13174}13175if(tail.length >= 2 && initElem.length == 0) {13176initElem = [tail.shift()];13177}13178if(tail.length == 0) {13179if(initElem.length > 0) this.layoutRow(initElem, w, coord, prop);13180return;13181}13182var c = tail[0];13183if(comp(initElem, w) >= comp([c].concat(initElem), w)) {13184this.computeDim(tail.slice(1), initElem.concat([c]), w, coord, comp, prop);13185} else {13186var newCoords = this.layoutRow(initElem, w, coord, prop);13187this.computeDim(tail, [], newCoords.dim, newCoords, comp, prop);13188}13189},131901319113192/*13193Method: worstAspectRatio1319413195Calculates the worst aspect ratio of a group of rectangles.1319613197See also:1319813199<http://en.wikipedia.org/wiki/Aspect_ratio>1320013201Parameters:1320213203ch - An array of nodes.13204w - The fixed dimension where rectangles are being laid out.1320513206Returns:1320713208The worst aspect ratio.132091321013211*/13212worstAspectRatio: function(ch, w) {13213if(!ch || ch.length == 0) return Number.MAX_VALUE;13214var areaSum = 0, maxArea = 0, minArea = Number.MAX_VALUE;13215for(var i=0, l=ch.length; i<l; i++) {13216var area = ch[i]._area;13217areaSum += area;13218minArea = minArea < area? minArea : area;13219maxArea = maxArea > area? maxArea : area;13220}13221var sqw = w * w, sqAreaSum = areaSum * areaSum;13222return Math.max(sqw * maxArea / sqAreaSum,13223sqAreaSum / (sqw * minArea));13224},1322513226/*13227Method: avgAspectRatio1322813229Calculates the average aspect ratio of a group of rectangles.1323013231See also:1323213233<http://en.wikipedia.org/wiki/Aspect_ratio>1323413235Parameters:1323613237ch - An array of nodes.13238w - The fixed dimension where rectangles are being laid out.1323913240Returns:1324113242The average aspect ratio.132431324413245*/13246avgAspectRatio: function(ch, w) {13247if(!ch || ch.length == 0) return Number.MAX_VALUE;13248var arSum = 0;13249for(var i=0, l=ch.length; i<l; i++) {13250var area = ch[i]._area;13251var h = area / w;13252arSum += w > h? w / h : h / w;13253}13254return arSum / l;13255},1325613257/*13258layoutLast1325913260Performs the layout of the last computed sibling.1326113262Parameters:1326313264ch - An array of nodes.13265w - A fixed dimension where nodes will be layed out.13266coord - A coordinates object specifying width, height, left and top style properties.13267*/13268layoutLast: function(ch, w, coord, prop) {13269var child = ch[0];13270child.getPos(prop).setc(coord.left, coord.top);13271child.setData('width', coord.width, prop);13272child.setData('height', coord.height, prop);13273}13274};132751327613277Layouts.TM.Squarified = new Class({13278Implements: Layouts.TM.Area,1327913280computePositions: function(node, coord, prop) {13281var config = this.config,13282max = Math.max;1328313284if (coord.width >= coord.height)13285this.layout.orientation = 'h';13286else13287this.layout.orientation = 'v';1328813289var ch = node.getSubnodes([1, 1], "ignore");13290if(ch.length > 0) {13291this.processChildrenLayout(node, ch, coord, prop);13292for(var i=0, l=ch.length; i<l; i++) {13293var chi = ch[i],13294offst = config.offset,13295height = max(chi.getData('height', prop) - offst - config.titleHeight, 0),13296width = max(chi.getData('width', prop) - offst, 0),13297chipos = chi.getPos(prop);1329813299coord = {13300'width': width,13301'height': height,13302'top': chipos.y + config.titleHeight,13303'left': chipos.x13304};13305this.computePositions(chi, coord, prop);13306}13307}13308},1330913310/*13311Method: processChildrenLayout1331213313Computes children real areas and other useful parameters for performing the Squarified algorithm.1331413315Parameters:1331613317par - The parent node of the json subtree.13318ch - An Array of nodes13319coord - A coordinates object specifying width, height, left and top style properties.13320*/13321processChildrenLayout: function(par, ch, coord, prop) {13322//compute children real areas13323var parentArea = coord.width * coord.height;13324var i, l=ch.length, totalChArea=0, chArea = [];13325for(i=0; i<l; i++) {13326chArea[i] = parseFloat(ch[i].getData('area', prop));13327totalChArea += chArea[i];13328}13329for(i=0; i<l; i++) {13330ch[i]._area = parentArea * chArea[i] / totalChArea;13331}13332var minimumSideValue = this.layout.horizontal()? coord.height : coord.width;13333ch.sort(function(a, b) {13334var diff = b._area - a._area;13335return diff? diff : (b.id == a.id? 0 : (b.id < a.id? 1 : -1));13336});13337var initElem = [ch[0]];13338var tail = ch.slice(1);13339this.squarify(tail, initElem, minimumSideValue, coord, prop);13340},1334113342/*13343Method: squarify1334413345Performs an heuristic method to calculate div elements sizes in order to have a good aspect ratio.1334613347Parameters:1334813349tail - An array of nodes.13350initElem - An array of nodes, containing the initial node to be laid out.13351w - A fixed dimension where nodes will be laid out.13352coord - A coordinates object specifying width, height, left and top style properties.13353*/13354squarify: function(tail, initElem, w, coord, prop) {13355this.computeDim(tail, initElem, w, coord, this.worstAspectRatio, prop);13356},1335713358/*13359Method: layoutRow1336013361Performs the layout of an array of nodes.1336213363Parameters:1336413365ch - An array of nodes.13366w - A fixed dimension where nodes will be laid out.13367coord - A coordinates object specifying width, height, left and top style properties.13368*/13369layoutRow: function(ch, w, coord, prop) {13370if(this.layout.horizontal()) {13371return this.layoutV(ch, w, coord, prop);13372} else {13373return this.layoutH(ch, w, coord, prop);13374}13375},1337613377layoutV: function(ch, w, coord, prop) {13378var totalArea = 0, rnd = function(x) { return x; };13379$.each(ch, function(elem) { totalArea += elem._area; });13380var width = rnd(totalArea / w), top = 0;13381for(var i=0, l=ch.length; i<l; i++) {13382var h = rnd(ch[i]._area / width);13383var chi = ch[i];13384chi.getPos(prop).setc(coord.left, coord.top + top);13385chi.setData('width', width, prop);13386chi.setData('height', h, prop);13387top += h;13388}13389var ans = {13390'height': coord.height,13391'width': coord.width - width,13392'top': coord.top,13393'left': coord.left + width13394};13395//take minimum side value.13396ans.dim = Math.min(ans.width, ans.height);13397if(ans.dim != ans.height) this.layout.change();13398return ans;13399},1340013401layoutH: function(ch, w, coord, prop) {13402var totalArea = 0;13403$.each(ch, function(elem) { totalArea += elem._area; });13404var height = totalArea / w,13405top = coord.top,13406left = 0;1340713408for(var i=0, l=ch.length; i<l; i++) {13409var chi = ch[i];13410var w = chi._area / height;13411chi.getPos(prop).setc(coord.left + left, top);13412chi.setData('width', w, prop);13413chi.setData('height', height, prop);13414left += w;13415}13416var ans = {13417'height': coord.height - height,13418'width': coord.width,13419'top': coord.top + height,13420'left': coord.left13421};13422ans.dim = Math.min(ans.width, ans.height);13423if(ans.dim != ans.width) this.layout.change();13424return ans;13425}13426});1342713428Layouts.TM.Strip = new Class({13429Implements: Layouts.TM.Area,1343013431/*13432Method: compute1343313434Called by loadJSON to calculate recursively all node positions and lay out the tree.1343513436Parameters:1343713438json - A JSON subtree. See also <Loader.loadJSON>.13439coord - A coordinates object specifying width, height, left and top style properties.13440*/13441computePositions: function(node, coord, prop) {13442var ch = node.getSubnodes([1, 1], "ignore"),13443config = this.config,13444max = Math.max;13445if(ch.length > 0) {13446this.processChildrenLayout(node, ch, coord, prop);13447for(var i=0, l=ch.length; i<l; i++) {13448var chi = ch[i];13449var offst = config.offset,13450height = max(chi.getData('height', prop) - offst - config.titleHeight, 0),13451width = max(chi.getData('width', prop) - offst, 0);13452var chipos = chi.getPos(prop);13453coord = {13454'width': width,13455'height': height,13456'top': chipos.y + config.titleHeight,13457'left': chipos.x13458};13459this.computePositions(chi, coord, prop);13460}13461}13462},1346313464/*13465Method: processChildrenLayout1346613467Computes children real areas and other useful parameters for performing the Strip algorithm.1346813469Parameters:1347013471par - The parent node of the json subtree.13472ch - An Array of nodes13473coord - A coordinates object specifying width, height, left and top style properties.13474*/13475processChildrenLayout: function(par, ch, coord, prop) {13476//compute children real areas13477var parentArea = coord.width * coord.height;13478var i, l=ch.length, totalChArea=0, chArea = [];13479for(i=0; i<l; i++) {13480chArea[i] = +ch[i].getData('area', prop);13481totalChArea += chArea[i];13482}13483for(i=0; i<l; i++) {13484ch[i]._area = parentArea * chArea[i] / totalChArea;13485}13486var side = this.layout.horizontal()? coord.width : coord.height;13487var initElem = [ch[0]];13488var tail = ch.slice(1);13489this.stripify(tail, initElem, side, coord, prop);13490},1349113492/*13493Method: stripify1349413495Performs an heuristic method to calculate div elements sizes in order to have13496a good compromise between aspect ratio and order.1349713498Parameters:1349913500tail - An array of nodes.13501initElem - An array of nodes.13502w - A fixed dimension where nodes will be layed out.13503coord - A coordinates object specifying width, height, left and top style properties.13504*/13505stripify: function(tail, initElem, w, coord, prop) {13506this.computeDim(tail, initElem, w, coord, this.avgAspectRatio, prop);13507},1350813509/*13510Method: layoutRow1351113512Performs the layout of an array of nodes.1351313514Parameters:1351513516ch - An array of nodes.13517w - A fixed dimension where nodes will be laid out.13518coord - A coordinates object specifying width, height, left and top style properties.13519*/13520layoutRow: function(ch, w, coord, prop) {13521if(this.layout.horizontal()) {13522return this.layoutH(ch, w, coord, prop);13523} else {13524return this.layoutV(ch, w, coord, prop);13525}13526},1352713528layoutV: function(ch, w, coord, prop) {13529var totalArea = 0;13530$.each(ch, function(elem) { totalArea += elem._area; });13531var width = totalArea / w, top = 0;13532for(var i=0, l=ch.length; i<l; i++) {13533var chi = ch[i];13534var h = chi._area / width;13535chi.getPos(prop).setc(coord.left,13536coord.top + (w - h - top));13537chi.setData('width', width, prop);13538chi.setData('height', h, prop);13539top += h;13540}1354113542return {13543'height': coord.height,13544'width': coord.width - width,13545'top': coord.top,13546'left': coord.left + width,13547'dim': w13548};13549},1355013551layoutH: function(ch, w, coord, prop) {13552var totalArea = 0;13553$.each(ch, function(elem) { totalArea += elem._area; });13554var height = totalArea / w,13555top = coord.height - height,13556left = 0;1355713558for(var i=0, l=ch.length; i<l; i++) {13559var chi = ch[i];13560var s = chi._area / height;13561chi.getPos(prop).setc(coord.left + left, coord.top + top);13562chi.setData('width', s, prop);13563chi.setData('height', height, prop);13564left += s;13565}13566return {13567'height': coord.height - height,13568'width': coord.width,13569'top': coord.top,13570'left': coord.left,13571'dim': w13572};13573}13574});135751357613577/*13578* Class: Layouts.Icicle13579*13580* Implements the icicle tree layout.13581*13582* Implemented By:13583*13584* <Icicle>13585*13586*/1358713588Layouts.Icicle = new Class({13589/*13590* Method: compute13591*13592* Called by loadJSON to calculate all node positions.13593*13594* Parameters:13595*13596* posType - The nodes' position to compute. Either "start", "end" or13597* "current". Defaults to "current".13598*/13599compute: function(posType) {13600posType = posType || "current";1360113602var root = this.graph.getNode(this.root),13603config = this.config,13604size = this.canvas.getSize(),13605width = size.width,13606height = size.height,13607offset = config.offset,13608levelsToShow = config.constrained ? config.levelsToShow : Number.MAX_VALUE;1360913610this.controller.onBeforeCompute(root);1361113612Graph.Util.computeLevels(this.graph, root.id, 0, "ignore");1361313614var treeDepth = 0;1361513616Graph.Util.eachLevel(root, 0, false, function (n, d) { if(d > treeDepth) treeDepth = d; });1361713618var startNode = this.graph.getNode(this.clickedNode && this.clickedNode.id || root.id);13619var maxDepth = Math.min(treeDepth, levelsToShow-1);13620var initialDepth = startNode._depth;13621if(this.layout.horizontal()) {13622this.computeSubtree(startNode, -width/2, -height/2, width/(maxDepth+1), height, initialDepth, maxDepth, posType);13623} else {13624this.computeSubtree(startNode, -width/2, -height/2, width, height/(maxDepth+1), initialDepth, maxDepth, posType);13625}13626},1362713628computeSubtree: function (root, x, y, width, height, initialDepth, maxDepth, posType) {13629root.getPos(posType).setc(x, y);13630root.setData('width', width, posType);13631root.setData('height', height, posType);1363213633var nodeLength, prevNodeLength = 0, totalDim = 0;13634var children = Graph.Util.getSubnodes(root, [1, 1], 'ignore'); // next level from this node1363513636if(!children.length)13637return;1363813639$.each(children, function(e) { totalDim += e.getData('dim'); });1364013641for(var i=0, l=children.length; i < l; i++) {13642if(this.layout.horizontal()) {13643nodeLength = height * children[i].getData('dim') / totalDim;13644this.computeSubtree(children[i], x+width, y, width, nodeLength, initialDepth, maxDepth, posType);13645y += nodeLength;13646} else {13647nodeLength = width * children[i].getData('dim') / totalDim;13648this.computeSubtree(children[i], x, y+height, nodeLength, height, initialDepth, maxDepth, posType);13649x += nodeLength;13650}13651}13652}13653});13654136551365613657/*13658* File: Icicle.js13659*13660*/1366113662/*13663Class: Icicle1366413665Icicle space filling visualization.1366613667Implements:1366813669All <Loader> methods1367013671Constructor Options:1367213673Inherits options from1367413675- <Options.Canvas>13676- <Options.Controller>13677- <Options.Node>13678- <Options.Edge>13679- <Options.Label>13680- <Options.Events>13681- <Options.Tips>13682- <Options.NodeStyles>13683- <Options.Navigation>1368413685Additionally, there are other parameters and some default values changed1368613687orientation - (string) Default's *h*. Whether to set horizontal or vertical layouts. Possible values are 'h' and 'v'.13688offset - (number) Default's *2*. Boxes offset.13689constrained - (boolean) Default's *false*. Whether to show the entire tree when loaded or just the number of levels specified by _levelsToShow_.13690levelsToShow - (number) Default's *3*. The number of levels to show for a subtree. This number is relative to the selected node.13691animate - (boolean) Default's *false*. Whether to animate transitions.13692Node.type - Described in <Options.Node>. Default's *rectangle*.13693Label.type - Described in <Options.Label>. Default's *Native*.13694duration - Described in <Options.Fx>. Default's *700*.13695fps - Described in <Options.Fx>. Default's *45*.1369613697Instance Properties:1369813699canvas - Access a <Canvas> instance.13700graph - Access a <Graph> instance.13701op - Access a <Icicle.Op> instance.13702fx - Access a <Icicle.Plot> instance.13703labels - Access a <Icicle.Label> interface implementation.1370413705*/1370613707$jit.Icicle = new Class({13708Implements: [ Loader, Extras, Layouts.Icicle ],1370913710layout: {13711orientation: "h",13712vertical: function(){13713return this.orientation == "v";13714},13715horizontal: function(){13716return this.orientation == "h";13717},13718change: function(){13719this.orientation = this.vertical()? "h" : "v";13720}13721},1372213723initialize: function(controller) {13724var config = {13725animate: false,13726orientation: "h",13727offset: 2,13728levelsToShow: Number.MAX_VALUE,13729constrained: false,13730Node: {13731type: 'rectangle',13732overridable: true13733},13734Edge: {13735type: 'none'13736},13737Label: {13738type: 'Native'13739},13740duration: 700,13741fps: 4513742};1374313744var opts = Options("Canvas", "Node", "Edge", "Fx", "Tips", "NodeStyles",13745"Events", "Navigation", "Controller", "Label");13746this.controller = this.config = $.merge(opts, config, controller);13747this.layout.orientation = this.config.orientation;1374813749var canvasConfig = this.config;13750if (canvasConfig.useCanvas) {13751this.canvas = canvasConfig.useCanvas;13752this.config.labelContainer = this.canvas.id + '-label';13753} else {13754this.canvas = new Canvas(this, canvasConfig);13755this.config.labelContainer = (typeof canvasConfig.injectInto == 'string'? canvasConfig.injectInto : canvasConfig.injectInto.id) + '-label';13756}1375713758this.graphOptions = {13759'klass': Complex,13760'Node': {13761'selected': false,13762'exist': true,13763'drawn': true13764}13765};1376613767this.graph = new Graph(13768this.graphOptions, this.config.Node, this.config.Edge, this.config.Label);1376913770this.labels = new $jit.Icicle.Label[this.config.Label.type](this);13771this.fx = new $jit.Icicle.Plot(this, $jit.Icicle);13772this.op = new $jit.Icicle.Op(this);13773this.group = new $jit.Icicle.Group(this);13774this.clickedNode = null;1377513776this.initializeExtras();13777},1377813779/*13780Method: refresh1378113782Computes positions and plots the tree.13783*/13784refresh: function(){13785var labelType = this.config.Label.type;13786if(labelType != 'Native') {13787var that = this;13788this.graph.eachNode(function(n) { that.labels.hideLabel(n, false); });13789}13790this.compute();13791this.plot();13792},1379313794/*13795Method: plot1379613797Plots the Icicle visualization. This is a shortcut to *fx.plot*.1379813799*/13800plot: function(){13801this.fx.plot(this.config);13802},1380313804/*13805Method: enter1380613807Sets the node as root.1380813809Parameters:1381013811node - (object) A <Graph.Node>.1381213813*/13814enter: function (node) {13815if (this.busy)13816return;13817this.busy = true;1381813819var that = this,13820config = this.config;1382113822var callback = {13823onComplete: function() {13824//compute positions of newly inserted nodes13825if(config.request)13826that.compute();1382713828if(config.animate) {13829that.graph.nodeList.setDataset(['current', 'end'], {13830'alpha': [1, 0] //fade nodes13831});1383213833Graph.Util.eachSubgraph(node, function(n) {13834n.setData('alpha', 1, 'end');13835}, "ignore");1383613837that.fx.animate({13838duration: 500,13839modes:['node-property:alpha'],13840onComplete: function() {13841that.clickedNode = node;13842that.compute('end');1384313844that.fx.animate({13845modes:['linear', 'node-property:width:height'],13846duration: 1000,13847onComplete: function() {13848that.busy = false;13849that.clickedNode = node;13850}13851});13852}13853});13854} else {13855that.clickedNode = node;13856that.busy = false;13857that.refresh();13858}13859}13860};1386113862if(config.request) {13863this.requestNodes(clickedNode, callback);13864} else {13865callback.onComplete();13866}13867},1386813869/*13870Method: out1387113872Sets the parent node of the current selected node as root.1387313874*/13875out: function(){13876if(this.busy)13877return;1387813879var that = this,13880GUtil = Graph.Util,13881config = this.config,13882graph = this.graph,13883parents = GUtil.getParents(graph.getNode(this.clickedNode && this.clickedNode.id || this.root)),13884parent = parents[0],13885clickedNode = parent,13886previousClickedNode = this.clickedNode;1388713888this.busy = true;13889this.events.hoveredNode = false;1389013891if(!parent) {13892this.busy = false;13893return;13894}1389513896//final plot callback13897callback = {13898onComplete: function() {13899that.clickedNode = parent;13900if(config.request) {13901that.requestNodes(parent, {13902onComplete: function() {13903that.compute();13904that.plot();13905that.busy = false;13906}13907});13908} else {13909that.compute();13910that.plot();13911that.busy = false;13912}13913}13914};1391513916//animate node positions13917if(config.animate) {13918this.clickedNode = clickedNode;13919this.compute('end');13920//animate the visible subtree only13921this.clickedNode = previousClickedNode;13922this.fx.animate({13923modes:['linear', 'node-property:width:height'],13924duration: 1000,13925onComplete: function() {13926//animate the parent subtree13927that.clickedNode = clickedNode;13928//change nodes alpha13929graph.nodeList.setDataset(['current', 'end'], {13930'alpha': [0, 1]13931});13932GUtil.eachSubgraph(previousClickedNode, function(node) {13933node.setData('alpha', 1);13934}, "ignore");13935that.fx.animate({13936duration: 500,13937modes:['node-property:alpha'],13938onComplete: function() {13939callback.onComplete();13940}13941});13942}13943});13944} else {13945callback.onComplete();13946}13947},13948requestNodes: function(node, onComplete){13949var handler = $.merge(this.controller, onComplete),13950levelsToShow = this.config.constrained ? this.config.levelsToShow : Number.MAX_VALUE;1395113952if (handler.request) {13953var leaves = [], d = node._depth;13954Graph.Util.eachLevel(node, 0, levelsToShow, function(n){13955if (n.drawn && !Graph.Util.anySubnode(n)) {13956leaves.push(n);13957n._level = n._depth - d;13958if (this.config.constrained)13959n._level = levelsToShow - n._level;1396013961}13962});13963this.group.requestNodes(leaves, handler);13964} else {13965handler.onComplete();13966}13967}13968});1396913970/*13971Class: Icicle.Op1397213973Custom extension of <Graph.Op>.1397413975Extends:1397613977All <Graph.Op> methods1397813979See also:1398013981<Graph.Op>1398213983*/13984$jit.Icicle.Op = new Class({1398513986Implements: Graph.Op1398713988});1398913990/*13991* Performs operations on group of nodes.13992*/13993$jit.Icicle.Group = new Class({1399413995initialize: function(viz){13996this.viz = viz;13997this.canvas = viz.canvas;13998this.config = viz.config;13999},1400014001/*14002* Calls the request method on the controller to request a subtree for each node.14003*/14004requestNodes: function(nodes, controller){14005var counter = 0, len = nodes.length, nodeSelected = {};14006var complete = function(){14007controller.onComplete();14008};14009var viz = this.viz;14010if (len == 0)14011complete();14012for(var i = 0; i < len; i++) {14013nodeSelected[nodes[i].id] = nodes[i];14014controller.request(nodes[i].id, nodes[i]._level, {14015onComplete: function(nodeId, data){14016if (data && data.children) {14017data.id = nodeId;14018viz.op.sum(data, {14019type: 'nothing'14020});14021}14022if (++counter == len) {14023Graph.Util.computeLevels(viz.graph, viz.root, 0);14024complete();14025}14026}14027});14028}14029}14030});1403114032/*14033Class: Icicle.Plot1403414035Custom extension of <Graph.Plot>.1403614037Extends:1403814039All <Graph.Plot> methods1404014041See also:1404214043<Graph.Plot>1404414045*/14046$jit.Icicle.Plot = new Class({14047Implements: Graph.Plot,1404814049plot: function(opt, animating){14050opt = opt || this.viz.controller;14051var viz = this.viz,14052graph = viz.graph,14053root = graph.getNode(viz.clickedNode && viz.clickedNode.id || viz.root),14054initialDepth = root._depth;1405514056viz.canvas.clear();14057this.plotTree(root, $.merge(opt, {14058'withLabels': true,14059'hideLabels': false,14060'plotSubtree': function(root, node) {14061return !viz.config.constrained ||14062(node._depth - initialDepth < viz.config.levelsToShow);14063}14064}), animating);14065}14066});1406714068/*14069Class: Icicle.Label1407014071Custom extension of <Graph.Label>.14072Contains custom <Graph.Label.SVG>, <Graph.Label.HTML> and <Graph.Label.Native> extensions.1407314074Extends:1407514076All <Graph.Label> methods and subclasses.1407714078See also:1407914080<Graph.Label>, <Graph.Label.Native>, <Graph.Label.HTML>, <Graph.Label.SVG>.1408114082*/14083$jit.Icicle.Label = {};1408414085/*14086Icicle.Label.Native1408714088Custom extension of <Graph.Label.Native>.1408914090Extends:1409114092All <Graph.Label.Native> methods1409314094See also:1409514096<Graph.Label.Native>1409714098*/14099$jit.Icicle.Label.Native = new Class({14100Implements: Graph.Label.Native,1410114102renderLabel: function(canvas, node, controller) {14103var ctx = canvas.getCtx(),14104width = node.getData('width'),14105height = node.getData('height'),14106size = node.getLabelData('size'),14107m = ctx.measureText(node.name);1410814109// Guess as much as possible if the label will fit in the node14110if(height < (size * 1.5) || width < m.width)14111return;1411214113var pos = node.pos.getc(true);14114ctx.fillText(node.name,14115pos.x + width / 2,14116pos.y + height / 2);14117}14118});1411914120/*14121Icicle.Label.SVG1412214123Custom extension of <Graph.Label.SVG>.1412414125Extends:1412614127All <Graph.Label.SVG> methods1412814129See also:1413014131<Graph.Label.SVG>14132*/14133$jit.Icicle.Label.SVG = new Class( {14134Implements: Graph.Label.SVG,1413514136initialize: function(viz){14137this.viz = viz;14138},1413914140/*14141placeLabel1414214143Overrides abstract method placeLabel in <Graph.Plot>.1414414145Parameters:1414614147tag - A DOM label element.14148node - A <Graph.Node>.14149controller - A configuration/controller object passed to the visualization.14150*/14151placeLabel: function(tag, node, controller){14152var pos = node.pos.getc(true), canvas = this.viz.canvas;14153var radius = canvas.getSize();14154var labelPos = {14155x: Math.round(pos.x + radius.width / 2),14156y: Math.round(pos.y + radius.height / 2)14157};14158tag.setAttribute('x', labelPos.x);14159tag.setAttribute('y', labelPos.y);1416014161controller.onPlaceLabel(tag, node);14162}14163});1416414165/*14166Icicle.Label.HTML1416714168Custom extension of <Graph.Label.HTML>.1416914170Extends:1417114172All <Graph.Label.HTML> methods.1417314174See also:1417514176<Graph.Label.HTML>1417714178*/14179$jit.Icicle.Label.HTML = new Class( {14180Implements: Graph.Label.HTML,1418114182initialize: function(viz){14183this.viz = viz;14184},1418514186/*14187placeLabel1418814189Overrides abstract method placeLabel in <Graph.Plot>.1419014191Parameters:1419214193tag - A DOM label element.14194node - A <Graph.Node>.14195controller - A configuration/controller object passed to the visualization.14196*/14197placeLabel: function(tag, node, controller){14198var pos = node.pos.getc(true), canvas = this.viz.canvas;14199var radius = canvas.getSize();14200var labelPos = {14201x: Math.round(pos.x + radius.width / 2),14202y: Math.round(pos.y + radius.height / 2)14203};1420414205var style = tag.style;14206style.left = labelPos.x + 'px';14207style.top = labelPos.y + 'px';14208style.display = '';1420914210controller.onPlaceLabel(tag, node);14211}14212});1421314214/*14215Class: Icicle.Plot.NodeTypes1421614217This class contains a list of <Graph.Node> built-in types.14218Node types implemented are 'none', 'rectangle'.1421914220You can add your custom node types, customizing your visualization to the extreme.1422114222Example:1422314224(start code js)14225Icicle.Plot.NodeTypes.implement({14226'mySpecialType': {14227'render': function(node, canvas) {14228//print your custom node to canvas14229},14230//optional14231'contains': function(node, pos) {14232//return true if pos is inside the node or false otherwise14233}14234}14235});14236(end code)1423714238*/14239$jit.Icicle.Plot.NodeTypes = new Class( {14240'none': {14241'render': $.empty14242},1424314244'rectangle': {14245'render': function(node, canvas, animating) {14246var config = this.viz.config;14247var offset = config.offset;14248var width = node.getData('width');14249var height = node.getData('height');14250var border = node.getData('border');14251var pos = node.pos.getc(true);14252var posx = pos.x + offset / 2, posy = pos.y + offset / 2;14253var ctx = canvas.getCtx();1425414255if(width - offset < 2 || height - offset < 2) return;1425614257if(config.cushion) {14258var color = node.getData('color');14259var lg = ctx.createRadialGradient(posx + (width - offset)/2,14260posy + (height - offset)/2, 1,14261posx + (width-offset)/2, posy + (height-offset)/2,14262width < height? height : width);14263var colorGrad = $.rgbToHex($.map($.hexToRgb(color),14264function(r) { return r * 0.3 >> 0; }));14265lg.addColorStop(0, color);14266lg.addColorStop(1, colorGrad);14267ctx.fillStyle = lg;14268}1426914270if (border) {14271ctx.strokeStyle = border;14272ctx.lineWidth = 3;14273}1427414275ctx.fillRect(posx, posy, Math.max(0, width - offset), Math.max(0, height - offset));14276border && ctx.strokeRect(pos.x, pos.y, width, height);14277},1427814279'contains': function(node, pos) {14280if(this.viz.clickedNode && !$jit.Graph.Util.isDescendantOf(node, this.viz.clickedNode.id)) return false;14281var npos = node.pos.getc(true),14282width = node.getData('width'),14283height = node.getData('height');14284return this.nodeHelper.rectangle.contains({x: npos.x + width/2, y: npos.y + height/2}, pos, width, height);14285}14286}14287});1428814289$jit.Icicle.Plot.EdgeTypes = new Class( {14290'none': $.empty14291});14292142931429414295/*14296* File: Layouts.ForceDirected.js14297*14298*/1429914300/*14301* Class: Layouts.ForceDirected14302*14303* Implements a Force Directed Layout.14304*14305* Implemented By:14306*14307* <ForceDirected>14308*14309* Credits:14310*14311* Marcus Cobden <http://marcuscobden.co.uk>14312*14313*/14314Layouts.ForceDirected = new Class({1431514316getOptions: function(random) {14317var s = this.canvas.getSize();14318var w = s.width, h = s.height;14319//count nodes14320var count = 0;14321this.graph.eachNode(function(n) {14322count++;14323});14324var k2 = w * h / count, k = Math.sqrt(k2);14325var l = this.config.levelDistance;1432614327return {14328width: w,14329height: h,14330tstart: w * 0.1,14331nodef: function(x) { return k2 / (x || 1); },14332edgef: function(x) { return /* x * x / k; */ k * (x - l); }14333};14334},1433514336compute: function(property, incremental) {14337var prop = $.splat(property || ['current', 'start', 'end']);14338var opt = this.getOptions();14339NodeDim.compute(this.graph, prop, this.config);14340this.graph.computeLevels(this.root, 0, "ignore");14341this.graph.eachNode(function(n) {14342$.each(prop, function(p) {14343var pos = n.getPos(p);14344if(pos.equals(Complex.KER)) {14345pos.x = opt.width/5 * (Math.random() - 0.5);14346pos.y = opt.height/5 * (Math.random() - 0.5);14347}14348//initialize disp vector14349n.disp = {};14350$.each(prop, function(p) {14351n.disp[p] = $C(0, 0);14352});14353});14354});14355this.computePositions(prop, opt, incremental);14356},1435714358computePositions: function(property, opt, incremental) {14359var times = this.config.iterations, i = 0, that = this;14360if(incremental) {14361(function iter() {14362for(var total=incremental.iter, j=0; j<total; j++) {14363opt.t = opt.tstart;14364if(times) opt.t *= (1 - i++/(times -1));14365that.computePositionStep(property, opt);14366if(times && i >= times) {14367incremental.onComplete();14368return;14369}14370}14371incremental.onStep(Math.round(i / (times -1) * 100));14372setTimeout(iter, 1);14373})();14374} else {14375for(; i < times; i++) {14376opt.t = opt.tstart * (1 - i/(times -1));14377this.computePositionStep(property, opt);14378}14379}14380},1438114382computePositionStep: function(property, opt) {14383var graph = this.graph;14384var min = Math.min, max = Math.max;14385var dpos = $C(0, 0);14386//calculate repulsive forces14387graph.eachNode(function(v) {14388//initialize disp14389$.each(property, function(p) {14390v.disp[p].x = 0; v.disp[p].y = 0;14391});14392graph.eachNode(function(u) {14393if(u.id != v.id) {14394$.each(property, function(p) {14395var vp = v.getPos(p), up = u.getPos(p);14396dpos.x = vp.x - up.x;14397dpos.y = vp.y - up.y;14398var norm = dpos.norm() || 1;14399v.disp[p].$add(dpos14400.$scale(opt.nodef(norm) / norm));14401});14402}14403});14404});14405//calculate attractive forces14406var T = !!graph.getNode(this.root).visited;14407graph.eachNode(function(node) {14408node.eachAdjacency(function(adj) {14409var nodeTo = adj.nodeTo;14410if(!!nodeTo.visited === T) {14411$.each(property, function(p) {14412var vp = node.getPos(p), up = nodeTo.getPos(p);14413dpos.x = vp.x - up.x;14414dpos.y = vp.y - up.y;14415var norm = dpos.norm() || 1;14416node.disp[p].$add(dpos.$scale(-opt.edgef(norm) / norm));14417nodeTo.disp[p].$add(dpos.$scale(-1));14418});14419}14420});14421node.visited = !T;14422});14423//arrange positions to fit the canvas14424var t = opt.t, w2 = opt.width / 2, h2 = opt.height / 2;14425graph.eachNode(function(u) {14426$.each(property, function(p) {14427var disp = u.disp[p];14428var norm = disp.norm() || 1;14429var p = u.getPos(p);14430p.$add($C(disp.x * min(Math.abs(disp.x), t) / norm,14431disp.y * min(Math.abs(disp.y), t) / norm));14432p.x = min(w2, max(-w2, p.x));14433p.y = min(h2, max(-h2, p.y));14434});14435});14436}14437});1443814439/*14440* File: ForceDirected.js14441*/1444214443/*14444Class: ForceDirected1444514446A visualization that lays graphs using a Force-Directed layout algorithm.1444714448Inspired by:1444914450Force-Directed Drawing Algorithms (Stephen G. Kobourov) <http://www.cs.brown.edu/~rt/gdhandbook/chapters/force-directed.pdf>1445114452Implements:1445314454All <Loader> methods1445514456Constructor Options:1445714458Inherits options from1445914460- <Options.Canvas>14461- <Options.Controller>14462- <Options.Node>14463- <Options.Edge>14464- <Options.Label>14465- <Options.Events>14466- <Options.Tips>14467- <Options.NodeStyles>14468- <Options.Navigation>1446914470Additionally, there are two parameters1447114472levelDistance - (number) Default's *50*. The natural length desired for the edges.14473iterations - (number) Default's *50*. The number of iterations for the spring layout simulation. Depending on the browser's speed you could set this to a more 'interesting' number, like *200*.1447414475Instance Properties:1447614477canvas - Access a <Canvas> instance.14478graph - Access a <Graph> instance.14479op - Access a <ForceDirected.Op> instance.14480fx - Access a <ForceDirected.Plot> instance.14481labels - Access a <ForceDirected.Label> interface implementation.1448214483*/1448414485$jit.ForceDirected = new Class( {1448614487Implements: [ Loader, Extras, Layouts.ForceDirected ],1448814489initialize: function(controller) {14490var $ForceDirected = $jit.ForceDirected;1449114492var config = {14493iterations: 50,14494levelDistance: 5014495};1449614497this.controller = this.config = $.merge(Options("Canvas", "Node", "Edge",14498"Fx", "Tips", "NodeStyles", "Events", "Navigation", "Controller", "Label"), config, controller);1449914500var canvasConfig = this.config;14501if(canvasConfig.useCanvas) {14502this.canvas = canvasConfig.useCanvas;14503this.config.labelContainer = this.canvas.id + '-label';14504} else {14505if(canvasConfig.background) {14506canvasConfig.background = $.merge({14507type: 'Circles'14508}, canvasConfig.background);14509}14510this.canvas = new Canvas(this, canvasConfig);14511this.config.labelContainer = (typeof canvasConfig.injectInto == 'string'? canvasConfig.injectInto : canvasConfig.injectInto.id) + '-label';14512}1451314514this.graphOptions = {14515'klass': Complex,14516'Node': {14517'selected': false,14518'exist': true,14519'drawn': true14520}14521};14522this.graph = new Graph(this.graphOptions, this.config.Node,14523this.config.Edge);14524this.labels = new $ForceDirected.Label[canvasConfig.Label.type](this);14525this.fx = new $ForceDirected.Plot(this, $ForceDirected);14526this.op = new $ForceDirected.Op(this);14527this.json = null;14528this.busy = false;14529// initialize extras14530this.initializeExtras();14531},1453214533/*14534Method: refresh1453514536Computes positions and plots the tree.14537*/14538refresh: function() {14539this.compute();14540this.plot();14541},1454214543reposition: function() {14544this.compute('end');14545},1454614547/*14548Method: computeIncremental1454914550Performs the Force Directed algorithm incrementally.1455114552Description:1455314554ForceDirected algorithms can perform many computations and lead to JavaScript taking too much time to complete.14555This method splits the algorithm into smaller parts allowing the user to track the evolution of the algorithm and14556avoiding browser messages such as "This script is taking too long to complete".1455714558Parameters:1455914560opt - (object) The object properties are described below1456114562iter - (number) Default's *20*. Split the algorithm into pieces of _iter_ iterations. For example, if the _iterations_ configuration property14563of your <ForceDirected> class is 100, then you could set _iter_ to 20 to split the main algorithm into 5 smaller pieces.1456414565property - (string) Default's *end*. Whether to update starting, current or ending node positions. Possible values are 'end', 'start', 'current'.14566You can also set an array of these properties. If you'd like to keep the current node positions but to perform these14567computations for final animation positions then you can just choose 'end'.1456814569onStep - (function) A callback function called when each "small part" of the algorithm completed. This function gets as first formal14570parameter a percentage value.1457114572onComplete - A callback function called when the algorithm completed.1457314574Example:1457514576In this example I calculate the end positions and then animate the graph to those positions1457714578(start code js)14579var fd = new $jit.ForceDirected(...);14580fd.computeIncremental({14581iter: 20,14582property: 'end',14583onStep: function(perc) {14584Log.write("loading " + perc + "%");14585},14586onComplete: function() {14587Log.write("done");14588fd.animate();14589}14590});14591(end code)1459214593In this example I calculate all positions and (re)plot the graph1459414595(start code js)14596var fd = new ForceDirected(...);14597fd.computeIncremental({14598iter: 20,14599property: ['end', 'start', 'current'],14600onStep: function(perc) {14601Log.write("loading " + perc + "%");14602},14603onComplete: function() {14604Log.write("done");14605fd.plot();14606}14607});14608(end code)1460914610*/14611computeIncremental: function(opt) {14612opt = $.merge( {14613iter: 20,14614property: 'end',14615onStep: $.empty,14616onComplete: $.empty14617}, opt || {});1461814619this.config.onBeforeCompute(this.graph.getNode(this.root));14620this.compute(opt.property, opt);14621},1462214623/*14624Method: plot1462514626Plots the ForceDirected graph. This is a shortcut to *fx.plot*.14627*/14628plot: function() {14629this.fx.plot();14630},1463114632/*14633Method: animate1463414635Animates the graph from the current positions to the 'end' node positions.14636*/14637animate: function(opt) {14638this.fx.animate($.merge( {14639modes: [ 'linear' ]14640}, opt || {}));14641}14642});1464314644$jit.ForceDirected.$extend = true;1464514646(function(ForceDirected) {1464714648/*14649Class: ForceDirected.Op1465014651Custom extension of <Graph.Op>.1465214653Extends:1465414655All <Graph.Op> methods1465614657See also:1465814659<Graph.Op>1466014661*/14662ForceDirected.Op = new Class( {1466314664Implements: Graph.Op1466514666});1466714668/*14669Class: ForceDirected.Plot1467014671Custom extension of <Graph.Plot>.1467214673Extends:1467414675All <Graph.Plot> methods1467614677See also:1467814679<Graph.Plot>1468014681*/14682ForceDirected.Plot = new Class( {1468314684Implements: Graph.Plot1468514686});1468714688/*14689Class: ForceDirected.Label1469014691Custom extension of <Graph.Label>.14692Contains custom <Graph.Label.SVG>, <Graph.Label.HTML> and <Graph.Label.Native> extensions.1469314694Extends:1469514696All <Graph.Label> methods and subclasses.1469714698See also:1469914700<Graph.Label>, <Graph.Label.Native>, <Graph.Label.HTML>, <Graph.Label.SVG>.1470114702*/14703ForceDirected.Label = {};1470414705/*14706ForceDirected.Label.Native1470714708Custom extension of <Graph.Label.Native>.1470914710Extends:1471114712All <Graph.Label.Native> methods1471314714See also:1471514716<Graph.Label.Native>1471714718*/14719ForceDirected.Label.Native = new Class( {14720Implements: Graph.Label.Native14721});1472214723/*14724ForceDirected.Label.SVG1472514726Custom extension of <Graph.Label.SVG>.1472714728Extends:1472914730All <Graph.Label.SVG> methods1473114732See also:1473314734<Graph.Label.SVG>1473514736*/14737ForceDirected.Label.SVG = new Class( {14738Implements: Graph.Label.SVG,1473914740initialize: function(viz) {14741this.viz = viz;14742},1474314744/*14745placeLabel1474614747Overrides abstract method placeLabel in <Graph.Label>.1474814749Parameters:1475014751tag - A DOM label element.14752node - A <Graph.Node>.14753controller - A configuration/controller object passed to the visualization.1475414755*/14756placeLabel: function(tag, node, controller) {14757var pos = node.pos.getc(true),14758canvas = this.viz.canvas,14759ox = canvas.translateOffsetX,14760oy = canvas.translateOffsetY,14761sx = canvas.scaleOffsetX,14762sy = canvas.scaleOffsetY,14763radius = canvas.getSize();14764var labelPos = {14765x: Math.round(pos.x * sx + ox + radius.width / 2),14766y: Math.round(pos.y * sy + oy + radius.height / 2)14767};14768tag.setAttribute('x', labelPos.x);14769tag.setAttribute('y', labelPos.y);1477014771controller.onPlaceLabel(tag, node);14772}14773});1477414775/*14776ForceDirected.Label.HTML1477714778Custom extension of <Graph.Label.HTML>.1477914780Extends:1478114782All <Graph.Label.HTML> methods.1478314784See also:1478514786<Graph.Label.HTML>1478714788*/14789ForceDirected.Label.HTML = new Class( {14790Implements: Graph.Label.HTML,1479114792initialize: function(viz) {14793this.viz = viz;14794},14795/*14796placeLabel1479714798Overrides abstract method placeLabel in <Graph.Plot>.1479914800Parameters:1480114802tag - A DOM label element.14803node - A <Graph.Node>.14804controller - A configuration/controller object passed to the visualization.1480514806*/14807placeLabel: function(tag, node, controller) {14808var pos = node.pos.getc(true),14809canvas = this.viz.canvas,14810ox = canvas.translateOffsetX,14811oy = canvas.translateOffsetY,14812sx = canvas.scaleOffsetX,14813sy = canvas.scaleOffsetY,14814radius = canvas.getSize();14815var labelPos = {14816x: Math.round(pos.x * sx + ox + radius.width / 2),14817y: Math.round(pos.y * sy + oy + radius.height / 2)14818};14819var style = tag.style;14820style.left = labelPos.x + 'px';14821style.top = labelPos.y + 'px';14822style.display = this.fitsInCanvas(labelPos, canvas) ? '' : 'none';1482314824controller.onPlaceLabel(tag, node);14825}14826});1482714828/*14829Class: ForceDirected.Plot.NodeTypes1483014831This class contains a list of <Graph.Node> built-in types.14832Node types implemented are 'none', 'circle', 'triangle', 'rectangle', 'star', 'ellipse' and 'square'.1483314834You can add your custom node types, customizing your visualization to the extreme.1483514836Example:1483714838(start code js)14839ForceDirected.Plot.NodeTypes.implement({14840'mySpecialType': {14841'render': function(node, canvas) {14842//print your custom node to canvas14843},14844//optional14845'contains': function(node, pos) {14846//return true if pos is inside the node or false otherwise14847}14848}14849});14850(end code)1485114852*/14853ForceDirected.Plot.NodeTypes = new Class({14854'none': {14855'render': $.empty,14856'contains': $.lambda(false)14857},14858'circle': {14859'render': function(node, canvas){14860var pos = node.pos.getc(true),14861dim = node.getData('dim');14862this.nodeHelper.circle.render('fill', pos, dim, canvas);14863},14864'contains': function(node, pos){14865var npos = node.pos.getc(true),14866dim = node.getData('dim');14867return this.nodeHelper.circle.contains(npos, pos, dim);14868}14869},14870'ellipse': {14871'render': function(node, canvas){14872var pos = node.pos.getc(true),14873width = node.getData('width'),14874height = node.getData('height');14875this.nodeHelper.ellipse.render('fill', pos, width, height, canvas);14876},14877'contains': function(node, pos){14878var npos = node.pos.getc(true),14879width = node.getData('width'),14880height = node.getData('height');14881return this.nodeHelper.ellipse.contains(npos, pos, width, height);14882}14883},14884'square': {14885'render': function(node, canvas){14886var pos = node.pos.getc(true),14887dim = node.getData('dim');14888this.nodeHelper.square.render('fill', pos, dim, canvas);14889},14890'contains': function(node, pos){14891var npos = node.pos.getc(true),14892dim = node.getData('dim');14893return this.nodeHelper.square.contains(npos, pos, dim);14894}14895},14896'rectangle': {14897'render': function(node, canvas){14898var pos = node.pos.getc(true),14899width = node.getData('width'),14900height = node.getData('height');14901this.nodeHelper.rectangle.render('fill', pos, width, height, canvas);14902},14903'contains': function(node, pos){14904var npos = node.pos.getc(true),14905width = node.getData('width'),14906height = node.getData('height');14907return this.nodeHelper.rectangle.contains(npos, pos, width, height);14908}14909},14910'triangle': {14911'render': function(node, canvas){14912var pos = node.pos.getc(true),14913dim = node.getData('dim');14914this.nodeHelper.triangle.render('fill', pos, dim, canvas);14915},14916'contains': function(node, pos) {14917var npos = node.pos.getc(true),14918dim = node.getData('dim');14919return this.nodeHelper.triangle.contains(npos, pos, dim);14920}14921},14922'star': {14923'render': function(node, canvas){14924var pos = node.pos.getc(true),14925dim = node.getData('dim');14926this.nodeHelper.star.render('fill', pos, dim, canvas);14927},14928'contains': function(node, pos) {14929var npos = node.pos.getc(true),14930dim = node.getData('dim');14931return this.nodeHelper.star.contains(npos, pos, dim);14932}14933}14934});1493514936/*14937Class: ForceDirected.Plot.EdgeTypes1493814939This class contains a list of <Graph.Adjacence> built-in types.14940Edge types implemented are 'none', 'line' and 'arrow'.1494114942You can add your custom edge types, customizing your visualization to the extreme.1494314944Example:1494514946(start code js)14947ForceDirected.Plot.EdgeTypes.implement({14948'mySpecialType': {14949'render': function(adj, canvas) {14950//print your custom edge to canvas14951},14952//optional14953'contains': function(adj, pos) {14954//return true if pos is inside the arc or false otherwise14955}14956}14957});14958(end code)1495914960*/14961ForceDirected.Plot.EdgeTypes = new Class({14962'none': $.empty,14963'line': {14964'render': function(adj, canvas) {14965var from = adj.nodeFrom.pos.getc(true),14966to = adj.nodeTo.pos.getc(true);14967this.edgeHelper.line.render(from, to, canvas);14968},14969'contains': function(adj, pos) {14970var from = adj.nodeFrom.pos.getc(true),14971to = adj.nodeTo.pos.getc(true);14972return this.edgeHelper.line.contains(from, to, pos, this.edge.epsilon);14973}14974},14975'arrow': {14976'render': function(adj, canvas) {14977var from = adj.nodeFrom.pos.getc(true),14978to = adj.nodeTo.pos.getc(true),14979dim = adj.getData('dim'),14980direction = adj.data.$direction,14981inv = (direction && direction.length>1 && direction[0] != adj.nodeFrom.id);14982this.edgeHelper.arrow.render(from, to, dim, inv, canvas);14983},14984'contains': function(adj, pos) {14985var from = adj.nodeFrom.pos.getc(true),14986to = adj.nodeTo.pos.getc(true);14987return this.edgeHelper.arrow.contains(from, to, pos, this.edge.epsilon);14988}14989}14990});1499114992})($jit.ForceDirected);149931499414995/*14996* File: Treemap.js14997*14998*/1499915000$jit.TM = {};1500115002var TM = $jit.TM;1500315004$jit.TM.$extend = true;1500515006/*15007Class: TM.Base1500815009Abstract class providing base functionality for <TM.Squarified>, <TM.Strip> and <TM.SliceAndDice> visualizations.1501015011Implements:1501215013All <Loader> methods1501415015Constructor Options:1501615017Inherits options from1501815019- <Options.Canvas>15020- <Options.Controller>15021- <Options.Node>15022- <Options.Edge>15023- <Options.Label>15024- <Options.Events>15025- <Options.Tips>15026- <Options.NodeStyles>15027- <Options.Navigation>1502815029Additionally, there are other parameters and some default values changed1503015031orientation - (string) Default's *h*. Whether to set horizontal or vertical layouts. Possible values are 'h' and 'v'.15032titleHeight - (number) Default's *13*. The height of the title rectangle for inner (non-leaf) nodes.15033offset - (number) Default's *2*. Boxes offset.15034constrained - (boolean) Default's *false*. Whether to show the entire tree when loaded or just the number of levels specified by _levelsToShow_.15035levelsToShow - (number) Default's *3*. The number of levels to show for a subtree. This number is relative to the selected node.15036animate - (boolean) Default's *false*. Whether to animate transitions.15037Node.type - Described in <Options.Node>. Default's *rectangle*.15038duration - Described in <Options.Fx>. Default's *700*.15039fps - Described in <Options.Fx>. Default's *45*.1504015041Instance Properties:1504215043canvas - Access a <Canvas> instance.15044graph - Access a <Graph> instance.15045op - Access a <TM.Op> instance.15046fx - Access a <TM.Plot> instance.15047labels - Access a <TM.Label> interface implementation.1504815049Inspired by:1505015051Squarified Treemaps (Mark Bruls, Kees Huizing, and Jarke J. van Wijk) <http://www.win.tue.nl/~vanwijk/stm.pdf>1505215053Tree visualization with tree-maps: 2-d space-filling approach (Ben Shneiderman) <http://hcil.cs.umd.edu/trs/91-03/91-03.html>1505415055Note:1505615057This visualization was built and engineered from scratch, taking only the paper as inspiration, and only shares some features with the visualization described in the paper.1505815059*/15060TM.Base = {15061layout: {15062orientation: "h",15063vertical: function(){15064return this.orientation == "v";15065},15066horizontal: function(){15067return this.orientation == "h";15068},15069change: function(){15070this.orientation = this.vertical()? "h" : "v";15071}15072},1507315074initialize: function(controller){15075var config = {15076orientation: "h",15077titleHeight: 13,15078offset: 2,15079levelsToShow: 0,15080constrained: false,15081animate: false,15082Node: {15083type: 'rectangle',15084overridable: true,15085//we all know why this is not zero,15086//right, Firefox?15087width: 3,15088height: 3,15089color: '#444'15090},15091Label: {15092textAlign: 'center',15093textBaseline: 'top'15094},15095Edge: {15096type: 'none'15097},15098duration: 700,15099fps: 4515100};1510115102this.controller = this.config = $.merge(Options("Canvas", "Node", "Edge",15103"Fx", "Controller", "Tips", "NodeStyles", "Events", "Navigation", "Label"), config, controller);15104this.layout.orientation = this.config.orientation;1510515106var canvasConfig = this.config;15107if (canvasConfig.useCanvas) {15108this.canvas = canvasConfig.useCanvas;15109this.config.labelContainer = this.canvas.id + '-label';15110} else {15111if(canvasConfig.background) {15112canvasConfig.background = $.merge({15113type: 'Circles'15114}, canvasConfig.background);15115}15116this.canvas = new Canvas(this, canvasConfig);15117this.config.labelContainer = (typeof canvasConfig.injectInto == 'string'? canvasConfig.injectInto : canvasConfig.injectInto.id) + '-label';15118}1511915120this.graphOptions = {15121'klass': Complex,15122'Node': {15123'selected': false,15124'exist': true,15125'drawn': true15126}15127};15128this.graph = new Graph(this.graphOptions, this.config.Node,15129this.config.Edge);15130this.labels = new TM.Label[canvasConfig.Label.type](this);15131this.fx = new TM.Plot(this);15132this.op = new TM.Op(this);15133this.group = new TM.Group(this);15134this.geom = new TM.Geom(this);15135this.clickedNode = null;15136this.busy = false;15137// initialize extras15138this.initializeExtras();15139},1514015141/*15142Method: refresh1514315144Computes positions and plots the tree.15145*/15146refresh: function(){15147if(this.busy) return;15148this.busy = true;15149var that = this;15150if(this.config.animate) {15151this.compute('end');15152this.config.levelsToShow > 0 && this.geom.setRightLevelToShow(this.graph.getNode(this.clickedNode15153&& this.clickedNode.id || this.root));15154this.fx.animate($.merge(this.config, {15155modes: ['linear', 'node-property:width:height'],15156onComplete: function() {15157that.busy = false;15158}15159}));15160} else {15161var labelType = this.config.Label.type;15162if(labelType != 'Native') {15163var that = this;15164this.graph.eachNode(function(n) { that.labels.hideLabel(n, false); });15165}15166this.busy = false;15167this.compute();15168this.config.levelsToShow > 0 && this.geom.setRightLevelToShow(this.graph.getNode(this.clickedNode15169&& this.clickedNode.id || this.root));15170this.plot();15171}15172},1517315174/*15175Method: plot1517615177Plots the TreeMap. This is a shortcut to *fx.plot*.1517815179*/15180plot: function(){15181this.fx.plot();15182},1518315184/*15185Method: leaf1518615187Returns whether the node is a leaf.1518815189Parameters:1519015191n - (object) A <Graph.Node>.1519215193*/15194leaf: function(n){15195return n.getSubnodes([151961, 115197], "ignore").length == 0;15198},1519915200/*15201Method: enter1520215203Sets the node as root.1520415205Parameters:1520615207n - (object) A <Graph.Node>.1520815209*/15210enter: function(n){15211if(this.busy) return;15212this.busy = true;1521315214var that = this,15215config = this.config,15216graph = this.graph,15217clickedNode = n,15218previousClickedNode = this.clickedNode;1521915220var callback = {15221onComplete: function() {15222//ensure that nodes are shown for that level15223if(config.levelsToShow > 0) {15224that.geom.setRightLevelToShow(n);15225}15226//compute positions of newly inserted nodes15227if(config.levelsToShow > 0 || config.request) that.compute();15228if(config.animate) {15229//fade nodes15230graph.nodeList.setData('alpha', 0, 'end');15231n.eachSubgraph(function(n) {15232n.setData('alpha', 1, 'end');15233}, "ignore");15234that.fx.animate({15235duration: 500,15236modes:['node-property:alpha'],15237onComplete: function() {15238//compute end positions15239that.clickedNode = clickedNode;15240that.compute('end');15241//animate positions15242//TODO(nico) commenting this line didn't seem to throw errors...15243that.clickedNode = previousClickedNode;15244that.fx.animate({15245modes:['linear', 'node-property:width:height'],15246duration: 1000,15247onComplete: function() {15248that.busy = false;15249//TODO(nico) check comment above15250that.clickedNode = clickedNode;15251}15252});15253}15254});15255} else {15256that.busy = false;15257that.clickedNode = n;15258that.refresh();15259}15260}15261};15262if(config.request) {15263this.requestNodes(clickedNode, callback);15264} else {15265callback.onComplete();15266}15267},1526815269/*15270Method: out1527115272Sets the parent node of the current selected node as root.1527315274*/15275out: function(){15276if(this.busy) return;15277this.busy = true;15278this.events.hoveredNode = false;15279var that = this,15280config = this.config,15281graph = this.graph,15282parents = graph.getNode(this.clickedNode15283&& this.clickedNode.id || this.root).getParents(),15284parent = parents[0],15285clickedNode = parent,15286previousClickedNode = this.clickedNode;1528715288//if no parents return15289if(!parent) {15290this.busy = false;15291return;15292}15293//final plot callback15294callback = {15295onComplete: function() {15296that.clickedNode = parent;15297if(config.request) {15298that.requestNodes(parent, {15299onComplete: function() {15300that.compute();15301that.plot();15302that.busy = false;15303}15304});15305} else {15306that.compute();15307that.plot();15308that.busy = false;15309}15310}15311};15312//prune tree15313if (config.levelsToShow > 0)15314this.geom.setRightLevelToShow(parent);15315//animate node positions15316if(config.animate) {15317this.clickedNode = clickedNode;15318this.compute('end');15319//animate the visible subtree only15320this.clickedNode = previousClickedNode;15321this.fx.animate({15322modes:['linear', 'node-property:width:height'],15323duration: 1000,15324onComplete: function() {15325//animate the parent subtree15326that.clickedNode = clickedNode;15327//change nodes alpha15328graph.eachNode(function(n) {15329n.setDataset(['current', 'end'], {15330'alpha': [0, 1]15331});15332}, "ignore");15333previousClickedNode.eachSubgraph(function(node) {15334node.setData('alpha', 1);15335}, "ignore");15336that.fx.animate({15337duration: 500,15338modes:['node-property:alpha'],15339onComplete: function() {15340callback.onComplete();15341}15342});15343}15344});15345} else {15346callback.onComplete();15347}15348},1534915350requestNodes: function(node, onComplete){15351var handler = $.merge(this.controller, onComplete),15352lev = this.config.levelsToShow;15353if (handler.request) {15354var leaves = [], d = node._depth;15355node.eachLevel(0, lev, function(n){15356var nodeLevel = lev - (n._depth - d);15357if (n.drawn && !n.anySubnode() && nodeLevel > 0) {15358leaves.push(n);15359n._level = nodeLevel;15360}15361});15362this.group.requestNodes(leaves, handler);15363} else {15364handler.onComplete();15365}15366},1536715368reposition: function() {15369this.compute('end');15370}15371};1537215373/*15374Class: TM.Op1537515376Custom extension of <Graph.Op>.1537715378Extends:1537915380All <Graph.Op> methods1538115382See also:1538315384<Graph.Op>1538515386*/15387TM.Op = new Class({15388Implements: Graph.Op,1538915390initialize: function(viz){15391this.viz = viz;15392}15393});1539415395//extend level methods of Graph.Geom15396TM.Geom = new Class({15397Implements: Graph.Geom,1539815399getRightLevelToShow: function() {15400return this.viz.config.levelsToShow;15401},1540215403setRightLevelToShow: function(node) {15404var level = this.getRightLevelToShow(),15405fx = this.viz.labels;15406node.eachLevel(0, level+1, function(n) {15407var d = n._depth - node._depth;15408if(d > level) {15409n.drawn = false;15410n.exist = false;15411n.ignore = true;15412fx.hideLabel(n, false);15413} else {15414n.drawn = true;15415n.exist = true;15416delete n.ignore;15417}15418});15419node.drawn = true;15420delete node.ignore;15421}15422});1542315424/*1542515426Performs operations on group of nodes.1542715428*/15429TM.Group = new Class( {1543015431initialize: function(viz){15432this.viz = viz;15433this.canvas = viz.canvas;15434this.config = viz.config;15435},1543615437/*1543815439Calls the request method on the controller to request a subtree for each node.15440*/15441requestNodes: function(nodes, controller){15442var counter = 0, len = nodes.length, nodeSelected = {};15443var complete = function(){15444controller.onComplete();15445};15446var viz = this.viz;15447if (len == 0)15448complete();15449for ( var i = 0; i < len; i++) {15450nodeSelected[nodes[i].id] = nodes[i];15451controller.request(nodes[i].id, nodes[i]._level, {15452onComplete: function(nodeId, data){15453if (data && data.children) {15454data.id = nodeId;15455viz.op.sum(data, {15456type: 'nothing'15457});15458}15459if (++counter == len) {15460viz.graph.computeLevels(viz.root, 0);15461complete();15462}15463}15464});15465}15466}15467});1546815469/*15470Class: TM.Plot1547115472Custom extension of <Graph.Plot>.1547315474Extends:1547515476All <Graph.Plot> methods1547715478See also:1547915480<Graph.Plot>1548115482*/15483TM.Plot = new Class({1548415485Implements: Graph.Plot,1548615487initialize: function(viz){15488this.viz = viz;15489this.config = viz.config;15490this.node = this.config.Node;15491this.edge = this.config.Edge;15492this.animation = new Animation;15493this.nodeTypes = new TM.Plot.NodeTypes;15494this.edgeTypes = new TM.Plot.EdgeTypes;15495this.labels = viz.labels;15496},1549715498plot: function(opt, animating){15499var viz = this.viz,15500graph = viz.graph;15501viz.canvas.clear();15502this.plotTree(graph.getNode(viz.clickedNode && viz.clickedNode.id || viz.root), $.merge(viz.config, opt || {}, {15503'withLabels': true,15504'hideLabels': false,15505'plotSubtree': function(n, ch){15506return n.anySubnode("exist");15507}15508}), animating);15509}15510});1551115512/*15513Class: TM.Label1551415515Custom extension of <Graph.Label>.15516Contains custom <Graph.Label.SVG>, <Graph.Label.HTML> and <Graph.Label.Native> extensions.1551715518Extends:1551915520All <Graph.Label> methods and subclasses.1552115522See also:1552315524<Graph.Label>, <Graph.Label.Native>, <Graph.Label.HTML>, <Graph.Label.SVG>.1552515526*/15527TM.Label = {};1552815529/*15530TM.Label.Native1553115532Custom extension of <Graph.Label.Native>.1553315534Extends:1553515536All <Graph.Label.Native> methods1553715538See also:1553915540<Graph.Label.Native>15541*/15542TM.Label.Native = new Class({15543Implements: Graph.Label.Native,1554415545initialize: function(viz) {15546this.config = viz.config;15547this.leaf = viz.leaf;15548},1554915550renderLabel: function(canvas, node, controller){15551if(!this.leaf(node) && !this.config.titleHeight) return;15552var pos = node.pos.getc(true),15553ctx = canvas.getCtx(),15554width = node.getData('width'),15555height = node.getData('height'),15556x = pos.x + width/2,15557y = pos.y;1555815559ctx.fillText(node.name, x, y, width);15560}15561});1556215563/*15564TM.Label.SVG1556515566Custom extension of <Graph.Label.SVG>.1556715568Extends:1556915570All <Graph.Label.SVG> methods1557115572See also:1557315574<Graph.Label.SVG>15575*/15576TM.Label.SVG = new Class( {15577Implements: Graph.Label.SVG,1557815579initialize: function(viz){15580this.viz = viz;15581this.leaf = viz.leaf;15582this.config = viz.config;15583},1558415585/*15586placeLabel1558715588Overrides abstract method placeLabel in <Graph.Plot>.1558915590Parameters:1559115592tag - A DOM label element.15593node - A <Graph.Node>.15594controller - A configuration/controller object passed to the visualization.1559515596*/15597placeLabel: function(tag, node, controller){15598var pos = node.pos.getc(true),15599canvas = this.viz.canvas,15600ox = canvas.translateOffsetX,15601oy = canvas.translateOffsetY,15602sx = canvas.scaleOffsetX,15603sy = canvas.scaleOffsetY,15604radius = canvas.getSize();15605var labelPos = {15606x: Math.round(pos.x * sx + ox + radius.width / 2),15607y: Math.round(pos.y * sy + oy + radius.height / 2)15608};15609tag.setAttribute('x', labelPos.x);15610tag.setAttribute('y', labelPos.y);1561115612if(!this.leaf(node) && !this.config.titleHeight) {15613tag.style.display = 'none';15614}15615controller.onPlaceLabel(tag, node);15616}15617});1561815619/*15620TM.Label.HTML1562115622Custom extension of <Graph.Label.HTML>.1562315624Extends:1562515626All <Graph.Label.HTML> methods.1562715628See also:1562915630<Graph.Label.HTML>1563115632*/15633TM.Label.HTML = new Class( {15634Implements: Graph.Label.HTML,1563515636initialize: function(viz){15637this.viz = viz;15638this.leaf = viz.leaf;15639this.config = viz.config;15640},1564115642/*15643placeLabel1564415645Overrides abstract method placeLabel in <Graph.Plot>.1564615647Parameters:1564815649tag - A DOM label element.15650node - A <Graph.Node>.15651controller - A configuration/controller object passed to the visualization.1565215653*/15654placeLabel: function(tag, node, controller){15655var pos = node.pos.getc(true),15656canvas = this.viz.canvas,15657ox = canvas.translateOffsetX,15658oy = canvas.translateOffsetY,15659sx = canvas.scaleOffsetX,15660sy = canvas.scaleOffsetY,15661radius = canvas.getSize();15662var labelPos = {15663x: Math.round(pos.x * sx + ox + radius.width / 2),15664y: Math.round(pos.y * sy + oy + radius.height / 2)15665};1566615667var style = tag.style;15668style.left = labelPos.x + 'px';15669style.top = labelPos.y + 'px';15670style.width = node.getData('width') * sx + 'px';15671style.height = node.getData('height') * sy + 'px';15672style.zIndex = node._depth * 100;15673style.display = '';1567415675if(!this.leaf(node) && !this.config.titleHeight) {15676tag.style.display = 'none';15677}15678controller.onPlaceLabel(tag, node);15679}15680});1568115682/*15683Class: TM.Plot.NodeTypes1568415685This class contains a list of <Graph.Node> built-in types.15686Node types implemented are 'none', 'rectangle'.1568715688You can add your custom node types, customizing your visualization to the extreme.1568915690Example:1569115692(start code js)15693TM.Plot.NodeTypes.implement({15694'mySpecialType': {15695'render': function(node, canvas) {15696//print your custom node to canvas15697},15698//optional15699'contains': function(node, pos) {15700//return true if pos is inside the node or false otherwise15701}15702}15703});15704(end code)1570515706*/15707TM.Plot.NodeTypes = new Class( {15708'none': {15709'render': $.empty15710},1571115712'rectangle': {15713'render': function(node, canvas, animating){15714var leaf = this.viz.leaf(node),15715config = this.config,15716offst = config.offset,15717titleHeight = config.titleHeight,15718pos = node.pos.getc(true),15719width = node.getData('width'),15720height = node.getData('height'),15721border = node.getData('border'),15722ctx = canvas.getCtx(),15723posx = pos.x + offst / 2,15724posy = pos.y + offst / 2;15725if(width <= offst || height <= offst) return;15726if (leaf) {15727if(config.cushion) {15728var lg = ctx.createRadialGradient(posx + (width-offst)/2, posy + (height-offst)/2, 1,15729posx + (width-offst)/2, posy + (height-offst)/2, width < height? height : width);15730var color = node.getData('color');15731var colorGrad = $.rgbToHex($.map($.hexToRgb(color),15732function(r) { return r * 0.2 >> 0; }));15733lg.addColorStop(0, color);15734lg.addColorStop(1, colorGrad);15735ctx.fillStyle = lg;15736}15737ctx.fillRect(posx, posy, width - offst, height - offst);15738if(border) {15739ctx.save();15740ctx.strokeStyle = border;15741ctx.strokeRect(posx, posy, width - offst, height - offst);15742ctx.restore();15743}15744} else if(titleHeight > 0){15745ctx.fillRect(pos.x + offst / 2, pos.y + offst / 2, width - offst,15746titleHeight - offst);15747if(border) {15748ctx.save();15749ctx.strokeStyle = border;15750ctx.strokeRect(pos.x + offst / 2, pos.y + offst / 2, width - offst,15751height - offst);15752ctx.restore();15753}15754}15755},15756'contains': function(node, pos) {15757if(this.viz.clickedNode && !node.isDescendantOf(this.viz.clickedNode.id) || node.ignore) return false;15758var npos = node.pos.getc(true),15759width = node.getData('width'),15760leaf = this.viz.leaf(node),15761height = leaf? node.getData('height') : this.config.titleHeight;15762return this.nodeHelper.rectangle.contains({x: npos.x + width/2, y: npos.y + height/2}, pos, width, height);15763}15764}15765});1576615767TM.Plot.EdgeTypes = new Class( {15768'none': $.empty15769});1577015771/*15772Class: TM.SliceAndDice1577315774A slice and dice TreeMap visualization.1577515776Implements:1577715778All <TM.Base> methods and properties.15779*/15780TM.SliceAndDice = new Class( {15781Implements: [15782Loader, Extras, TM.Base, Layouts.TM.SliceAndDice15783]15784});1578515786/*15787Class: TM.Squarified1578815789A squarified TreeMap visualization.1579015791Implements:1579215793All <TM.Base> methods and properties.15794*/15795TM.Squarified = new Class( {15796Implements: [15797Loader, Extras, TM.Base, Layouts.TM.Squarified15798]15799});1580015801/*15802Class: TM.Strip1580315804A strip TreeMap visualization.1580515806Implements:1580715808All <TM.Base> methods and properties.15809*/15810TM.Strip = new Class( {15811Implements: [15812Loader, Extras, TM.Base, Layouts.TM.Strip15813]15814});158151581615817/*15818* File: RGraph.js15819*15820*/1582115822/*15823Class: RGraph1582415825A radial graph visualization with advanced animations.1582615827Inspired by:1582815829Animated Exploration of Dynamic Graphs with Radial Layout (Ka-Ping Yee, Danyel Fisher, Rachna Dhamija, Marti Hearst) <http://bailando.sims.berkeley.edu/papers/infovis01.htm>1583015831Note:1583215833This visualization was built and engineered from scratch, taking only the paper as inspiration, and only shares some features with the visualization described in the paper.1583415835Implements:1583615837All <Loader> methods1583815839Constructor Options:1584015841Inherits options from1584215843- <Options.Canvas>15844- <Options.Controller>15845- <Options.Node>15846- <Options.Edge>15847- <Options.Label>15848- <Options.Events>15849- <Options.Tips>15850- <Options.NodeStyles>15851- <Options.Navigation>1585215853Additionally, there are other parameters and some default values changed1585415855interpolation - (string) Default's *linear*. Describes the way nodes are interpolated. Possible values are 'linear' and 'polar'.15856levelDistance - (number) Default's *100*. The distance between levels of the tree.1585715858Instance Properties:1585915860canvas - Access a <Canvas> instance.15861graph - Access a <Graph> instance.15862op - Access a <RGraph.Op> instance.15863fx - Access a <RGraph.Plot> instance.15864labels - Access a <RGraph.Label> interface implementation.15865*/1586615867$jit.RGraph = new Class( {1586815869Implements: [15870Loader, Extras, Layouts.Radial15871],1587215873initialize: function(controller){15874var $RGraph = $jit.RGraph;1587515876var config = {15877interpolation: 'linear',15878levelDistance: 10015879};1588015881this.controller = this.config = $.merge(Options("Canvas", "Node", "Edge",15882"Fx", "Controller", "Tips", "NodeStyles", "Events", "Navigation", "Label"), config, controller);1588315884var canvasConfig = this.config;15885if(canvasConfig.useCanvas) {15886this.canvas = canvasConfig.useCanvas;15887this.config.labelContainer = this.canvas.id + '-label';15888} else {15889if(canvasConfig.background) {15890canvasConfig.background = $.merge({15891type: 'Circles'15892}, canvasConfig.background);15893}15894this.canvas = new Canvas(this, canvasConfig);15895this.config.labelContainer = (typeof canvasConfig.injectInto == 'string'? canvasConfig.injectInto : canvasConfig.injectInto.id) + '-label';15896}1589715898this.graphOptions = {15899'klass': Polar,15900'Node': {15901'selected': false,15902'exist': true,15903'drawn': true15904}15905};15906this.graph = new Graph(this.graphOptions, this.config.Node,15907this.config.Edge);15908this.labels = new $RGraph.Label[canvasConfig.Label.type](this);15909this.fx = new $RGraph.Plot(this, $RGraph);15910this.op = new $RGraph.Op(this);15911this.json = null;15912this.root = null;15913this.busy = false;15914this.parent = false;15915// initialize extras15916this.initializeExtras();15917},1591815919/*1592015921createLevelDistanceFunc1592215923Returns the levelDistance function used for calculating a node distance15924to its origin. This function returns a function that is computed15925per level and not per node, such that all nodes with the same depth will have the15926same distance to the origin. The resulting function gets the15927parent node as parameter and returns a float.1592815929*/15930createLevelDistanceFunc: function(){15931var ld = this.config.levelDistance;15932return function(elem){15933return (elem._depth + 1) * ld;15934};15935},1593615937/*15938Method: refresh1593915940Computes positions and plots the tree.1594115942*/15943refresh: function(){15944this.compute();15945this.plot();15946},1594715948reposition: function(){15949this.compute('end');15950},1595115952/*15953Method: plot1595415955Plots the RGraph. This is a shortcut to *fx.plot*.15956*/15957plot: function(){15958this.fx.plot();15959},15960/*15961getNodeAndParentAngle1596215963Returns the _parent_ of the given node, also calculating its angle span.15964*/15965getNodeAndParentAngle: function(id){15966var theta = false;15967var n = this.graph.getNode(id);15968var ps = n.getParents();15969var p = (ps.length > 0)? ps[0] : false;15970if (p) {15971var posParent = p.pos.getc(), posChild = n.pos.getc();15972var newPos = posParent.add(posChild.scale(-1));15973theta = Math.atan2(newPos.y, newPos.x);15974if (theta < 0)15975theta += 2 * Math.PI;15976}15977return {15978parent: p,15979theta: theta15980};15981},15982/*15983tagChildren1598415985Enumerates the children in order to maintain child ordering (second constraint of the paper).15986*/15987tagChildren: function(par, id){15988if (par.angleSpan) {15989var adjs = [];15990par.eachAdjacency(function(elem){15991adjs.push(elem.nodeTo);15992}, "ignore");15993var len = adjs.length;15994for ( var i = 0; i < len && id != adjs[i].id; i++)15995;15996for ( var j = (i + 1) % len, k = 0; id != adjs[j].id; j = (j + 1) % len) {15997adjs[j].dist = k++;15998}15999}16000},16001/*16002Method: onClick1600316004Animates the <RGraph> to center the node specified by *id*.1600516006Parameters:1600716008id - A <Graph.Node> id.16009opt - (optional|object) An object containing some extra properties described below16010hideLabels - (boolean) Default's *true*. Hide labels when performing the animation.1601116012Example:1601316014(start code js)16015rgraph.onClick('someid');16016//or also...16017rgraph.onClick('someid', {16018hideLabels: false16019});16020(end code)1602116022*/16023onClick: function(id, opt){16024if (this.root != id && !this.busy) {16025this.busy = true;16026this.root = id;16027var that = this;16028this.controller.onBeforeCompute(this.graph.getNode(id));16029var obj = this.getNodeAndParentAngle(id);1603016031// second constraint16032this.tagChildren(obj.parent, id);16033this.parent = obj.parent;16034this.compute('end');1603516036// first constraint16037var thetaDiff = obj.theta - obj.parent.endPos.theta;16038this.graph.eachNode(function(elem){16039elem.endPos.set(elem.endPos.getp().add($P(thetaDiff, 0)));16040});1604116042var mode = this.config.interpolation;16043opt = $.merge( {16044onComplete: $.empty16045}, opt || {});1604616047this.fx.animate($.merge( {16048hideLabels: true,16049modes: [16050mode16051]16052}, opt, {16053onComplete: function(){16054that.busy = false;16055opt.onComplete();16056}16057}));16058}16059}16060});1606116062$jit.RGraph.$extend = true;1606316064(function(RGraph){1606516066/*16067Class: RGraph.Op1606816069Custom extension of <Graph.Op>.1607016071Extends:1607216073All <Graph.Op> methods1607416075See also:1607616077<Graph.Op>1607816079*/16080RGraph.Op = new Class( {1608116082Implements: Graph.Op1608316084});1608516086/*16087Class: RGraph.Plot1608816089Custom extension of <Graph.Plot>.1609016091Extends:1609216093All <Graph.Plot> methods1609416095See also:1609616097<Graph.Plot>1609816099*/16100RGraph.Plot = new Class( {1610116102Implements: Graph.Plot1610316104});1610516106/*16107Object: RGraph.Label1610816109Custom extension of <Graph.Label>.16110Contains custom <Graph.Label.SVG>, <Graph.Label.HTML> and <Graph.Label.Native> extensions.1611116112Extends:1611316114All <Graph.Label> methods and subclasses.1611516116See also:1611716118<Graph.Label>, <Graph.Label.Native>, <Graph.Label.HTML>, <Graph.Label.SVG>.1611916120*/16121RGraph.Label = {};1612216123/*16124RGraph.Label.Native1612516126Custom extension of <Graph.Label.Native>.1612716128Extends:1612916130All <Graph.Label.Native> methods1613116132See also:1613316134<Graph.Label.Native>1613516136*/16137RGraph.Label.Native = new Class( {16138Implements: Graph.Label.Native16139});1614016141/*16142RGraph.Label.SVG1614316144Custom extension of <Graph.Label.SVG>.1614516146Extends:1614716148All <Graph.Label.SVG> methods1614916150See also:1615116152<Graph.Label.SVG>1615316154*/16155RGraph.Label.SVG = new Class( {16156Implements: Graph.Label.SVG,1615716158initialize: function(viz){16159this.viz = viz;16160},1616116162/*16163placeLabel1616416165Overrides abstract method placeLabel in <Graph.Plot>.1616616167Parameters:1616816169tag - A DOM label element.16170node - A <Graph.Node>.16171controller - A configuration/controller object passed to the visualization.1617216173*/16174placeLabel: function(tag, node, controller){16175var pos = node.pos.getc(true),16176canvas = this.viz.canvas,16177ox = canvas.translateOffsetX,16178oy = canvas.translateOffsetY,16179sx = canvas.scaleOffsetX,16180sy = canvas.scaleOffsetY,16181radius = canvas.getSize();16182var labelPos = {16183x: Math.round(pos.x * sx + ox + radius.width / 2),16184y: Math.round(pos.y * sy + oy + radius.height / 2)16185};16186tag.setAttribute('x', labelPos.x);16187tag.setAttribute('y', labelPos.y);1618816189controller.onPlaceLabel(tag, node);16190}16191});1619216193/*16194RGraph.Label.HTML1619516196Custom extension of <Graph.Label.HTML>.1619716198Extends:1619916200All <Graph.Label.HTML> methods.1620116202See also:1620316204<Graph.Label.HTML>1620516206*/16207RGraph.Label.HTML = new Class( {16208Implements: Graph.Label.HTML,1620916210initialize: function(viz){16211this.viz = viz;16212},16213/*16214placeLabel1621516216Overrides abstract method placeLabel in <Graph.Plot>.1621716218Parameters:1621916220tag - A DOM label element.16221node - A <Graph.Node>.16222controller - A configuration/controller object passed to the visualization.1622316224*/16225placeLabel: function(tag, node, controller){16226var pos = node.pos.getc(true),16227canvas = this.viz.canvas,16228ox = canvas.translateOffsetX,16229oy = canvas.translateOffsetY,16230sx = canvas.scaleOffsetX,16231sy = canvas.scaleOffsetY,16232radius = canvas.getSize();16233var labelPos = {16234x: Math.round(pos.x * sx + ox + radius.width / 2),16235y: Math.round(pos.y * sy + oy + radius.height / 2)16236};1623716238var style = tag.style;16239style.left = labelPos.x + 'px';16240style.top = labelPos.y + 'px';16241style.display = this.fitsInCanvas(labelPos, canvas)? '' : 'none';1624216243controller.onPlaceLabel(tag, node);16244}16245});1624616247/*16248Class: RGraph.Plot.NodeTypes1624916250This class contains a list of <Graph.Node> built-in types.16251Node types implemented are 'none', 'circle', 'triangle', 'rectangle', 'star', 'ellipse' and 'square'.1625216253You can add your custom node types, customizing your visualization to the extreme.1625416255Example:1625616257(start code js)16258RGraph.Plot.NodeTypes.implement({16259'mySpecialType': {16260'render': function(node, canvas) {16261//print your custom node to canvas16262},16263//optional16264'contains': function(node, pos) {16265//return true if pos is inside the node or false otherwise16266}16267}16268});16269(end code)1627016271*/16272RGraph.Plot.NodeTypes = new Class({16273'none': {16274'render': $.empty,16275'contains': $.lambda(false)16276},16277'circle': {16278'render': function(node, canvas){16279var pos = node.pos.getc(true),16280dim = node.getData('dim');16281this.nodeHelper.circle.render('fill', pos, dim, canvas);16282},16283'contains': function(node, pos){16284var npos = node.pos.getc(true),16285dim = node.getData('dim');16286return this.nodeHelper.circle.contains(npos, pos, dim);16287}16288},16289'ellipse': {16290'render': function(node, canvas){16291var pos = node.pos.getc(true),16292width = node.getData('width'),16293height = node.getData('height');16294this.nodeHelper.ellipse.render('fill', pos, width, height, canvas);16295},16296'contains': function(node, pos){16297var npos = node.pos.getc(true),16298width = node.getData('width'),16299height = node.getData('height');16300return this.nodeHelper.ellipse.contains(npos, pos, width, height);16301}16302},16303'square': {16304'render': function(node, canvas){16305var pos = node.pos.getc(true),16306dim = node.getData('dim');16307this.nodeHelper.square.render('fill', pos, dim, canvas);16308},16309'contains': function(node, pos){16310var npos = node.pos.getc(true),16311dim = node.getData('dim');16312return this.nodeHelper.square.contains(npos, pos, dim);16313}16314},16315'rectangle': {16316'render': function(node, canvas){16317var pos = node.pos.getc(true),16318width = node.getData('width'),16319height = node.getData('height');16320this.nodeHelper.rectangle.render('fill', pos, width, height, canvas);16321},16322'contains': function(node, pos){16323var npos = node.pos.getc(true),16324width = node.getData('width'),16325height = node.getData('height');16326return this.nodeHelper.rectangle.contains(npos, pos, width, height);16327}16328},16329'triangle': {16330'render': function(node, canvas){16331var pos = node.pos.getc(true),16332dim = node.getData('dim');16333this.nodeHelper.triangle.render('fill', pos, dim, canvas);16334},16335'contains': function(node, pos) {16336var npos = node.pos.getc(true),16337dim = node.getData('dim');16338return this.nodeHelper.triangle.contains(npos, pos, dim);16339}16340},16341'star': {16342'render': function(node, canvas){16343var pos = node.pos.getc(true),16344dim = node.getData('dim');16345this.nodeHelper.star.render('fill', pos, dim, canvas);16346},16347'contains': function(node, pos) {16348var npos = node.pos.getc(true),16349dim = node.getData('dim');16350return this.nodeHelper.star.contains(npos, pos, dim);16351}16352}16353});1635416355/*16356Class: RGraph.Plot.EdgeTypes1635716358This class contains a list of <Graph.Adjacence> built-in types.16359Edge types implemented are 'none', 'line' and 'arrow'.1636016361You can add your custom edge types, customizing your visualization to the extreme.1636216363Example:1636416365(start code js)16366RGraph.Plot.EdgeTypes.implement({16367'mySpecialType': {16368'render': function(adj, canvas) {16369//print your custom edge to canvas16370},16371//optional16372'contains': function(adj, pos) {16373//return true if pos is inside the arc or false otherwise16374}16375}16376});16377(end code)1637816379*/16380RGraph.Plot.EdgeTypes = new Class({16381'none': $.empty,16382'line': {16383'render': function(adj, canvas) {16384var from = adj.nodeFrom.pos.getc(true),16385to = adj.nodeTo.pos.getc(true);16386this.edgeHelper.line.render(from, to, canvas);16387},16388'contains': function(adj, pos) {16389var from = adj.nodeFrom.pos.getc(true),16390to = adj.nodeTo.pos.getc(true);16391return this.edgeHelper.line.contains(from, to, pos, this.edge.epsilon);16392}16393},16394'arrow': {16395'render': function(adj, canvas) {16396var from = adj.nodeFrom.pos.getc(true),16397to = adj.nodeTo.pos.getc(true),16398dim = adj.getData('dim'),16399direction = adj.data.$direction,16400inv = (direction && direction.length>1 && direction[0] != adj.nodeFrom.id);16401this.edgeHelper.arrow.render(from, to, dim, inv, canvas);16402},16403'contains': function(adj, pos) {16404var from = adj.nodeFrom.pos.getc(true),16405to = adj.nodeTo.pos.getc(true);16406return this.edgeHelper.arrow.contains(from, to, pos, this.edge.epsilon);16407}16408}16409});1641016411})($jit.RGraph);164121641316414/*16415* File: Hypertree.js16416*16417*/1641816419/*16420Complex1642116422A multi-purpose Complex Class with common methods. Extended for the Hypertree.1642316424*/16425/*16426moebiusTransformation1642716428Calculates a moebius transformation for this point / complex.16429For more information go to:16430http://en.wikipedia.org/wiki/Moebius_transformation.1643116432Parameters:1643316434c - An initialized Complex instance representing a translation Vector.16435*/1643616437Complex.prototype.moebiusTransformation = function(c) {16438var num = this.add(c);16439var den = c.$conjugate().$prod(this);16440den.x++;16441return num.$div(den);16442};1644316444/*16445moebiusTransformation1644616447Calculates a moebius transformation for the hyperbolic tree.1644816449<http://en.wikipedia.org/wiki/Moebius_transformation>1645016451Parameters:1645216453graph - A <Graph> instance.16454pos - A <Complex>.16455prop - A property array.16456theta - Rotation angle.16457startPos - _optional_ start position.16458*/16459Graph.Util.moebiusTransformation = function(graph, pos, prop, startPos, flags) {16460this.eachNode(graph, function(elem) {16461for ( var i = 0; i < prop.length; i++) {16462var p = pos[i].scale(-1), property = startPos ? startPos : prop[i];16463elem.getPos(prop[i]).set(elem.getPos(property).getc().moebiusTransformation(p));16464}16465}, flags);16466};1646716468/*16469Class: Hypertree1647016471A Hyperbolic Tree/Graph visualization.1647216473Inspired by:1647416475A Focus+Context Technique Based on Hyperbolic Geometry for Visualizing Large Hierarchies (John Lamping, Ramana Rao, and Peter Pirolli).16476<http://www.cs.tau.ac.il/~asharf/shrek/Projects/HypBrowser/startree-chi95.pdf>1647716478Note:1647916480This visualization was built and engineered from scratch, taking only the paper as inspiration, and only shares some features with the Hypertree described in the paper.1648116482Implements:1648316484All <Loader> methods1648516486Constructor Options:1648716488Inherits options from1648916490- <Options.Canvas>16491- <Options.Controller>16492- <Options.Node>16493- <Options.Edge>16494- <Options.Label>16495- <Options.Events>16496- <Options.Tips>16497- <Options.NodeStyles>16498- <Options.Navigation>1649916500Additionally, there are other parameters and some default values changed1650116502radius - (string|number) Default's *auto*. The radius of the disc to plot the <Hypertree> in. 'auto' will take the smaller value from the width and height canvas dimensions. You can also set this to a custom value, for example *250*.16503offset - (number) Default's *0*. A number in the range [0, 1) that will be substracted to each node position to make a more compact <Hypertree>. This will avoid placing nodes too far from each other when a there's a selected node.16504fps - Described in <Options.Fx>. It's default value has been changed to *35*.16505duration - Described in <Options.Fx>. It's default value has been changed to *1500*.16506Edge.type - Described in <Options.Edge>. It's default value has been changed to *hyperline*.1650716508Instance Properties:1650916510canvas - Access a <Canvas> instance.16511graph - Access a <Graph> instance.16512op - Access a <Hypertree.Op> instance.16513fx - Access a <Hypertree.Plot> instance.16514labels - Access a <Hypertree.Label> interface implementation.1651516516*/1651716518$jit.Hypertree = new Class( {1651916520Implements: [ Loader, Extras, Layouts.Radial ],1652116522initialize: function(controller) {16523var $Hypertree = $jit.Hypertree;1652416525var config = {16526radius: "auto",16527offset: 0,16528Edge: {16529type: 'hyperline'16530},16531duration: 1500,16532fps: 3516533};16534this.controller = this.config = $.merge(Options("Canvas", "Node", "Edge",16535"Fx", "Tips", "NodeStyles", "Events", "Navigation", "Controller", "Label"), config, controller);1653616537var canvasConfig = this.config;16538if(canvasConfig.useCanvas) {16539this.canvas = canvasConfig.useCanvas;16540this.config.labelContainer = this.canvas.id + '-label';16541} else {16542if(canvasConfig.background) {16543canvasConfig.background = $.merge({16544type: 'Circles'16545}, canvasConfig.background);16546}16547this.canvas = new Canvas(this, canvasConfig);16548this.config.labelContainer = (typeof canvasConfig.injectInto == 'string'? canvasConfig.injectInto : canvasConfig.injectInto.id) + '-label';16549}1655016551this.graphOptions = {16552'klass': Polar,16553'Node': {16554'selected': false,16555'exist': true,16556'drawn': true16557}16558};16559this.graph = new Graph(this.graphOptions, this.config.Node,16560this.config.Edge);16561this.labels = new $Hypertree.Label[canvasConfig.Label.type](this);16562this.fx = new $Hypertree.Plot(this, $Hypertree);16563this.op = new $Hypertree.Op(this);16564this.json = null;16565this.root = null;16566this.busy = false;16567// initialize extras16568this.initializeExtras();16569},1657016571/*1657216573createLevelDistanceFunc1657416575Returns the levelDistance function used for calculating a node distance16576to its origin. This function returns a function that is computed16577per level and not per node, such that all nodes with the same depth will have the16578same distance to the origin. The resulting function gets the16579parent node as parameter and returns a float.1658016581*/16582createLevelDistanceFunc: function() {16583// get max viz. length.16584var r = this.getRadius();16585// get max depth.16586var depth = 0, max = Math.max, config = this.config;16587this.graph.eachNode(function(node) {16588depth = max(node._depth, depth);16589}, "ignore");16590depth++;16591// node distance generator16592var genDistFunc = function(a) {16593return function(node) {16594node.scale = r;16595var d = node._depth + 1;16596var acum = 0, pow = Math.pow;16597while (d) {16598acum += pow(a, d--);16599}16600return acum - config.offset;16601};16602};16603// estimate better edge length.16604for ( var i = 0.51; i <= 1; i += 0.01) {16605var valSeries = (1 - Math.pow(i, depth)) / (1 - i);16606if (valSeries >= 2) { return genDistFunc(i - 0.01); }16607}16608return genDistFunc(0.75);16609},1661016611/*16612Method: getRadius1661316614Returns the current radius of the visualization. If *config.radius* is *auto* then it16615calculates the radius by taking the smaller size of the <Canvas> widget.1661616617See also:1661816619<Canvas.getSize>1662016621*/16622getRadius: function() {16623var rad = this.config.radius;16624if (rad !== "auto") { return rad; }16625var s = this.canvas.getSize();16626return Math.min(s.width, s.height) / 2;16627},1662816629/*16630Method: refresh1663116632Computes positions and plots the tree.1663316634Parameters:1663516636reposition - (optional|boolean) Set this to *true* to force all positions (current, start, end) to match.1663716638*/16639refresh: function(reposition) {16640if (reposition) {16641this.reposition();16642this.graph.eachNode(function(node) {16643node.startPos.rho = node.pos.rho = node.endPos.rho;16644node.startPos.theta = node.pos.theta = node.endPos.theta;16645});16646} else {16647this.compute();16648}16649this.plot();16650},1665116652/*16653reposition1665416655Computes nodes' positions and restores the tree to its previous position.1665616657For calculating nodes' positions the root must be placed on its origin. This method does this16658and then attemps to restore the hypertree to its previous position.1665916660*/16661reposition: function() {16662this.compute('end');16663var vector = this.graph.getNode(this.root).pos.getc().scale(-1);16664Graph.Util.moebiusTransformation(this.graph, [ vector ], [ 'end' ],16665'end', "ignore");16666this.graph.eachNode(function(node) {16667if (node.ignore) {16668node.endPos.rho = node.pos.rho;16669node.endPos.theta = node.pos.theta;16670}16671});16672},1667316674/*16675Method: plot1667616677Plots the <Hypertree>. This is a shortcut to *fx.plot*.1667816679*/16680plot: function() {16681this.fx.plot();16682},1668316684/*16685Method: onClick1668616687Animates the <Hypertree> to center the node specified by *id*.1668816689Parameters:1669016691id - A <Graph.Node> id.16692opt - (optional|object) An object containing some extra properties described below16693hideLabels - (boolean) Default's *true*. Hide labels when performing the animation.1669416695Example:1669616697(start code js)16698ht.onClick('someid');16699//or also...16700ht.onClick('someid', {16701hideLabels: false16702});16703(end code)1670416705*/16706onClick: function(id, opt) {16707var pos = this.graph.getNode(id).pos.getc(true);16708this.move(pos, opt);16709},1671016711/*16712Method: move1671316714Translates the tree to the given position.1671516716Parameters:1671716718pos - (object) A *x, y* coordinate object where x, y in [0, 1), to move the tree to.16719opt - This object has been defined in <Hypertree.onClick>1672016721Example:1672216723(start code js)16724ht.move({ x: 0, y: 0.7 }, {16725hideLabels: false16726});16727(end code)1672816729*/16730move: function(pos, opt) {16731var versor = $C(pos.x, pos.y);16732if (this.busy === false && versor.norm() < 1) {16733this.busy = true;16734var root = this.graph.getClosestNodeToPos(versor), that = this;16735this.graph.computeLevels(root.id, 0);16736this.controller.onBeforeCompute(root);16737opt = $.merge( {16738onComplete: $.empty16739}, opt || {});16740this.fx.animate($.merge( {16741modes: [ 'moebius' ],16742hideLabels: true16743}, opt, {16744onComplete: function() {16745that.busy = false;16746opt.onComplete();16747}16748}), versor);16749}16750}16751});1675216753$jit.Hypertree.$extend = true;1675416755(function(Hypertree) {1675616757/*16758Class: Hypertree.Op1675916760Custom extension of <Graph.Op>.1676116762Extends:1676316764All <Graph.Op> methods1676516766See also:1676716768<Graph.Op>1676916770*/16771Hypertree.Op = new Class( {1677216773Implements: Graph.Op1677416775});1677616777/*16778Class: Hypertree.Plot1677916780Custom extension of <Graph.Plot>.1678116782Extends:1678316784All <Graph.Plot> methods1678516786See also:1678716788<Graph.Plot>1678916790*/16791Hypertree.Plot = new Class( {1679216793Implements: Graph.Plot1679416795});1679616797/*16798Object: Hypertree.Label1679916800Custom extension of <Graph.Label>.16801Contains custom <Graph.Label.SVG>, <Graph.Label.HTML> and <Graph.Label.Native> extensions.1680216803Extends:1680416805All <Graph.Label> methods and subclasses.1680616807See also:1680816809<Graph.Label>, <Graph.Label.Native>, <Graph.Label.HTML>, <Graph.Label.SVG>.1681016811*/16812Hypertree.Label = {};1681316814/*16815Hypertree.Label.Native1681616817Custom extension of <Graph.Label.Native>.1681816819Extends:1682016821All <Graph.Label.Native> methods1682216823See also:1682416825<Graph.Label.Native>1682616827*/16828Hypertree.Label.Native = new Class( {16829Implements: Graph.Label.Native,1683016831initialize: function(viz) {16832this.viz = viz;16833},1683416835renderLabel: function(canvas, node, controller) {16836var ctx = canvas.getCtx();16837var coord = node.pos.getc(true);16838var s = this.viz.getRadius();16839ctx.fillText(node.name, coord.x * s, coord.y * s);16840}16841});1684216843/*16844Hypertree.Label.SVG1684516846Custom extension of <Graph.Label.SVG>.1684716848Extends:1684916850All <Graph.Label.SVG> methods1685116852See also:1685316854<Graph.Label.SVG>1685516856*/16857Hypertree.Label.SVG = new Class( {16858Implements: Graph.Label.SVG,1685916860initialize: function(viz) {16861this.viz = viz;16862},1686316864/*16865placeLabel1686616867Overrides abstract method placeLabel in <Graph.Plot>.1686816869Parameters:1687016871tag - A DOM label element.16872node - A <Graph.Node>.16873controller - A configuration/controller object passed to the visualization.1687416875*/16876placeLabel: function(tag, node, controller) {16877var pos = node.pos.getc(true),16878canvas = this.viz.canvas,16879ox = canvas.translateOffsetX,16880oy = canvas.translateOffsetY,16881sx = canvas.scaleOffsetX,16882sy = canvas.scaleOffsetY,16883radius = canvas.getSize(),16884r = this.viz.getRadius();16885var labelPos = {16886x: Math.round((pos.x * sx) * r + ox + radius.width / 2),16887y: Math.round((pos.y * sy) * r + oy + radius.height / 2)16888};16889tag.setAttribute('x', labelPos.x);16890tag.setAttribute('y', labelPos.y);16891controller.onPlaceLabel(tag, node);16892}16893});1689416895/*16896Hypertree.Label.HTML1689716898Custom extension of <Graph.Label.HTML>.1689916900Extends:1690116902All <Graph.Label.HTML> methods.1690316904See also:1690516906<Graph.Label.HTML>1690716908*/16909Hypertree.Label.HTML = new Class( {16910Implements: Graph.Label.HTML,1691116912initialize: function(viz) {16913this.viz = viz;16914},16915/*16916placeLabel1691716918Overrides abstract method placeLabel in <Graph.Plot>.1691916920Parameters:1692116922tag - A DOM label element.16923node - A <Graph.Node>.16924controller - A configuration/controller object passed to the visualization.1692516926*/16927placeLabel: function(tag, node, controller) {16928var pos = node.pos.getc(true),16929canvas = this.viz.canvas,16930ox = canvas.translateOffsetX,16931oy = canvas.translateOffsetY,16932sx = canvas.scaleOffsetX,16933sy = canvas.scaleOffsetY,16934radius = canvas.getSize(),16935r = this.viz.getRadius();16936var labelPos = {16937x: Math.round((pos.x * sx) * r + ox + radius.width / 2),16938y: Math.round((pos.y * sy) * r + oy + radius.height / 2)16939};16940var style = tag.style;16941style.left = labelPos.x + 'px';16942style.top = labelPos.y + 'px';16943style.display = this.fitsInCanvas(labelPos, canvas) ? '' : 'none';1694416945controller.onPlaceLabel(tag, node);16946}16947});1694816949/*16950Class: Hypertree.Plot.NodeTypes1695116952This class contains a list of <Graph.Node> built-in types.16953Node types implemented are 'none', 'circle', 'triangle', 'rectangle', 'star', 'ellipse' and 'square'.1695416955You can add your custom node types, customizing your visualization to the extreme.1695616957Example:1695816959(start code js)16960Hypertree.Plot.NodeTypes.implement({16961'mySpecialType': {16962'render': function(node, canvas) {16963//print your custom node to canvas16964},16965//optional16966'contains': function(node, pos) {16967//return true if pos is inside the node or false otherwise16968}16969}16970});16971(end code)1697216973*/16974Hypertree.Plot.NodeTypes = new Class({16975'none': {16976'render': $.empty,16977'contains': $.lambda(false)16978},16979'circle': {16980'render': function(node, canvas) {16981var nconfig = this.node,16982dim = node.getData('dim'),16983p = node.pos.getc();16984dim = nconfig.transform? dim * (1 - p.squaredNorm()) : dim;16985p.$scale(node.scale);16986if (dim > 0.2) {16987this.nodeHelper.circle.render('fill', p, dim, canvas);16988}16989},16990'contains': function(node, pos) {16991var dim = node.getData('dim'),16992npos = node.pos.getc().$scale(node.scale);16993return this.nodeHelper.circle.contains(npos, pos, dim);16994}16995},16996'ellipse': {16997'render': function(node, canvas) {16998var pos = node.pos.getc().$scale(node.scale),16999width = node.getData('width'),17000height = node.getData('height');17001this.nodeHelper.ellipse.render('fill', pos, width, height, canvas);17002},17003'contains': function(node, pos) {17004var width = node.getData('width'),17005height = node.getData('height'),17006npos = node.pos.getc().$scale(node.scale);17007return this.nodeHelper.circle.contains(npos, pos, width, height);17008}17009},17010'square': {17011'render': function(node, canvas) {17012var nconfig = this.node,17013dim = node.getData('dim'),17014p = node.pos.getc();17015dim = nconfig.transform? dim * (1 - p.squaredNorm()) : dim;17016p.$scale(node.scale);17017if (dim > 0.2) {17018this.nodeHelper.square.render('fill', p, dim, canvas);17019}17020},17021'contains': function(node, pos) {17022var dim = node.getData('dim'),17023npos = node.pos.getc().$scale(node.scale);17024return this.nodeHelper.square.contains(npos, pos, dim);17025}17026},17027'rectangle': {17028'render': function(node, canvas) {17029var nconfig = this.node,17030width = node.getData('width'),17031height = node.getData('height'),17032pos = node.pos.getc();17033width = nconfig.transform? width * (1 - pos.squaredNorm()) : width;17034height = nconfig.transform? height * (1 - pos.squaredNorm()) : height;17035pos.$scale(node.scale);17036if (width > 0.2 && height > 0.2) {17037this.nodeHelper.rectangle.render('fill', pos, width, height, canvas);17038}17039},17040'contains': function(node, pos) {17041var width = node.getData('width'),17042height = node.getData('height'),17043npos = node.pos.getc().$scale(node.scale);17044return this.nodeHelper.rectangle.contains(npos, pos, width, height);17045}17046},17047'triangle': {17048'render': function(node, canvas) {17049var nconfig = this.node,17050dim = node.getData('dim'),17051p = node.pos.getc();17052dim = nconfig.transform? dim * (1 - p.squaredNorm()) : dim;17053p.$scale(node.scale);17054if (dim > 0.2) {17055this.nodeHelper.triangle.render('fill', p, dim, canvas);17056}17057},17058'contains': function(node, pos) {17059var dim = node.getData('dim'),17060npos = node.pos.getc().$scale(node.scale);17061return this.nodeHelper.triangle.contains(npos, pos, dim);17062}17063},17064'star': {17065'render': function(node, canvas) {17066var nconfig = this.node,17067dim = node.getData('dim'),17068p = node.pos.getc();17069dim = nconfig.transform? dim * (1 - p.squaredNorm()) : dim;17070p.$scale(node.scale);17071if (dim > 0.2) {17072this.nodeHelper.star.render('fill', p, dim, canvas);17073}17074},17075'contains': function(node, pos) {17076var dim = node.getData('dim'),17077npos = node.pos.getc().$scale(node.scale);17078return this.nodeHelper.star.contains(npos, pos, dim);17079}17080}17081});1708217083/*17084Class: Hypertree.Plot.EdgeTypes1708517086This class contains a list of <Graph.Adjacence> built-in types.17087Edge types implemented are 'none', 'line', 'arrow' and 'hyperline'.1708817089You can add your custom edge types, customizing your visualization to the extreme.1709017091Example:1709217093(start code js)17094Hypertree.Plot.EdgeTypes.implement({17095'mySpecialType': {17096'render': function(adj, canvas) {17097//print your custom edge to canvas17098},17099//optional17100'contains': function(adj, pos) {17101//return true if pos is inside the arc or false otherwise17102}17103}17104});17105(end code)1710617107*/17108Hypertree.Plot.EdgeTypes = new Class({17109'none': $.empty,17110'line': {17111'render': function(adj, canvas) {17112var from = adj.nodeFrom.pos.getc(true),17113to = adj.nodeTo.pos.getc(true),17114r = adj.nodeFrom.scale;17115this.edgeHelper.line.render({x:from.x*r, y:from.y*r}, {x:to.x*r, y:to.y*r}, canvas);17116},17117'contains': function(adj, pos) {17118var from = adj.nodeFrom.pos.getc(true),17119to = adj.nodeTo.pos.getc(true),17120r = adj.nodeFrom.scale;17121this.edgeHelper.line.contains({x:from.x*r, y:from.y*r}, {x:to.x*r, y:to.y*r}, pos, this.edge.epsilon);17122}17123},17124'arrow': {17125'render': function(adj, canvas) {17126var from = adj.nodeFrom.pos.getc(true),17127to = adj.nodeTo.pos.getc(true),17128r = adj.nodeFrom.scale,17129dim = adj.getData('dim'),17130direction = adj.data.$direction,17131inv = (direction && direction.length>1 && direction[0] != adj.nodeFrom.id);17132this.edgeHelper.arrow.render({x:from.x*r, y:from.y*r}, {x:to.x*r, y:to.y*r}, dim, inv, canvas);17133},17134'contains': function(adj, pos) {17135var from = adj.nodeFrom.pos.getc(true),17136to = adj.nodeTo.pos.getc(true),17137r = adj.nodeFrom.scale;17138this.edgeHelper.arrow.contains({x:from.x*r, y:from.y*r}, {x:to.x*r, y:to.y*r}, pos, this.edge.epsilon);17139}17140},17141'hyperline': {17142'render': function(adj, canvas) {17143var from = adj.nodeFrom.pos.getc(),17144to = adj.nodeTo.pos.getc(),17145dim = this.viz.getRadius();17146this.edgeHelper.hyperline.render(from, to, dim, canvas);17147},17148'contains': $.lambda(false)17149}17150});1715117152})($jit.Hypertree);17153})();171541715517156