2 Copyright (c) 2012 Sencha Inc. - Author: Nicolas Garcia Belmonte (http://philogb.github.com/)
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
33 Defines the namespace for all library Classes and Objects.
34 This variable is the *only* global variable defined in the Toolkit.
35 There are also other interesting properties attached to this variable described below.
37 this.$jit = function(w) {
46 $jit.version = '2.0.1';
50 Works just like *document.getElementById*
54 var element = $jit.id('elementId');
62 Contains utility functions.
64 Some of the utility functions and the Class system were based in the MooTools Framework
65 <http://mootools.net>. Copyright (c) 2006-2010 Valerio Proietti, <http://mad4milk.net/>.
66 MIT license <http://mootools.net/license.txt>.
68 These methods are generally also implemented in DOM manipulation frameworks like JQuery, MooTools and Prototype.
69 I'd suggest you to use the functions from those libraries instead of using these, since their functions
70 are widely used and tested in many different platforms/browsers. Use these functions only if you have to.
74 return document.getElementById(d);
77 $.empty = function() {
83 Augment an object by appending another object's properties.
87 original - (object) The object to be extended.
88 extended - (object) An object which properties are going to be appended to the original object.
92 $jit.util.extend({ 'a': 1, 'b': 2 }, { 'b': 3, 'c': 4 }); //{ 'a':1, 'b': 3, 'c': 4 }
95 $.extend = function(original, extended) {
96 for ( var key in (extended || {}))
97 original[key] = extended[key];
101 $.lambda = function(value) {
102 return (typeof value == 'function') ? value : function() {
107 $.time = Date.now || function() {
114 Returns an array wrapping *obj* if *obj* is not an array. Returns *obj* otherwise.
118 obj - (mixed) The object to be wrapped in an array.
122 $jit.util.splat(3); //[3]
123 $jit.util.splat([3]); //[3]
126 $.splat = function(obj) {
127 var type = $.type(obj);
128 return type ? ((type != 'array') ? [ obj ] : obj) : [];
131 $.type = function(elem) {
132 var type = $.type.s.call(elem).match(/^\[object\s(.*)\]$/)[1].toLowerCase();
133 if(type != 'object') return type;
134 if(elem && elem.$$family) return elem.$$family;
135 if(elem && elem.nodeType == 9) return 'htmldocument';
136 return (elem && elem.nodeName && elem.nodeType == 1)? 'element' : type;
138 $.type.s = Object.prototype.toString;
143 Iterates through an iterable applying *f*.
147 iterable - (array) The original array.
148 fn - (function) The function to apply to the array elements.
152 $jit.util.each([3, 4, 5], function(n) { alert('number ' + n); });
155 $.each = function(iterable, fn) {
156 var type = $.type(iterable);
157 if (type == 'object') {
158 for ( var key in iterable)
159 fn(iterable[key], key);
161 for ( var i = 0, l = iterable.length; i < l; i++)
166 $.indexOf = function(array, item) {
167 if(array.indexOf) return array.indexOf(item);
168 for(var i=0,l=array.length; i<l; i++) {
169 if(array[i] === item) return i;
177 Maps or collects an array by applying *f*.
181 array - (array) The original array.
182 f - (function) The function to apply to the array elements.
186 $jit.util.map([3, 4, 5], function(n) { return n*n; }); //[9, 16, 25]
189 $.map = function(array, f) {
191 $.each(array, function(elem, i) {
192 ans.push(f(elem, i));
200 Iteratively applies the binary function *f* storing the result in an accumulator.
204 array - (array) The original array.
205 f - (function) The function to apply to the array elements.
206 opt - (optional|mixed) The starting value for the acumulator.
210 $jit.util.reduce([3, 4, 5], function(x, y) { return x + y; }, 0); //12
213 $.reduce = function(array, f, opt) {
214 var l = array.length;
216 var acum = arguments.length == 3? opt : array[--l];
218 acum = f(acum, array[l]);
226 Merges n-objects and their sub-objects creating a new, fresh object.
230 An arbitrary number of objects.
234 $jit.util.merge({ 'a': 1, 'b': 2 }, { 'b': 3, 'c': 4 }); //{ 'a':1, 'b': 3, 'c': 4 }
237 $.merge = function() {
239 for ( var i = 0, l = arguments.length; i < l; i++) {
240 var object = arguments[i];
241 if ($.type(object) != 'object')
243 for ( var key in object) {
244 var op = object[key], mp = mix[key];
245 mix[key] = (mp && $.type(op) == 'object' && $.type(mp) == 'object') ? $
246 .merge(mp, op) : $.unlink(op);
252 $.unlink = function(object) {
254 switch ($.type(object)) {
257 for ( var p in object)
258 unlinked[p] = $.unlink(object[p]);
262 for ( var i = 0, l = object.length; i < l; i++)
263 unlinked[i] = $.unlink(object[i]);
272 if(arguments.length === 0) return [];
273 for(var j=0, ans=[], l=arguments.length, ml=arguments[0].length; j<ml; j++) {
274 for(var i=0, row=[]; i<l; i++) {
275 row.push(arguments[i][j]);
285 Converts an RGB array into a Hex string.
289 srcArray - (array) An array with R, G and B values
293 $jit.util.rgbToHex([255, 255, 255]); //'#ffffff'
296 $.rgbToHex = function(srcArray, array) {
297 if (srcArray.length < 3)
299 if (srcArray.length == 4 && srcArray[3] == 0 && !array)
300 return 'transparent';
302 for ( var i = 0; i < 3; i++) {
303 var bit = (srcArray[i] - 0).toString(16);
304 hex.push(bit.length == 1 ? '0' + bit : bit);
306 return array ? hex : '#' + hex.join('');
312 Converts an Hex color string into an RGB array.
316 hex - (string) A color hex string.
320 $jit.util.hexToRgb('#fff'); //[255, 255, 255]
323 $.hexToRgb = function(hex) {
324 if (hex.length != 7) {
325 hex = hex.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
330 for ( var i = 0; i < 3; i++) {
332 if (value.length == 1)
334 rgb.push(parseInt(value, 16));
338 hex = parseInt(hex.slice(1), 16);
339 return [ hex >> 16, hex >> 8 & 0xff, hex & 0xff ];
343 $.destroy = function(elem) {
346 elem.parentNode.removeChild(elem);
347 if (elem.clearAttributes)
348 elem.clearAttributes();
351 $.clean = function(elem) {
352 for (var ch = elem.childNodes, i = 0, l = ch.length; i < l; i++) {
360 Cross-browser add event listener.
364 obj - (obj) The Element to attach the listener to.
365 type - (string) The listener type. For example 'click', or 'mousemove'.
366 fn - (function) The callback function to be used when the event is fired.
370 $jit.util.addEvent(elem, 'click', function(){ alert('hello'); });
373 $.addEvent = function(obj, type, fn) {
374 if (obj.addEventListener)
375 obj.addEventListener(type, fn, false);
377 obj.attachEvent('on' + type, fn);
380 $.addEvents = function(obj, typeObj) {
381 for(var type in typeObj) {
382 $.addEvent(obj, type, typeObj[type]);
386 $.hasClass = function(obj, klass) {
387 return (' ' + obj.className + ' ').indexOf(' ' + klass + ' ') > -1;
390 $.addClass = function(obj, klass) {
391 if (!$.hasClass(obj, klass))
392 obj.className = (obj.className + " " + klass);
395 $.removeClass = function(obj, klass) {
396 obj.className = obj.className.replace(new RegExp(
397 '(^|\\s)' + klass + '(?:\\s|$)'), '$1');
400 $.getPos = function(elem) {
401 var offset = getOffsets(elem);
402 var scroll = getScrolls(elem);
404 x: offset.x - scroll.x,
405 y: offset.y - scroll.y
408 function getOffsets(elem) {
413 while (elem && !isBody(elem)) {
414 position.x += elem.offsetLeft;
415 position.y += elem.offsetTop;
416 elem = elem.offsetParent;
421 function getScrolls(elem) {
426 while (elem && !isBody(elem)) {
427 position.x += elem.scrollLeft;
428 position.y += elem.scrollTop;
429 elem = elem.parentNode;
434 function isBody(element) {
435 return (/^(?:body|html)$/i).test(element.tagName);
440 get: function(e, win) {
442 return e || win.event;
444 getWheel: function(e) {
445 return e.wheelDelta? e.wheelDelta / 120 : -(e.detail || 0) / 3;
447 isRightClick: function(e) {
448 return (e.which == 3 || e.button == 2);
450 getPos: function(e, win) {
451 // get mouse position
454 var doc = win.document;
455 doc = doc.documentElement || doc.body;
456 //TODO(nico): make touch event handling better
457 if(e.touches && e.touches.length) {
461 x: e.pageX || (e.clientX + doc.scrollLeft),
462 y: e.pageY || (e.clientY + doc.scrollTop)
467 if (e.stopPropagation) e.stopPropagation();
468 e.cancelBubble = true;
469 if (e.preventDefault) e.preventDefault();
470 else e.returnValue = false;
474 $jit.util = $jit.id = $;
476 var Class = function(properties) {
477 properties = properties || {};
478 var klass = function() {
479 for ( var key in this) {
480 if (typeof this[key] != 'function')
481 this[key] = $.unlink(this[key]);
483 this.constructor = klass;
484 if (Class.prototyping)
486 var instance = this.initialize ? this.initialize.apply(this, arguments)
489 this.$$family = 'class';
493 for ( var mutator in Class.Mutators) {
494 if (!properties[mutator])
496 properties = Class.Mutators[mutator](properties, properties[mutator]);
497 delete properties[mutator];
500 $.extend(klass, this);
501 klass.constructor = Class;
502 klass.prototype = properties;
508 Implements: function(self, klasses) {
509 $.each($.splat(klasses), function(klass) {
510 Class.prototyping = klass;
511 var instance = (typeof klass == 'function') ? new klass : klass;
512 for ( var prop in instance) {
513 if (!(prop in self)) {
514 self[prop] = instance[prop];
517 delete Class.prototyping;
526 inherit: function(object, properties) {
527 for ( var key in properties) {
528 var override = properties[key];
529 var previous = object[key];
530 var type = $.type(override);
531 if (previous && type == 'function') {
532 if (override != previous) {
533 Class.override(object, key, override);
535 } else if (type == 'object') {
536 object[key] = $.merge(previous, override);
538 object[key] = override;
544 override: function(object, name, method) {
545 var parent = Class.prototyping;
546 if (parent && object[name] != parent[name])
548 var override = function() {
549 var previous = this.parent;
550 this.parent = parent ? parent[name] : object[name];
551 var value = method.apply(this, arguments);
552 this.parent = previous;
555 object[name] = override;
560 Class.prototype.implement = function() {
561 var proto = this.prototype;
562 $.each(Array.prototype.slice.call(arguments || []), function(properties) {
563 Class.inherit(proto, properties);
573 Provides JSON utility functions.
575 Most of these functions are JSON-tree traversal and manipulation functions.
581 Clears all tree nodes having depth greater than maxLevel.
585 tree - (object) A JSON tree object. For more information please see <Loader.loadJSON>.
586 maxLevel - (number) An integer specifying the maximum level allowed for this tree. All nodes having depth greater than max level will be deleted.
589 prune: function(tree, maxLevel) {
590 this.each(tree, function(elem, i) {
591 if (i == maxLevel && elem.children) {
592 delete elem.children;
600 Returns the parent node of the node having _id_ as id.
604 tree - (object) A JSON tree object. See also <Loader.loadJSON>.
605 id - (string) The _id_ of the child node whose parent will be returned.
609 A tree JSON node if any, or false otherwise.
612 getParent: function(tree, id) {
615 var ch = tree.children;
616 if (ch && ch.length > 0) {
617 for ( var i = 0; i < ch.length; i++) {
621 var ans = this.getParent(ch[i], id);
632 Returns the subtree that matches the given id.
636 tree - (object) A JSON tree object. See also <Loader.loadJSON>.
637 id - (string) A node *unique* identifier.
641 A subtree having a root node matching the given id. Returns null if no subtree matching the id is found.
644 getSubtree: function(tree, id) {
647 for ( var i = 0, ch = tree.children; ch && i < ch.length; i++) {
648 var t = this.getSubtree(ch[i], id);
657 Iterates on tree nodes with relative depth less or equal than a specified level.
661 tree - (object) A JSON tree or subtree. See also <Loader.loadJSON>.
662 initLevel - (number) An integer specifying the initial relative level. Usually zero.
663 toLevel - (number) An integer specifying a top level. This method will iterate only through nodes with depth less than or equal this number.
664 action - (function) A function that receives a node and an integer specifying the actual level of the node.
668 $jit.json.eachLevel(tree, 0, 3, function(node, depth) {
669 alert(node.name + ' ' + depth);
673 eachLevel: function(tree, initLevel, toLevel, action) {
674 if (initLevel <= toLevel) {
675 action(tree, initLevel);
676 if(!tree.children) return;
677 for ( var i = 0, ch = tree.children; i < ch.length; i++) {
678 this.eachLevel(ch[i], initLevel + 1, toLevel, action);
685 A JSON tree iterator.
689 tree - (object) A JSON tree or subtree. See also <Loader.loadJSON>.
690 action - (function) A function that receives a node.
694 $jit.json.each(tree, function(node) {
700 each: function(tree, action) {
701 this.eachLevel(tree, 0, Number.MAX_VALUE, action);
707 An object containing multiple type of transformations.
718 var Trans = $jit.Trans;
722 var makeTrans = function(transition, params){
723 params = $.splat(params);
724 return $.extend(transition, {
725 easeIn: function(pos){
726 return transition(pos, params);
728 easeOut: function(pos){
729 return 1 - transition(1 - pos, params);
731 easeInOut: function(pos){
732 return (pos <= 0.5)? transition(2 * pos, params) / 2 : (2 - transition(
733 2 * (1 - pos), params)) / 2;
741 return Math.pow(p, x[0] || 6);
745 return Math.pow(2, 8 * (p - 1));
749 return 1 - Math.sin(Math.acos(p));
753 return 1 - Math.sin((1 - p) * Math.PI / 2);
756 Back: function(p, x){
758 return Math.pow(p, 2) * ((x + 1) * p - x);
763 for ( var a = 0, b = 1; 1; a += b, b /= 2) {
764 if (p >= (7 - 4 * a) / 11) {
765 value = b * b - Math.pow((11 - 6 * a - 11 * p) / 4, 2);
772 Elastic: function(p, x){
773 return Math.pow(2, 10 * --p)
774 * Math.cos(20 * p * Math.PI * (x[0] || 1) / 3);
779 $.each(transitions, function(val, key){
780 Trans[key] = makeTrans(val);
784 'Quad', 'Cubic', 'Quart', 'Quint'
785 ], function(elem, i){
786 Trans[elem] = makeTrans(function(p){
796 A Class that can perform animations for generic objects.
798 If you are looking for animation transitions please take a look at the <Trans> object.
806 The 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>.
810 var Animation = new Class( {
812 initialize: function(options){
813 this.setOptions(options);
816 setOptions: function(options){
820 transition: Trans.Quart.easeInOut,
825 this.opt = $.merge(opt, options || {});
830 var time = $.time(), opt = this.opt;
831 if (time < this.time + opt.duration) {
832 var delta = opt.transition((time - this.time) / opt.duration);
835 this.timer = clearInterval(this.timer);
849 startTimer: function(){
850 var that = this, fps = this.opt.fps;
853 this.time = $.time() - this.time;
854 this.timer = setInterval((function(){
856 }), Math.round(1000 / fps));
870 stopTimer: function(){
873 this.time = $.time() - this.time;
874 this.timer = clearInterval(this.timer);
881 if (this.opt.link == 'cancel') {
890 var Options = function() {
891 var args = arguments;
892 for(var i=0, l=args.length, ans={}; i<l; i++) {
893 var opt = Options[args[i]];
904 * File: Options.Canvas.js
909 Object: Options.Canvas
911 These are Canvas general options, like where to append it in the DOM, its dimensions, background,
912 and other more advanced options.
932 var viz = new $jit.Viz({
933 injectInto: 'someContainerId',
941 injectInto - *required* (string|element) The id of the DOM container for the visualization. It can also be an Element provided that it has an id.
942 type - (string) Context type. Default's 2D but can be 3D for webGL enabled browsers.
943 width - (number) Default's to the *container's offsetWidth*. The width of the canvas.
944 height - (number) Default's to the *container's offsetHeight*. The height of the canvas.
945 useCanvas - (boolean|object) Default's *false*. You can pass another <Canvas> instance to be used by the visualization.
946 withLabels - (boolean) Default's *true*. Whether to use a label container for the visualization.
947 background - (boolean|object) Default's *false*. An object containing information about the rendering of a background canvas.
966 direction: { x: -100, y: -100, z: -100 },
967 color: [0.5, 0.3, 0.1]
974 * File: Options.Node.js
981 Provides Node rendering options for Tree and Graph based visualizations.
1008 var viz = new $jit.Viz({
1020 overridable - (boolean) Default's *false*. Determine whether or not general node properties can be overridden by a particular <Graph.Node>.
1021 type - (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.
1022 color - (string) Default's *#ccb*. Node color.
1023 alpha - (number) Default's *1*. The Node's alpha value. *1* is for full opacity.
1024 dim - (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).
1025 height - (number) Default's *20*. Used by 'rectangle' and 'ellipse' node types. The height of the node shape.
1026 width - (number) Default's *90*. Used by 'rectangle' and 'ellipse' node types. The width of the node shape.
1027 autoHeight - (boolean) Default's *false*. Whether to set an auto height for the node depending on the content of the Node's label.
1028 autoWidth - (boolean) Default's *false*. Whether to set an auto width for the node depending on the content of the Node's label.
1029 lineWidth - (number) Default's *1*. Used only by some Node shapes. The line width of the strokes of a node.
1030 transform - (boolean) Default's *true*. Only used by the <Hypertree> visualization. Whether to scale the nodes according to the moebius transformation.
1031 align - (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.
1032 angularWidth - (number) Default's *1*. Used in radial layouts (like <RGraph> or <Sunburst> visualizations). The amount of relative 'space' set for a node.
1033 span - (number) Default's *1*. Used in radial layouts (like <RGraph> or <Sunburst> visualizations). The angle span amount set for a node.
1034 CanvasStyles - (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.
1054 //Raw canvas styles to be
1055 //applied to the context instance
1056 //before plotting a node
1062 * File: Options.Edge.js
1067 Object: Options.Edge
1069 Provides Edge rendering options for Tree and Graph based visualizations.
1088 var viz = new $jit.Viz({
1094 shadowColor: '#ccc',
1103 overridable - (boolean) Default's *false*. Determine whether or not general edges properties can be overridden by a particular <Graph.Adjacence>.
1104 type - (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.
1105 color - (string) Default's '#ccb'. Edge color.
1106 lineWidth - (number) Default's *1*. Line/Edge width.
1107 alpha - (number) Default's *1*. The Edge's alpha value. *1* is for full opacity.
1108 dim - (number) Default's *15*. An extra parameter used by other complex shapes such as quadratic, bezier or arrow, to determine the shape's diameter.
1109 epsilon - (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*.
1110 CanvasStyles - (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.
1114 If 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.
1127 //Raw canvas styles to be
1128 //applied to the context instance
1129 //before plotting an edge
1135 * File: Options.Fx.js
1142 Provides animation options like duration of the animations, frames per second and animation transitions.
1150 transition: $jit.Trans.Quart.easeInOut,
1158 var viz = new $jit.Viz({
1161 transition: $jit.Trans.linear
1167 clearCanvas - (boolean) Default's *true*. Whether to clear the frame/canvas when the viz is plotted or animated.
1168 duration - (number) Default's *2500*. Duration of the animation in milliseconds.
1169 fps - (number) Default's *40*. Frames per second.
1170 transition - (object) Default's *$jit.Trans.Quart.easeInOut*. The transition used for the animations. See below for a more detailed explanation.
1174 This object is used for specifying different animation transitions in all visualizations.
1176 There are many different type of animation transitions.
1180 Displays a linear transition
1188 Displays a Quadratic transition.
1192 >Trans.Quad.easeInOut
1198 Displays a Cubic transition.
1201 >Trans.Cubic.easeOut
1202 >Trans.Cubic.easeInOut
1208 Displays a Quartetic transition.
1211 >Trans.Quart.easeOut
1212 >Trans.Quart.easeInOut
1218 Displays a Quintic transition.
1221 >Trans.Quint.easeOut
1222 >Trans.Quint.easeInOut
1228 Displays an Exponential transition.
1232 >Trans.Expo.easeInOut
1238 Displays a Circular transition.
1242 >Trans.Circ.easeInOut
1248 Displays a Sineousidal transition.
1252 >Trans.Sine.easeInOut
1260 >Trans.Back.easeInOut
1268 >Trans.Bounce.easeIn
1269 >Trans.Bounce.easeOut
1270 >Trans.Bounce.easeInOut
1278 >Trans.Elastic.easeIn
1279 >Trans.Elastic.easeOut
1280 >Trans.Elastic.easeInOut
1286 Easing 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>.
1295 transition: $jit.Trans.Quart.easeInOut,
1300 * File: Options.Label.js
1304 Object: Options.Label
1306 Provides styling for Labels such as font size, family, etc. Also sets Node labels as HTML, SVG or Native canvas elements.
1313 type: 'HTML', //'SVG', 'Native'
1316 family: 'sans-serif',
1317 textAlign: 'center',
1318 textBaseline: 'alphabetic',
1326 var viz = new $jit.Viz({
1337 overridable - (boolean) Default's *false*. Determine whether or not general label properties can be overridden by a particular <Graph.Node>.
1338 type - (string) Default's *HTML*. The type for the labels. Can be 'HTML', 'SVG' or 'Native' canvas labels.
1339 style - (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.
1340 size - (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.
1341 family - (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.
1342 color - (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.
1348 type: 'HTML', //'SVG', 'Native'
1351 family: 'sans-serif',
1352 textAlign: 'center',
1353 textBaseline: 'alphabetic',
1359 * File: Options.Tips.js
1364 Object: Options.Tips
1384 var viz = new $jit.Viz({
1390 onShow: function(tip, node) {
1391 tip.innerHTML = node.name;
1399 enable - (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.
1400 type - (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.
1401 offsetX - (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.
1402 offsetY - (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.
1403 onShow(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.
1404 onHide() - This callack is used when hiding a tooltip.
1421 * File: Options.NodeStyles.js
1426 Object: Options.NodeStyles
1428 Apply different styles when a node is hovered or selected.
1433 Options.NodeStyles = {
1444 var viz = new $jit.Viz({
1459 enable - (boolean) Default's *false*. Whether to enable this option.
1460 type - (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>.
1461 stylesHover - (boolean|object) Default's *false*. An object with node styles just like the ones defined for <Options.Node> or *false* otherwise.
1462 stylesClick - (boolean|object) Default's *false*. An object with node styles just like the ones defined for <Options.Node> or *false* otherwise.
1465 Options.NodeStyles = {
1476 * File: Options.Events.js
1481 Object: Options.Events
1483 Configuration for adding mouse/touch event handlers to Nodes.
1490 enableForEdges: false,
1493 onRightClick: $.empty,
1494 onMouseMove: $.empty,
1495 onMouseEnter: $.empty,
1496 onMouseLeave: $.empty,
1497 onDragStart: $.empty,
1498 onDragMove: $.empty,
1499 onDragCancel: $.empty,
1501 onTouchStart: $.empty,
1502 onTouchMove: $.empty,
1503 onTouchEnd: $.empty,
1504 onTouchCancel: $.empty,
1505 onMouseWheel: $.empty
1512 var viz = new $jit.Viz({
1515 onClick: function(node, eventInfo, e) {
1518 onMouseEnter: function(node, eventInfo, e) {
1519 viz.canvas.getElement().style.cursor = 'pointer';
1521 onMouseLeave: function(node, eventInfo, e) {
1522 viz.canvas.getElement().style.cursor = '';
1530 enable - (boolean) Default's *false*. Whether to enable the Event system.
1531 enableForEdges - (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*.
1532 type - (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.
1533 onClick(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.
1534 onRightClick(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.
1535 onMouseMove(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.
1536 onMouseEnter(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.
1537 onMouseLeave(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.
1538 onDragStart(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.
1539 onDragMove(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.
1540 onDragEnd(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.
1541 onDragCancel(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.
1542 onTouchStart(node, eventInfo, e) - Behaves just like onDragStart.
1543 onTouchMove(node, eventInfo, e) - Behaves just like onDragMove.
1544 onTouchEnd(node, eventInfo, e) - Behaves just like onDragEnd.
1545 onTouchCancel(node, eventInfo, e) - Behaves just like onDragCancel.
1546 onMouseWheel(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.
1553 enableForEdges: false,
1556 onRightClick: $.empty,
1557 onMouseMove: $.empty,
1558 onMouseEnter: $.empty,
1559 onMouseLeave: $.empty,
1560 onDragStart: $.empty,
1561 onDragMove: $.empty,
1562 onDragCancel: $.empty,
1564 onTouchStart: $.empty,
1565 onTouchMove: $.empty,
1566 onTouchEnd: $.empty,
1567 onMouseWheel: $.empty
1571 * File: Options.Navigation.js
1576 Object: Options.Navigation
1578 Panning and zooming options for Graph/Tree based visualizations. These options are implemented
1579 by all visualizations except charts (<AreaChart>, <BarChart> and <PieChart>).
1585 Options.Navigation = {
1588 panning: false, //true, 'avoid nodes'
1597 var viz = new $jit.Viz({
1600 panning: 'avoid nodes',
1608 enable - (boolean) Default's *false*. Whether to enable Navigation capabilities.
1609 type - (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.
1610 panning - (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>.
1611 zooming - (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.
1615 Options.Navigation = {
1620 panning: false, //true | 'avoid nodes'
1625 * File: Options.Controller.js
1630 Object: Options.Controller
1632 Provides controller methods. Controller methods are callback functions that get called at different stages
1633 of the animation, computing or plotting of the visualization.
1637 All visualizations except charts (<AreaChart>, <BarChart> and <PieChart>).
1643 Options.Controller = {
1644 onBeforeCompute: $.empty,
1645 onAfterCompute: $.empty,
1646 onCreateLabel: $.empty,
1647 onPlaceLabel: $.empty,
1648 onComplete: $.empty,
1649 onBeforePlotLine:$.empty,
1650 onAfterPlotLine: $.empty,
1651 onBeforePlotNode:$.empty,
1652 onAfterPlotNode: $.empty,
1661 var viz = new $jit.Viz({
1662 onBeforePlotNode: function(node) {
1664 node.setData('color', '#ffc');
1666 node.removeData('color');
1669 onBeforePlotLine: function(adj) {
1670 if(adj.nodeFrom.selected && adj.nodeTo.selected) {
1671 adj.setData('color', '#ffc');
1673 adj.removeData('color');
1676 onAfterCompute: function() {
1684 onBeforeCompute(node) - This method is called right before performing all computations and animations. The selected <Graph.Node> is passed as parameter.
1685 onAfterCompute() - This method is triggered after all animations or computations ended.
1686 onCreateLabel(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.
1687 onPlaceLabel(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.
1688 onBeforePlotNode(node) - This method is triggered right before plotting each <Graph.Node>. This method is useful for changing a node style right before plotting it.
1689 onAfterPlotNode(node) - This method is triggered right after plotting each <Graph.Node>.
1690 onBeforePlotLine(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.
1691 onAfterPlotLine(adj) - This method is triggered right after plotting a <Graph.Adjacence>.
1692 onBeforeRemoveNode(node) - This method is triggered right before removing each <Graph.Node>.
1694 *Used in <ST>, <TM.Base> and <Icicle> visualizations*
1696 request(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.
1699 Options.Controller = {
1702 onBeforeCompute: $.empty,
1703 onAfterCompute: $.empty,
1704 onCreateLabel: $.empty,
1705 onPlaceLabel: $.empty,
1706 onComplete: $.empty,
1707 onBeforePlotLine: $.empty,
1708 onAfterPlotLine: $.empty,
1709 onBeforePlotNode: $.empty,
1710 onAfterPlotNode: $.empty,
1711 onBeforeRemoveNode:$.empty,
1719 * Provides Extras such as Tips and Style Effects.
1723 * Provides the <Tips> and <NodeStyles> classes and functions.
1728 * Manager for mouse events (clicking and mouse moving).
1730 * This class is used for registering objects implementing onClick
1731 * and onMousemove methods. These methods are called when clicking or
1732 * moving the mouse around the Canvas.
1733 * For now, <Tips> and <NodeStyles> are classes implementing these methods.
1736 var MultiExtrasInitializer = {
1737 initialize: function(className, viz) {
1739 this.canvas = viz.canvas;
1740 this.config = viz.config[className];
1741 this.nodeTypes = viz.fx.nodeTypes;
1742 var type = this.config.type;
1743 this.dom = type == 'auto'? (viz.config.Label.type != 'Native') : (type != 'Native');
1744 this.labelContainer = this.dom && viz.labels.getLabelContainer();
1745 this.isEnabled() && this.initializePost();
1747 initializePost: $.empty,
1748 setAsProperty: $.lambda(false),
1749 isEnabled: function() {
1750 return this.config.enable;
1752 isLabel: function(e, win, group) {
1753 e = $.event.get(e, win);
1754 var labelContainer = this.labelContainer,
1755 target = e.target || e.srcElement,
1756 related = e.relatedTarget;
1758 return related && related == this.viz.canvas.getCtx().canvas
1759 && !!target && this.isDescendantOf(target, labelContainer);
1761 return this.isDescendantOf(target, labelContainer);
1764 isDescendantOf: function(elem, par) {
1765 while(elem && elem.parentNode) {
1766 if(elem.parentNode == par)
1768 elem = elem.parentNode;
1774 var MultiEventsInterface = {
1776 onMouseDown: $.empty,
1777 onMouseMove: $.empty,
1778 onMouseOver: $.empty,
1779 onMouseOut: $.empty,
1780 onMouseWheel: $.empty,
1781 onTouchStart: $.empty,
1782 onTouchMove: $.empty,
1783 onTouchEnd: $.empty,
1784 onTouchCancel: $.empty
1787 var MouseEventsManager = new Class({
1788 initialize: function(viz) {
1790 this.canvas = viz.canvas;
1793 this.registeredObjects = [];
1794 this.attachEvents();
1797 attachEvents: function() {
1798 var htmlCanvas = this.canvas.getElement(),
1800 htmlCanvas.oncontextmenu = $.lambda(false);
1801 $.addEvents(htmlCanvas, {
1802 'mouseup': function(e, win) {
1803 var event = $.event.get(e, win);
1804 that.handleEvent('MouseUp', e, win,
1805 that.makeEventObject(e, win),
1806 $.event.isRightClick(event));
1808 'mousedown': function(e, win) {
1809 var event = $.event.get(e, win);
1810 that.handleEvent('MouseDown', e, win, that.makeEventObject(e, win),
1811 $.event.isRightClick(event));
1813 'mousemove': function(e, win) {
1814 that.handleEvent('MouseMove', e, win, that.makeEventObject(e, win));
1816 'mouseover': function(e, win) {
1817 that.handleEvent('MouseOver', e, win, that.makeEventObject(e, win));
1819 'mouseout': function(e, win) {
1820 that.handleEvent('MouseOut', e, win, that.makeEventObject(e, win));
1822 'touchstart': function(e, win) {
1823 that.handleEvent('TouchStart', e, win, that.makeEventObject(e, win));
1825 'touchmove': function(e, win) {
1826 that.handleEvent('TouchMove', e, win, that.makeEventObject(e, win));
1828 'touchend': function(e, win) {
1829 that.handleEvent('TouchEnd', e, win, that.makeEventObject(e, win));
1832 //attach mousewheel event
1833 var handleMouseWheel = function(e, win) {
1834 var event = $.event.get(e, win);
1835 var wheel = $.event.getWheel(event);
1836 that.handleEvent('MouseWheel', e, win, wheel);
1838 //TODO(nico): this is a horrible check for non-gecko browsers!
1839 if(!document.getBoxObjectFor && window.mozInnerScreenX == null) {
1840 $.addEvent(htmlCanvas, 'mousewheel', handleMouseWheel);
1842 htmlCanvas.addEventListener('DOMMouseScroll', handleMouseWheel, false);
1846 register: function(obj) {
1847 this.registeredObjects.push(obj);
1850 handleEvent: function() {
1851 var args = Array.prototype.slice.call(arguments),
1852 type = args.shift();
1853 for(var i=0, regs=this.registeredObjects, l=regs.length; i<l; i++) {
1854 regs[i]['on' + type].apply(regs[i], args);
1858 makeEventObject: function(e, win) {
1860 graph = this.viz.graph,
1862 ntypes = fx.nodeTypes,
1863 etypes = fx.edgeTypes;
1869 getNodeCalled: false,
1870 getEdgeCalled: false,
1871 getPos: function() {
1872 //TODO(nico): check why this can't be cache anymore when using edge detection
1873 //if(this.pos) return this.pos;
1874 var canvas = that.viz.canvas,
1875 s = canvas.getSize(),
1876 p = canvas.getPos(),
1877 ox = canvas.translateOffsetX,
1878 oy = canvas.translateOffsetY,
1879 sx = canvas.scaleOffsetX,
1880 sy = canvas.scaleOffsetY,
1881 pos = $.event.getPos(e, win);
1883 x: (pos.x - p.x - s.width/2 - ox) * 1/sx,
1884 y: (pos.y - p.y - s.height/2 - oy) * 1/sy
1888 getNode: function() {
1889 if(this.getNodeCalled) return this.node;
1890 this.getNodeCalled = true;
1891 for(var id in graph.nodes) {
1892 var n = graph.nodes[id],
1893 geom = n && ntypes[n.getData('type')],
1894 contains = geom && geom.contains && geom.contains.call(fx, n, this.getPos());
1896 this.contains = contains;
1897 return that.node = this.node = n;
1900 return that.node = this.node = false;
1902 getEdge: function() {
1903 if(this.getEdgeCalled) return this.edge;
1904 this.getEdgeCalled = true;
1906 var checkDupEdge = function(e) {
1908 for(var d in graph.dup) {
1909 var ee = graph.dup[d];
1910 if(e.nodeTo.id == ee.nodeTo.id && e.nodeFrom.id == ee.nodeFrom.id) {
1911 if(e.portTo == ee.portTo && e.portFrom == ee.portFrom) {
1913 break; // NOTE: does this break the outer for loop?
1920 // you want to go through all the edges in this graph
1921 for(var id in graph.edges) {
1922 var edgeFrom = graph.edges[id];
1924 for(var edgeId in edgeFrom) {
1925 // proceed with contains check
1926 var total = edgeFrom[edgeId].length;
1928 var e = edgeFrom[edgeId][0];
1930 if(checkDupEdge(e)) continue; // skip this edge if it is a dup
1932 var geom = e && etypes[e.getData('type')],
1933 contains = geom && geom.contains && geom.contains.call(fx, e, 0, this.getPos(), that.canvas);
1936 this.contains = contains;
1937 return that.edge = this.edge = e;
1940 for(var idj in edgeFrom[edgeId]) {
1941 var e = edgeFrom[edgeId][idj];
1943 if(checkDupEdge(e)) continue; // skip this edge if it is a dup
1945 var alpha = parseInt(idj,10);
1946 var start = (0.5-(total/2));
1949 var geom = e && etypes[e.getData('type')],
1950 contains = geom && geom.contains && geom.contains.call(fx, e, alpha, this.getPos(), that.canvas);
1952 this.contains = contains;
1953 return that.edge = this.edge = e;
1959 return that.edge = this.edge = false;
1962 if(this.getEdgeCalled) return this.edge;
1963 this.getEdgeCalled = true;
1965 for(var id in graph.edges) {
1966 var edgeFrom = graph.edges[id];
1968 for(var edgeId in edgeFrom) {
1969 if(edgeId in hashset) continue;
1970 var e = edgeFrom[edgeId],
1971 geom = e && etypes[e.getData('type')],
1972 contains = geom && geom.contains && geom.contains.call(fx, e, this.getPos());
1974 this.contains = contains;
1975 return that.edge = this.edge = e;
1979 return that.edge = this.edge = false;
1982 getContains: function() {
1983 if(this.getNodeCalled) return this.contains;
1985 return this.contains;
1992 * Provides the initialization function for <NodeStyles> and <Tips> implemented
1993 * by all main visualizations.
1997 initializeExtras: function() {
1998 var mem = new MouseEventsManager(this), that = this;
1999 $.each(['NodeStyles', 'Tips', 'Navigation', 'Events'], function(k) {
2000 var obj = new MultiExtras.Classes[k](k, that);
2001 if(obj.isEnabled()) {
2004 if(obj.setAsProperty()) {
2005 that[k.toLowerCase()] = obj;
2011 MultiExtras.Classes = {};
2015 This class defines an Event API to be accessed by the user.
2016 The methods implemented are the ones defined in the <Options.Events> object.
2019 MultiExtras.Classes.Events = new Class({
2020 Implements: [MultiExtrasInitializer, MultiEventsInterface],
2022 initializePost: function() {
2023 this.fx = this.viz.fx;
2024 this.ntypes = this.viz.fx.nodeTypes;
2025 this.etypes = this.viz.fx.edgeTypes;
2027 this.hovered = false;
2028 this.pressed = false;
2029 this.touched = false;
2031 this.touchMoved = false;
2036 setAsProperty: $.lambda(true),
2038 onMouseUp: function(e, win, event, isRightClick) {
2039 var evt = $.event.get(e, win);
2042 this.config.onRightClick(this.hovered, event, evt);
2044 this.config.onClick(this.pressed, event, evt);
2049 this.config.onDragEnd(this.pressed, event, evt);
2051 this.config.onDragCancel(this.pressed, event, evt);
2053 this.pressed = this.moved = false;
2057 onMouseOut: function(e, win, event) {
2059 var evt = $.event.get(e, win), label;
2060 if(this.dom && (label = this.isLabel(e, win, true))) {
2061 this.config.onMouseLeave(this.viz.graph.getNode(label.id),
2063 this.hovered = false;
2067 var rt = evt.relatedTarget,
2068 canvasWidget = this.canvas.getElement();
2069 while(rt && rt.parentNode) {
2070 if(canvasWidget == rt.parentNode) return;
2074 this.config.onMouseLeave(this.hovered,
2076 this.hovered = false;
2080 onMouseOver: function(e, win, event) {
2082 var evt = $.event.get(e, win), label;
2083 if(this.dom && (label = this.isLabel(e, win, true))) {
2084 this.hovered = this.viz.graph.getNode(label.id);
2085 this.config.onMouseEnter(this.hovered,
2090 onMouseMove: function(e, win, event) {
2091 var label, evt = $.event.get(e, win);
2094 this.config.onDragMove(this.pressed, event, evt);
2098 this.config.onMouseMove(this.hovered, event, evt);
2100 if(this.hovered) { // if there is a hovered element
2101 if(this.hovered.id == undefined) { // if this is an edge
2102 var hn = this.hovered;
2103 var geom = this.etypes[hn.getData('type')];
2104 var contains = false;
2106 // find alpha and total
2107 var list = this.viz.graph.edges[hn.nodeFrom.id][hn.nodeTo.id];
2108 if (list != undefined || list.length != 0) {
2109 var total = list.length;
2110 for(var idj = 0; idj < total; idj++) {
2113 // check if we need to skip this dup edge
2114 // NOTE: is this even needed, if getEdge() already fulfills this action?
2116 for(var d in this.viz.graph.dup) {
2117 var ee = this.viz.graph.dup[d];
2118 if(e.nodeTo.id == ee.nodeTo.id && e.nodeFrom.id == ee.nodeFrom.id) {
2119 if(e.portTo == ee.portTo && e.portFrom == ee.portFrom) {
2125 if(skip) continue; // skip this edge if it is a dup
2127 var alpha = parseInt(idj,10);
2128 var start = (0.5-(total/2));
2131 if(list[idj].portFrom == hn.portFrom
2132 && list[idj].portTo == hn.portTo) {
2133 contains = geom.contains.call(this.fx, hn, alpha, event.getPos(), this.canvas);
2140 this.config.onMouseEnter(this.hovered, event, evt);
2143 this.config.onMouseLeave(hn, event, evt);
2144 this.hovered = false;
2146 } else { // if this is a node
2147 var hn = this.hovered;
2148 var geom = hn.nodeFrom? this.etypes[hn.getData('type')] : this.ntypes[hn.getData('type')];
2149 var contains = geom && geom.contains
2150 && geom.contains.call(this.fx, hn, event.getPos());
2152 this.config.onMouseMove(hn, event, evt);
2155 this.config.onMouseLeave(hn, event, evt);
2156 this.hovered = false;
2161 if(this.hovered = event.getNode()) {
2162 this.config.onMouseEnter(this.hovered, event, evt);
2163 } else if (this.hovered = event.getEdge()) { // event for edges
2164 this.config.onMouseEnter(this.hovered, event, evt);
2166 this.config.onMouseMove(false, event, evt);
2171 onMouseWheel: function(e, win, delta) {
2172 this.config.onMouseWheel(delta, $.event.get(e, win));
2175 onMouseDown: function(e, win, event) {
2176 var evt = $.event.get(e, win), label;
2178 if(label = this.isLabel(e, win)) {
2179 this.pressed = this.viz.graph.getNode(label.id);
2182 this.pressed = event.getNode() || (this.config.enableForEdges && event.getEdge());
2184 this.pressed && this.config.onDragStart(this.pressed, event, evt);
2187 onTouchStart: function(e, win, event) {
2188 var evt = $.event.get(e, win), label;
2189 if(this.dom && (label = this.isLabel(e, win))) {
2190 this.touched = this.viz.graph.getNode(label.id);
2192 this.touched = event.getNode() || (this.config.enableForEdges && event.getEdge());
2194 this.touched && this.config.onTouchStart(this.touched, event, evt);
2197 onTouchMove: function(e, win, event) {
2198 var evt = $.event.get(e, win);
2200 this.touchMoved = true;
2201 this.config.onTouchMove(this.touched, event, evt);
2205 onTouchEnd: function(e, win, event) {
2206 var evt = $.event.get(e, win);
2208 if(this.touchMoved) {
2209 this.config.onTouchEnd(this.touched, event, evt);
2211 this.config.onTouchCancel(this.touched, event, evt);
2213 this.touched = this.touchMoved = false;
2221 A class containing tip related functions. This class is used internally.
2225 <ST>, <Sunburst>, <Hypertree>, <RGraph>, <TM>, <ForceDirected>, <Icicle>
2232 MultiExtras.Classes.Tips = new Class({
2233 Implements: [MultiExtrasInitializer, MultiEventsInterface],
2235 initializePost: function() {
2238 var tip = $('_tooltip') || document.createElement('div');
2239 tip.id = '_tooltip';
2240 tip.className = 'tip';
2241 $.extend(tip.style, {
2242 position: 'absolute',
2246 document.body.appendChild(tip);
2252 setAsProperty: $.lambda(true),
2254 onMouseOut: function(e, win) {
2256 var evt = $.event.get(e, win);
2257 if(this.dom && this.isLabel(e, win, true)) {
2262 var rt = e.relatedTarget,
2263 canvasWidget = this.canvas.getElement();
2264 while(rt && rt.parentNode) {
2265 if(canvasWidget == rt.parentNode) return;
2271 onMouseOver: function(e, win) {
2274 if(this.dom && (label = this.isLabel(e, win, false))) {
2275 this.node = this.viz.graph.getNode(label.id);
2276 this.config.onShow(this.tip, this.node, label);
2280 onMouseMove: function(e, win, opt) {
2281 if(this.dom && this.isLabel(e, win)) {
2282 this.setTooltipPosition($.event.getPos(e, win));
2285 var node = opt.getNode();
2286 var edge = opt.getEdge();
2287 if(!node && !edge) { // additionally for edge
2291 /*if(this.config.force || !this.node || this.node.id != node.id) {
2293 this.config.onShow(this.tip, node, opt.getContains());
2295 if (node != false) {
2296 if (this.config.force || !this.node || this.node.id != node.id) {
2299 this.config.onShow(this.tip, node, opt.getContains());
2301 } else if (edge != false) {
2304 this.config.onShow(this.tip, edge, opt.getContains());
2306 this.setTooltipPosition($.event.getPos(e, win));
2310 setTooltipPosition: function(pos) {
2315 //get viewport dimensions
2316 var elem = document.compatMode === "CSS1Compat" && document.documentElement ||
2318 document.documentElement;
2320 'width': elem.clientWidth,
2321 'height': elem.clientHeight,
2322 'x': window.pageXOffset ||
2323 document.documentElement && document.documentElement.scrollLeft ||
2324 document.body && document.body.scrollLeft ||
2326 'y': window.pageYOffset ||
2327 document.documentElement && document.documentElement.scrollTop ||
2328 document.body && document.body.scrollTop ||
2331 //get tooltip dimensions
2333 'width': tip.offsetWidth,
2334 'height': tip.offsetHeight
2336 //set tooltip position
2337 var x = cont.offsetX, y = cont.offsetY;
2338 style.top = ((pos.y + obj.height + y > view.height + view.y)?
2339 (pos.y - obj.height - y) : pos.y + y) + 'px';
2340 style.left = ((pos.x + obj.width + x > view.width + view.x)?
2341 (pos.x - obj.width - x) : pos.x + x) + 'px';
2344 hide: function(triggerCallback) {
2345 this.tip.style.display = 'none';
2346 triggerCallback && this.config.onHide();
2353 Change node styles when clicking or hovering a node. This class is used internally.
2357 <ST>, <Sunburst>, <Hypertree>, <RGraph>, <TM>, <ForceDirected>, <Icicle>
2361 <Options.NodeStyles>
2363 MultiExtras.Classes.NodeStyles = new Class({
2364 Implements: [MultiExtrasInitializer, MultiEventsInterface],
2366 initializePost: function() {
2367 this.fx = this.viz.fx;
2368 this.types = this.viz.fx.nodeTypes;
2369 this.nStyles = this.config;
2370 this.nodeStylesOnHover = this.nStyles.stylesHover;
2371 this.nodeStylesOnClick = this.nStyles.stylesClick;
2372 this.hoveredNode = false;
2373 this.fx.nodeFxAnimation = new Animation();
2379 onMouseOut: function(e, win) {
2380 this.down = this.move = false;
2381 if(!this.hoveredNode) return;
2383 if(this.dom && this.isLabel(e, win, true)) {
2384 this.toggleStylesOnHover(this.hoveredNode, false);
2387 var rt = e.relatedTarget,
2388 canvasWidget = this.canvas.getElement();
2389 while(rt && rt.parentNode) {
2390 if(canvasWidget == rt.parentNode) return;
2393 this.toggleStylesOnHover(this.hoveredNode, false);
2394 this.hoveredNode = false;
2397 onMouseOver: function(e, win) {
2400 if(this.dom && (label = this.isLabel(e, win, true))) {
2401 var node = this.viz.graph.getNode(label.id);
2402 if(node.selected) return;
2403 this.hoveredNode = node;
2404 this.toggleStylesOnHover(this.hoveredNode, true);
2408 onMouseDown: function(e, win, event, isRightClick) {
2409 if(isRightClick) return;
2411 if(this.dom && (label = this.isLabel(e, win))) {
2412 this.down = this.viz.graph.getNode(label.id);
2413 } else if(!this.dom) {
2414 this.down = event.getNode();
2419 onMouseUp: function(e, win, event, isRightClick) {
2420 if(isRightClick) return;
2422 this.onClick(event.getNode());
2424 this.down = this.move = false;
2427 getRestoredStyles: function(node, type) {
2428 var restoredStyles = {},
2429 nStyles = this['nodeStylesOn' + type];
2430 for(var prop in nStyles) {
2431 restoredStyles[prop] = node.styles['$' + prop];
2433 return restoredStyles;
2436 toggleStylesOnHover: function(node, set) {
2437 if(this.nodeStylesOnHover) {
2438 this.toggleStylesOn('Hover', node, set);
2442 toggleStylesOnClick: function(node, set) {
2443 if(this.nodeStylesOnClick) {
2444 this.toggleStylesOn('Click', node, set);
2448 toggleStylesOn: function(type, node, set) {
2450 var nStyles = this.nStyles;
2454 node.styles = $.merge(node.data, {});
2456 for(var s in this['nodeStylesOn' + type]) {
2458 if(!($s in node.styles)) {
2459 node.styles[$s] = node.getData(s);
2462 viz.fx.nodeFx($.extend({
2465 'properties': that['nodeStylesOn' + type]
2467 transition: Trans.Quart.easeOut,
2472 var restoredStyles = this.getRestoredStyles(node, type);
2473 viz.fx.nodeFx($.extend({
2476 'properties': restoredStyles
2478 transition: Trans.Quart.easeOut,
2485 onClick: function(node) {
2487 var nStyles = this.nodeStylesOnClick;
2488 if(!nStyles) return;
2489 //if the node is selected then unselect it
2491 this.toggleStylesOnClick(node, false);
2492 delete node.selected;
2494 //unselect all selected nodes...
2495 this.viz.graph.eachNode(function(n) {
2497 for(var s in nStyles) {
2498 n.setData(s, n.styles['$' + s], 'end');
2503 //select clicked node
2504 this.toggleStylesOnClick(node, true);
2505 node.selected = true;
2506 delete node.hovered;
2507 this.hoveredNode = false;
2511 onMouseMove: function(e, win, event) {
2512 //if mouse button is down and moving set move=true
2513 if(this.down) this.move = true;
2514 //already handled by mouseover/out
2515 if(this.dom && this.isLabel(e, win)) return;
2516 var nStyles = this.nodeStylesOnHover;
2517 if(!nStyles) return;
2520 if(this.hoveredNode) {
2521 var geom = this.types[this.hoveredNode.getData('type')];
2522 var contains = geom && geom.contains && geom.contains.call(this.fx,
2523 this.hoveredNode, event.getPos());
2524 if(contains) return;
2526 var node = event.getNode();
2527 //if no node is being hovered then just exit
2528 if(!this.hoveredNode && !node) return;
2529 //if the node is hovered then exit
2530 if(node.hovered) return;
2531 //select hovered node
2532 if(node && !node.selected) {
2533 //check if an animation is running and exit it
2534 this.fx.nodeFxAnimation.stopTimer();
2535 //unselect all hovered nodes...
2536 this.viz.graph.eachNode(function(n) {
2537 if(n.hovered && !n.selected) {
2538 for(var s in nStyles) {
2539 n.setData(s, n.styles['$' + s], 'end');
2544 //select hovered node
2545 node.hovered = true;
2546 this.hoveredNode = node;
2547 this.toggleStylesOnHover(node, true);
2548 } else if(this.hoveredNode && !this.hoveredNode.selected) {
2549 //check if an animation is running and exit it
2550 this.fx.nodeFxAnimation.stopTimer();
2551 //unselect hovered node
2552 this.toggleStylesOnHover(this.hoveredNode, false);
2553 delete this.hoveredNode.hovered;
2554 this.hoveredNode = false;
2560 MultiExtras.Classes.Navigation = new Class({
2561 Implements: [MultiExtrasInitializer, MultiEventsInterface],
2563 initializePost: function() {
2565 this.pressed = false;
2568 onMouseWheel: function(e, win, scroll) {
2569 if(!this.config.zooming) return;
2570 $.event.stop($.event.get(e, win));
2571 var val = this.config.zooming / 1000,
2572 ans = 1 + scroll * val;
2573 this.canvas.scale(ans, ans);
2576 onMouseDown: function(e, win, eventInfo) {
2577 if(!this.config.panning) return;
2578 e.preventDefault ? e.preventDefault() : e.returnValue = false;
2579 $.addClass(this.canvas.getElement(), 'grabbing');
2580 if(this.config.panning == 'avoid nodes' && (this.dom? this.isLabel(e, win) : eventInfo.getNode())) return;
2581 this.pressed = true;
2582 this.pos = eventInfo.getPos();
2583 var canvas = this.canvas,
2584 ox = canvas.translateOffsetX,
2585 oy = canvas.translateOffsetY,
2586 sx = canvas.scaleOffsetX,
2587 sy = canvas.scaleOffsetY;
2594 onMouseMove: function(e, win, eventInfo) {
2595 if(!this.config.panning) return;
2596 if(!this.pressed) return;
2597 if(this.config.panning == 'avoid nodes' && (this.dom? this.isLabel(e, win) : eventInfo.getNode())) return;
2598 var thispos = this.pos,
2599 currentPos = eventInfo.getPos(),
2600 canvas = this.canvas,
2601 ox = canvas.translateOffsetX,
2602 oy = canvas.translateOffsetY,
2603 sx = canvas.scaleOffsetX,
2604 sy = canvas.scaleOffsetY;
2609 var x = currentPos.x - thispos.x,
2610 y = currentPos.y - thispos.y;
2611 this.pos = currentPos;
2612 this.canvas.translate(x * 1/sx, y * 1/sy);
2615 onMouseUp: function(e, win, eventInfo, isRightClick) {
2616 if(!this.config.panning) return;
2617 $.removeClass(this.canvas.getElement(), 'grabbing');
2618 this.pressed = false;
2631 A canvas widget used by all visualizations. The canvas object can be accessed by doing *viz.canvas*. If you want to
2632 know more about <Canvas> options take a look at <Options.Canvas>.
2634 A canvas widget is a set of DOM elements that wrap the native canvas DOM Element providing a consistent API and behavior
2635 across all browsers. It can also include Elements to add DOM (SVG or HTML) label support to all visualizations.
2639 Suppose we have this HTML
2642 <div id="infovis"></div>
2645 Now we create a new Visualization
2648 var viz = new $jit.Viz({
2649 //Where to inject the canvas. Any div container will do.
2650 'injectInto':'infovis',
2651 //width and height for canvas.
2652 //Default's to the container offsetWidth and Height.
2658 The generated HTML will look like this
2662 <div id="infovis-canvaswidget" style="position:relative;">
2663 <canvas id="infovis-canvas" width=900 height=500
2664 style="position:absolute; top:0; left:0; width:900px; height:500px;" />
2665 <div id="infovis-label"
2666 style="overflow:visible; position:absolute; top:0; left:0; width:900px; height:0px">
2672 As you can see, the generated HTML consists of a canvas DOM Element of id *infovis-canvas* and a div label container
2673 of id *infovis-label*, wrapped in a main div container of id *infovis-canvaswidget*.
2678 //check for native canvas support
2679 var canvasType = typeof HTMLCanvasElement,
2680 supportsCanvas = (canvasType == 'object' || canvasType == 'function');
2681 //create element function
2682 function $E(tag, props) {
2683 var elem = document.createElement(tag);
2684 for(var p in props) {
2685 if(typeof props[p] == "object") {
2686 $.extend(elem[p], props[p]);
2691 if (tag == "canvas" && !supportsCanvas && G_vmlCanvasManager) {
2692 elem = G_vmlCanvasManager.initElement(document.body.appendChild(elem));
2696 //canvas widget which we will call just Canvas
2697 $jit.Canvas = Canvas = new Class({
2701 labelContainer: false,
2702 translateOffsetX: 0,
2703 translateOffsetY: 0,
2707 initialize: function(viz, opt) {
2709 this.opt = this.config = opt;
2710 var id = $.type(opt.injectInto) == 'string'?
2711 opt.injectInto:opt.injectInto.id,
2713 idLabel = id + "-label",
2715 width = opt.width || wrapper.offsetWidth,
2716 height = opt.height || wrapper.offsetHeight;
2719 var canvasOptions = {
2724 //create main wrapper
2725 this.element = $E('div', {
2726 'id': id + '-canvaswidget',
2728 'position': 'relative',
2729 'width': width + 'px',
2730 'height': height + 'px'
2733 //create label container
2734 this.labelContainer = this.createLabelContainer(opt.Label.type,
2735 idLabel, canvasOptions);
2736 //create primary canvas
2737 this.canvases.push(new Canvas.Base[type]({
2738 config: $.extend({idSuffix: '-canvas'}, canvasOptions),
2739 plot: function(base) {
2742 resize: function() {
2746 //create secondary canvas
2747 var back = opt.background;
2749 var backCanvas = new Canvas.Background[back.type](viz, $.extend(back, canvasOptions));
2750 this.canvases.push(new Canvas.Base[type](backCanvas));
2753 var len = this.canvases.length;
2755 this.element.appendChild(this.canvases[len].canvas);
2757 this.canvases[len].plot();
2760 this.element.appendChild(this.labelContainer);
2761 wrapper.appendChild(this.element);
2762 //Update canvas position when the page is scrolled.
2763 var timer = null, that = this;
2764 $.addEvent(window, 'scroll', function() {
2765 clearTimeout(timer);
2766 timer = setTimeout(function() {
2767 that.getPos(true); //update canvas position
2774 Returns the main canvas context object
2779 var ctx = canvas.getCtx();
2780 //Now I can use the native canvas context
2781 //and for example change some canvas styles
2782 ctx.globalAlpha = 1;
2785 getCtx: function(i) {
2786 return this.canvases[i || 0].getCtx();
2791 Returns the current Configuration for this Canvas Widget.
2796 var config = canvas.getConfig();
2799 getConfig: function() {
2805 Returns the main Canvas DOM wrapper
2810 var wrapper = canvas.getElement();
2811 //Returns <div id="infovis-canvaswidget" ... >...</div> as element
2814 getElement: function() {
2815 return this.element;
2820 Returns canvas dimensions.
2824 An object with *width* and *height* properties.
2828 canvas.getSize(); //returns { width: 900, height: 500 }
2831 getSize: function(i) {
2832 return this.canvases[i || 0].getSize();
2841 width - New canvas width.
2842 height - New canvas height.
2847 canvas.resize(width, height);
2851 resize: function(width, height) {
2853 this.translateOffsetX = this.translateOffsetY = 0;
2854 this.scaleOffsetX = this.scaleOffsetY = 1;
2855 for(var i=0, l=this.canvases.length; i<l; i++) {
2856 this.canvases[i].resize(width, height);
2858 var style = this.element.style;
2859 style.width = width + 'px';
2860 style.height = height + 'px';
2861 if(this.labelContainer)
2862 this.labelContainer.style.width = width + 'px';
2867 Applies a translation to the canvas.
2871 x - (number) x offset.
2872 y - (number) y offset.
2873 disablePlot - (boolean) Default's *false*. Set this to *true* if you don't want to refresh the visualization.
2878 canvas.translate(30, 30);
2882 translate: function(x, y, disablePlot) {
2883 this.translateOffsetX += x*this.scaleOffsetX;
2884 this.translateOffsetY += y*this.scaleOffsetY;
2885 for(var i=0, l=this.canvases.length; i<l; i++) {
2886 this.canvases[i].translate(x, y, disablePlot);
2896 x - (number) scale value.
2897 y - (number) scale value.
2898 disablePlot - (boolean) Default's *false*. Set this to *true* if you don't want to refresh the visualization.
2903 canvas.scale(0.5, 0.5);
2907 scale: function(x, y, disablePlot) {
2908 var px = this.scaleOffsetX * x,
2909 py = this.scaleOffsetY * y;
2910 var dx = this.translateOffsetX * (x -1) / px,
2911 dy = this.translateOffsetY * (y -1) / py;
2912 this.scaleOffsetX = px;
2913 this.scaleOffsetY = py;
2914 for(var i=0, l=this.canvases.length; i<l; i++) {
2915 this.canvases[i].scale(x, y, true);
2917 this.translate(dx, dy, false);
2922 Returns canvas zooming factors. *1* means initial zoom.
2926 An object with *x* and *y* properties.
2928 getZoom: function() {
2929 return new Complex(this.scaleOffsetX, this.scaleOffsetY);
2934 Sets the zoom to given factors. *1* means initial zoom.
2938 x - (number) zooming factor
2939 y - (number) zooming factor
2940 disablePlot - (boolean) Default's *false*. Set this to *true* if you don't want to refresh the visualization.
2944 canvas.setZoom(2, 2); //sets 2x zoom
2947 setZoom: function(x, y, disablePlot) {
2948 var cur = this.getZoom(),
2951 this.scale(px, py, disablePlot);
2956 Returns the canvas position as an *x, y* object.
2960 force - (boolean) Default's *false*. Set this to *true* if you want to recalculate the position without using any cache information.
2964 An object with *x* and *y* properties.
2968 canvas.getPos(true); //returns { x: 900, y: 500 }
2971 getPos: function(force){
2972 if(force || !this.pos) {
2973 return this.pos = $.getPos(this.getElement());
2983 this.canvases[i||0].clear();
2986 path: function(type, action){
2987 var ctx = this.canvases[0].getCtx();
2994 createLabelContainer: function(type, idLabel, dim) {
2995 var NS = 'http://www.w3.org/2000/svg';
2996 if(type == 'HTML' || type == 'Native') {
3000 'overflow': 'visible',
3001 'position': 'absolute',
3004 'width': dim.width + 'px',
3008 } else if(type == 'SVG') {
3009 var svgContainer = document.createElementNS(NS, 'svg:svg');
3010 svgContainer.setAttribute("width", dim.width);
3011 svgContainer.setAttribute('height', dim.height);
3012 var style = svgContainer.style;
3013 style.position = 'absolute';
3014 style.left = style.top = '0px';
3015 var labelContainer = document.createElementNS(NS, 'svg:g');
3016 labelContainer.setAttribute('width', dim.width);
3017 labelContainer.setAttribute('height', dim.height);
3018 labelContainer.setAttribute('x', 0);
3019 labelContainer.setAttribute('y', 0);
3020 labelContainer.setAttribute('id', idLabel);
3021 svgContainer.appendChild(labelContainer);
3022 return svgContainer;
3026 //base canvas wrapper
3028 Canvas.Base['2D'] = new Class({
3029 translateOffsetX: 0,
3030 translateOffsetY: 0,
3034 initialize: function(viz) {
3036 this.opt = viz.config;
3038 this.createCanvas();
3039 this.translateToCenter();
3041 createCanvas: function() {
3044 height = opt.height;
3045 this.canvas = $E('canvas', {
3046 'id': opt.injectInto + opt.idSuffix,
3050 'position': 'absolute',
3053 'width': width + 'px',
3054 'height': height + 'px'
3058 getCtx: function() {
3060 return this.ctx = this.canvas.getContext('2d');
3063 getSize: function() {
3064 if(this.size) return this.size;
3065 var canvas = this.canvas;
3066 return this.size = {
3067 width: canvas.width,
3068 height: canvas.height
3071 translateToCenter: function(ps) {
3072 var size = this.getSize(),
3073 width = ps? (size.width - ps.width - this.translateOffsetX*2) : size.width;
3074 height = ps? (size.height - ps.height - this.translateOffsetY*2) : size.height;
3075 var ctx = this.getCtx();
3076 ps && ctx.scale(1/this.scaleOffsetX, 1/this.scaleOffsetY);
3077 ctx.translate(width/2, height/2);
3079 resize: function(width, height) {
3080 var size = this.getSize(),
3081 canvas = this.canvas,
3082 styles = canvas.style;
3084 canvas.width = width;
3085 canvas.height = height;
3086 styles.width = width + "px";
3087 styles.height = height + "px";
3088 //small ExCanvas fix
3089 if(!supportsCanvas) {
3090 this.translateToCenter(size);
3092 this.translateToCenter();
3094 this.translateOffsetX =
3095 this.translateOffsetY = 0;
3097 this.scaleOffsetY = 1;
3099 this.viz.resize(width, height, this);
3101 translate: function(x, y, disablePlot) {
3102 var sx = this.scaleOffsetX,
3103 sy = this.scaleOffsetY;
3104 this.translateOffsetX += x*sx;
3105 this.translateOffsetY += y*sy;
3106 this.getCtx().translate(x, y);
3107 !disablePlot && this.plot();
3109 scale: function(x, y, disablePlot) {
3110 this.scaleOffsetX *= x;
3111 this.scaleOffsetY *= y;
3112 this.getCtx().scale(x, y);
3113 !disablePlot && this.plot();
3116 var size = this.getSize(),
3117 ox = this.translateOffsetX,
3118 oy = this.translateOffsetY,
3119 sx = this.scaleOffsetX,
3120 sy = this.scaleOffsetY;
3121 this.getCtx().clearRect((-size.width / 2 - ox) * 1/sx,
3122 (-size.height / 2 - oy) * 1/sy,
3123 size.width * 1/sx, size.height * 1/sy);
3127 this.viz.plot(this);
3130 //background canvases
3131 //TODO(nico): document this!
3132 Canvas.Background = {};
3133 Canvas.Background.Circles = new Class({
3134 initialize: function(viz, options) {
3136 this.config = $.merge({
3137 idSuffix: '-bkcanvas',
3144 resize: function(width, height, base) {
3147 plot: function(base) {
3148 var canvas = base.canvas,
3149 ctx = base.getCtx(),
3151 styles = conf.CanvasStyles;
3153 for(var s in styles) ctx[s] = styles[s];
3154 var n = conf.numberOfCircles,
3155 rho = conf.levelDistance;
3156 for(var i=1; i<=n; i++) {
3158 ctx.arc(0, 0, rho * i, 0, 2 * Math.PI, false);
3162 //TODO(nico): print labels too!
3171 * Defines the <Polar> class.
3175 * The <Polar> class, just like the <Complex> class, is used by the <Hypertree>, <ST> and <RGraph> as a 2D point representation.
3179 * <http://en.wikipedia.org/wiki/Polar_coordinates>
3186 A multi purpose polar representation.
3190 The <Polar> class, just like the <Complex> class, is used by the <Hypertree>, <ST> and <RGraph> as a 2D point representation.
3194 <http://en.wikipedia.org/wiki/Polar_coordinates>
3202 var Polar = function(theta, rho) {
3203 this.theta = theta || 0;
3204 this.rho = rho || 0;
3213 Returns a complex number.
3217 simple - _optional_ If *true*, this method will return only an object holding x and y properties and not a <Complex> instance. Default's *false*.
3223 getc: function(simple) {
3224 return this.toComplex(simple);
3230 Returns a <Polar> representation.
3234 A variable in polar coordinates.
3248 v - A <Complex> or <Polar> instance.
3253 this.theta = v.theta; this.rho = v.rho;
3259 Sets a <Complex> number.
3263 x - A <Complex> number real part.
3264 y - A <Complex> number imaginary part.
3267 setc: function(x, y) {
3268 this.rho = Math.sqrt(x * x + y * y);
3269 this.theta = Math.atan2(y, x);
3270 if(this.theta < 0) this.theta += Math.PI * 2;
3276 Sets a polar number.
3280 theta - A <Polar> number angle property.
3281 rho - A <Polar> number rho property.
3284 setp: function(theta, rho) {
3292 Returns a copy of the current object.
3296 A copy of the real object.
3299 return new Polar(this.theta, this.rho);
3305 Translates from polar to cartesian coordinates and returns a new <Complex> instance.
3309 simple - _optional_ If *true* this method will only return an object with x and y properties (and not the whole <Complex> instance). Default's *false*.
3313 A new <Complex> instance.
3315 toComplex: function(simple) {
3316 var x = Math.cos(this.theta) * this.rho;
3317 var y = Math.sin(this.theta) * this.rho;
3318 if(simple) return { 'x': x, 'y': y};
3319 return new Complex(x, y);
3325 Adds two <Polar> instances.
3329 polar - A <Polar> number.
3333 A new Polar instance.
3335 add: function(polar) {
3336 return new Polar(this.theta + polar.theta, this.rho + polar.rho);
3342 Scales a polar norm.
3346 number - A scale factor.
3350 A new Polar instance.
3352 scale: function(number) {
3353 return new Polar(this.theta, this.rho * number);
3361 Returns *true* if the theta and rho properties are equal.
3365 c - A <Polar> number.
3369 *true* if the theta and rho parameters for these objects are equal. *false* otherwise.
3371 equals: function(c) {
3372 return this.theta == c.theta && this.rho == c.rho;
3378 Adds two <Polar> instances affecting the current object.
3382 polar - A <Polar> instance.
3388 $add: function(polar) {
3389 this.theta = this.theta + polar.theta; this.rho += polar.rho;
3396 Adds two <Polar> instances affecting the current object. The resulting theta angle is modulo 2pi.
3400 polar - A <Polar> instance.
3406 $madd: function(polar) {
3407 this.theta = (this.theta + polar.theta) % (Math.PI * 2); this.rho += polar.rho;
3415 Scales a polar instance affecting the object.
3419 number - A scaling factor.
3425 $scale: function(number) {
3433 Returns *true* if the number is zero.
3436 isZero: function () {
3437 var almostZero = 0.0001, abs = Math.abs;
3438 return abs(this.theta) < almostZero && abs(this.rho) < almostZero;
3444 Calculates a polar interpolation between two points at a given delta moment.
3448 elem - A <Polar> instance.
3449 delta - A delta factor ranging [0, 1].
3453 A new <Polar> instance representing an interpolation between _this_ and _elem_
3455 interpolate: function(elem, delta) {
3456 var pi = Math.PI, pi2 = pi * 2;
3457 var ch = function(t) {
3458 var a = (t < 0)? (t % pi2) + pi2 : t % pi2;
3461 var tt = this.theta, et = elem.theta;
3462 var sum, diff = Math.abs(tt - et);
3465 sum = ch((et + ((tt - pi2) - et) * delta)) ;
3467 sum = ch((et - pi2 + (tt - (et)) * delta));
3469 } else if(diff >= pi) {
3471 sum = ch((et + ((tt - pi2) - et) * delta)) ;
3473 sum = ch((et - pi2 + (tt - (et - pi2)) * delta));
3476 sum = ch((et + (tt - et) * delta)) ;
3478 var r = (this.rho - elem.rho) * delta + elem.rho;
3487 var $P = function(a, b) { return new Polar(a, b); };
3489 Polar.KER = $P(0, 0);
3496 * Defines the <Complex> class.
3500 * The <Complex> class, just like the <Polar> class, is used by the <Hypertree>, <ST> and <RGraph> as a 2D point representation.
3504 * <http://en.wikipedia.org/wiki/Complex_number>
3511 A multi-purpose Complex Class with common methods.
3515 The <Complex> class, just like the <Polar> class, is used by the <Hypertree>, <ST> and <RGraph> as a 2D point representation.
3519 <http://en.wikipedia.org/wiki/Complex_number>
3523 x - _optional_ A Complex number real part.
3524 y - _optional_ A Complex number imaginary part.
3528 var Complex = function(x, y) {
3533 $jit.Complex = Complex;
3535 Complex.prototype = {
3539 Returns a complex number.
3552 Returns a <Polar> representation of this number.
3556 simple - _optional_ If *true*, this method will return only an object holding theta and rho properties and not a <Polar> instance. Default's *false*.
3560 A variable in <Polar> coordinates.
3562 getp: function(simple) {
3563 return this.toPolar(simple);
3574 c - A <Complex> or <Polar> instance.
3586 Sets a complex number.
3590 x - A <Complex> number Real part.
3591 y - A <Complex> number Imaginary part.
3594 setc: function(x, y) {
3602 Sets a polar number.
3606 theta - A <Polar> number theta property.
3607 rho - A <Polar> number rho property.
3610 setp: function(theta, rho) {
3611 this.x = Math.cos(theta) * rho;
3612 this.y = Math.sin(theta) * rho;
3618 Returns a copy of the current object.
3622 A copy of the real object.
3625 return new Complex(this.x, this.y);
3631 Transforms cartesian to polar coordinates.
3635 simple - _optional_ If *true* this method will only return an object with theta and rho properties (and not the whole <Polar> instance). Default's *false*.
3639 A new <Polar> instance.
3642 toPolar: function(simple) {
3643 var rho = this.norm();
3644 var atan = Math.atan2(this.y, this.x);
3645 if(atan < 0) atan += Math.PI * 2;
3646 if(simple) return { 'theta': atan, 'rho': rho };
3647 return new Polar(atan, rho);
3652 Calculates a <Complex> number norm.
3656 A real number representing the complex norm.
3659 return Math.sqrt(this.squaredNorm());
3665 Calculates a <Complex> number squared norm.
3669 A real number representing the complex squared norm.
3671 squaredNorm: function () {
3672 return this.x*this.x + this.y*this.y;
3678 Returns the result of adding two complex numbers.
3680 Does not alter the original object.
3684 pos - A <Complex> instance.
3688 The result of adding two complex numbers.
3690 add: function(pos) {
3691 return new Complex(this.x + pos.x, this.y + pos.y);
3697 Returns the result of multiplying two <Complex> numbers.
3699 Does not alter the original object.
3703 pos - A <Complex> instance.
3707 The result of multiplying two complex numbers.
3709 prod: function(pos) {
3710 return new Complex(this.x*pos.x - this.y*pos.y, this.y*pos.x + this.x*pos.y);
3716 Returns the conjugate of this <Complex> number.
3718 Does not alter the original object.
3722 The conjugate of this <Complex> number.
3724 conjugate: function() {
3725 return new Complex(this.x, -this.y);
3732 Returns the result of scaling a <Complex> instance.
3734 Does not alter the original object.
3738 factor - A scale factor.
3742 The result of scaling this complex to a factor.
3744 scale: function(factor) {
3745 return new Complex(this.x * factor, this.y * factor);
3753 Returns *true* if both real and imaginary parts are equal.
3757 c - A <Complex> instance.
3761 A boolean instance indicating if both <Complex> numbers are equal.
3763 equals: function(c) {
3764 return this.x == c.x && this.y == c.y;
3770 Returns the result of adding two <Complex> numbers.
3772 Alters the original object.
3776 pos - A <Complex> instance.
3780 The result of adding two complex numbers.
3782 $add: function(pos) {
3783 this.x += pos.x; this.y += pos.y;
3790 Returns the result of multiplying two <Complex> numbers.
3792 Alters the original object.
3796 pos - A <Complex> instance.
3800 The result of multiplying two complex numbers.
3802 $prod:function(pos) {
3803 var x = this.x, y = this.y;
3804 this.x = x*pos.x - y*pos.y;
3805 this.y = y*pos.x + x*pos.y;
3812 Returns the conjugate for this <Complex>.
3814 Alters the original object.
3818 The conjugate for this complex.
3820 $conjugate: function() {
3828 Returns the result of scaling a <Complex> instance.
3830 Alters the original object.
3834 factor - A scale factor.
3838 The result of scaling this complex to a factor.
3840 $scale: function(factor) {
3841 this.x *= factor; this.y *= factor;
3848 Returns the division of two <Complex> numbers.
3850 Alters the original object.
3854 pos - A <Complex> number.
3858 The result of scaling this complex to a factor.
3860 $div: function(pos) {
3861 var x = this.x, y = this.y;
3862 var sq = pos.squaredNorm();
3863 this.x = x * pos.x + y * pos.y; this.y = y * pos.x - x * pos.y;
3864 return this.$scale(1 / sq);
3870 Returns *true* if the number is zero.
3873 isZero: function () {
3874 var almostZero = 0.0001, abs = Math.abs;
3875 return abs(this.x) < almostZero && abs(this.y) < almostZero;
3879 var $C = function(a, b) { return new Complex(a, b); };
3881 Complex.KER = $C(0, 0);
3882 Complex.IM = $C(0, 1);
3894 A Graph Class that provides useful manipulation functions. You can find more manipulation methods in the <Graph.Util> object.
3896 An instance of this class can be accessed by using the *graph* parameter of any tree or graph visualization.
3901 //create new visualization
3902 var viz = new $jit.Viz(options);
3906 viz.graph; //<Graph> instance
3911 The following <Graph.Util> methods are implemented in <Graph>
3913 - <Graph.Util.getNode>
3914 - <Graph.Util.eachNode>
3915 - <Graph.Util.computeLevels>
3916 - <Graph.Util.eachBFS>
3917 - <Graph.Util.clean>
3918 - <Graph.Util.getClosestNodeToPos>
3919 - <Graph.Util.getClosestNodeToOrigin>
3923 $jit.MultiGraph = new Class({
3925 initialize: function(opt, Node, Edge, Label) {
3926 var innerOptions = {
3933 this.opt = $.merge(innerOptions, opt || {});
3936 this.dup = []; // duplicate (bidirectional) edges
3938 //add nodeList methods
3941 for(var p in Accessors) {
3942 that.nodeList[p] = (function(p) {
3944 var args = Array.prototype.slice.call(arguments);
3945 that.eachNode(function(n) {
3946 n[p].apply(n, args);
3957 Returns a <Graph.Node> by *id*.
3961 id - (string) A <Graph.Node> id.
3966 var node = graph.getNode('nodeId');
3969 getNode: function(id) {
3970 if(this.hasNode(id)) return this.nodes[id];
3977 An alias for <Graph.Util.getNode>. Returns a node by *id*.
3981 id - (string) A <Graph.Node> id.
3986 var node = graph.get('nodeId');
3990 return this.getNode(id);
3996 Returns a <Graph.Node> by *name*.
4000 name - (string) A <Graph.Node> name.
4005 var node = graph.getByName('someName');
4008 getByName: function(name) {
4009 for(var id in this.nodes) {
4010 var n = this.nodes[id];
4011 if(n.name == name) return n;
4017 Method: getAdjacence
4019 Returns a <Graph.Adjacence> object connecting nodes with ids *id* and *id2*.
4023 id - (string) A <Graph.Node> id.
4024 id2 - (string) A <Graph.Node> id.
4026 getAdjacence: function (id, id2) {
4027 if(id in this.edges) {
4028 return this.edges[id][id2];
4040 obj - An object with the properties described below
4042 id - (string) A node id
4043 name - (string) A node's name
4044 data - (object) A node's data hash
4050 addNode: function(obj) {
4051 if(!this.nodes[obj.id]) {
4052 var edges = this.edges[obj.id] = {};
4053 this.nodes[obj.id] = new MultiGraph.Node($.extend({
4056 'data': $.merge(obj.data || {}, {}),
4057 'adjacencies': edges
4064 return this.nodes[obj.id];
4068 Method: addAdjacence
4070 Connects nodes specified by *obj* and *obj2*. If not found, nodes are created.
4074 obj - (object) A <Graph.Node> object.
4075 obj2 - (object) Another <Graph.Node> object.
4076 data - (object) A data object. Used to store some extra information in the <Graph.Adjacence> object created.
4080 <Graph.Node>, <Graph.Adjacence>
4082 addAdjacence: function (obj, obj2, data) {
4083 if (obj.id == obj2.id) return null; // skip if node loops back to itself
4085 var portFrom = data["$nodeFromPort"];
4086 var portTo = data["$nodeToPort"];
4088 if(!this.hasNode(obj.id)) { this.addNode(obj); }
4089 if(!this.hasNode(obj2.id)) { this.addNode(obj2); }
4090 obj = this.nodes[obj.id]; obj2 = this.nodes[obj2.id];
4091 if(!obj.adjacentTo(obj2, portTo)) {
4092 var adjsObj = this.edges[obj.id] = this.edges[obj.id] || {};
4093 var adjsObj2 = this.edges[obj2.id] = this.edges[obj2.id] || {};
4095 adjsObj[obj2.id] = adjsObj[obj2.id] || [];
4096 adjsObj2[obj.id] = adjsObj2[obj.id] || [];
4098 var adj = new MultiGraph.Adjacence(obj, obj2, portFrom, portTo, data, this.Edge, this.Label);
4100 // if dup, then add to dup list
4101 for(var edge in adjsObj2[obj.id]) { // iterate through the adj for the other node
4102 var e = adjsObj2[obj.id][edge];
4103 if(e.nodeFrom.id == adj.nodeTo.id && e.nodeTo.id == adj.nodeFrom.id) {
4104 if(e.portFrom == adj.portTo && e.portTo == adj.portFrom) {
4105 this.dup.push(adj); // if the other node contains the edge, then add it to the dup list for later filtering
4110 // add this adjacency into the current edges object
4111 adjsObj[obj2.id].push(adj);
4112 if (adj.nodeFrom.data["$type"] != "swtch") {
4113 // if this is not a switch (e.g. host), then add it to the switch because it will never be added by itself
4114 adjsObj2[obj.id].push(adj);
4118 //adjsObj[obj2.id] = adjsObj2[obj.id] = new MultiGraph.Adjacence(obj, obj2, data["$nodeFromPort"], data["$nodeToPort"], data, this.Edge, this.Label);
4119 //return adjsObj[obj2.id];
4121 return this.edges[obj.id][obj2.id];
4127 Removes a <Graph.Node> matching the specified *id*.
4131 id - (string) A node's id.
4134 removeNode: function(id) {
4135 if(this.hasNode(id)) {
4136 delete this.nodes[id];
4137 var adjs = this.edges[id];
4138 for(var to in adjs) {
4139 delete this.edges[to][id];
4141 delete this.edges[id];
4146 Method: removeAdjacence
4148 Removes a <Graph.Adjacence> matching *id1* and *id2*.
4152 id1 - (string) A <Graph.Node> id.
4153 id2 - (string) A <Graph.Node> id.
4155 removeAdjacence: function(id1, id2) {
4156 delete this.edges[id1][id2];
4157 delete this.edges[id2][id1];
4163 Returns a boolean indicating if the node belongs to the <Graph> or not.
4167 id - (string) Node id.
4169 hasNode: function(id) {
4170 return id in this.nodes;
4179 empty: function() { this.nodes = {}; this.edges = {};}
4183 var MultiGraph = $jit.MultiGraph;
4188 Defines a set of methods for data, canvas and label styles manipulation implemented by <Graph.Node> and <Graph.Adjacence> instances.
4194 var getDataInternal = function(prefix, prop, type, force, prefixConfig) {
4196 type = type || 'current';
4197 prefix = "$" + (prefix ? prefix + "-" : "");
4199 if(type == 'current') {
4201 } else if(type == 'start') {
4202 data = this.startData;
4203 } else if(type == 'end') {
4204 data = this.endData;
4207 var dollar = prefix + prop;
4210 return data[dollar];
4213 if(!this.Config.overridable)
4214 return prefixConfig[prop] || 0;
4216 return (dollar in data) ?
4217 data[dollar] : ((dollar in this.data) ? this.data[dollar] : (prefixConfig[prop] || 0));
4220 var setDataInternal = function(prefix, prop, value, type) {
4221 type = type || 'current';
4222 prefix = '$' + (prefix ? prefix + '-' : '');
4226 if(type == 'current') {
4228 } else if(type == 'start') {
4229 data = this.startData;
4230 } else if(type == 'end') {
4231 data = this.endData;
4234 data[prefix + prop] = value;
4237 var removeDataInternal = function(prefix, properties) {
4238 prefix = '$' + (prefix ? prefix + '-' : '');
4240 $.each(properties, function(prop) {
4241 var pref = prefix + prop;
4242 delete that.data[pref];
4243 delete that.endData[pref];
4244 delete that.startData[pref];
4252 Returns the specified data value property.
4253 This is useful for querying special/reserved <Graph.Node> data properties
4254 (i.e dollar prefixed properties).
4258 prop - (string) The name of the property. The dollar sign is not needed. For
4259 example *getData(width)* will return *data.$width*.
4260 type - (string) The type of the data property queried. Default's "current". You can access *start* and *end*
4261 data properties also. These properties are used when making animations.
4262 force - (boolean) Whether to obtain the true value of the property (equivalent to
4263 *data.$prop*) or to check for *node.overridable = true* first.
4267 The value of the dollar prefixed property or the global Node/Edge property
4268 value if *overridable=false*
4272 node.getData('width'); //will return node.data.$width if Node.overridable=true;
4275 getData: function(prop, type, force) {
4276 return getDataInternal.call(this, "", prop, type, force, this.Config);
4283 Sets the current data property with some specific value.
4284 This method is only useful for reserved (dollar prefixed) properties.
4288 prop - (string) The name of the property. The dollar sign is not necessary. For
4289 example *setData(width)* will set *data.$width*.
4290 value - (mixed) The value to store.
4291 type - (string) The type of the data property to store. Default's "current" but
4292 can also be "start" or "end".
4297 node.setData('width', 30);
4300 If we were to make an animation of a node/edge width then we could do
4303 var node = viz.getNode('nodeId');
4304 //set start and end values
4305 node.setData('width', 10, 'start');
4306 node.setData('width', 30, 'end');
4307 //will animate nodes width property
4309 modes: ['node-property:width'],
4314 setData: function(prop, value, type) {
4315 setDataInternal.call(this, "", prop, value, type);
4321 Convenience method to set multiple data values at once.
4325 types - (array|string) A set of 'current', 'end' or 'start' values.
4326 obj - (object) A hash containing the names and values of the properties to be altered.
4330 node.setDataset(['current', 'end'], {
4332 'color': ['#fff', '#ccc']
4335 node.setDataset('end', {
4346 setDataset: function(types, obj) {
4347 types = $.splat(types);
4348 for(var attr in obj) {
4349 for(var i=0, val = $.splat(obj[attr]), l=types.length; i<l; i++) {
4350 this.setData(attr, val[i], types[i]);
4358 Remove data properties.
4362 One or more property names as arguments. The dollar sign is not needed.
4366 node.removeData('width'); //now the default width value is returned
4369 removeData: function() {
4370 removeDataInternal.call(this, "", Array.prototype.slice.call(arguments));
4374 Method: getCanvasStyle
4376 Returns the specified canvas style data value property. This is useful for
4377 querying special/reserved <Graph.Node> canvas style data properties (i.e.
4378 dollar prefixed properties that match with $canvas-<name of canvas style>).
4382 prop - (string) The name of the property. The dollar sign is not needed. For
4383 example *getCanvasStyle(shadowBlur)* will return *data[$canvas-shadowBlur]*.
4384 type - (string) The type of the data property queried. Default's *current*. You can access *start* and *end*
4385 data properties also.
4389 node.getCanvasStyle('shadowBlur');
4396 getCanvasStyle: function(prop, type, force) {
4397 return getDataInternal.call(
4398 this, 'canvas', prop, type, force, this.Config.CanvasStyles);
4402 Method: setCanvasStyle
4404 Sets the canvas style data property with some specific value.
4405 This method is only useful for reserved (dollar prefixed) properties.
4409 prop - (string) Name of the property. Can be any canvas property like 'shadowBlur', 'shadowColor', 'strokeStyle', etc.
4410 value - (mixed) The value to set to the property.
4411 type - (string) Default's *current*. Whether to set *start*, *current* or *end* type properties.
4416 node.setCanvasStyle('shadowBlur', 30);
4419 If we were to make an animation of a node/edge shadowBlur canvas style then we could do
4422 var node = viz.getNode('nodeId');
4423 //set start and end values
4424 node.setCanvasStyle('shadowBlur', 10, 'start');
4425 node.setCanvasStyle('shadowBlur', 30, 'end');
4426 //will animate nodes canvas style property for nodes
4428 modes: ['node-style:shadowBlur'],
4435 <Accessors.setData>.
4437 setCanvasStyle: function(prop, value, type) {
4438 setDataInternal.call(this, 'canvas', prop, value, type);
4442 Method: setCanvasStyles
4444 Convenience method to set multiple styles at once.
4448 types - (array|string) A set of 'current', 'end' or 'start' values.
4449 obj - (object) A hash containing the names and values of the properties to be altered.
4453 <Accessors.setDataset>.
4455 setCanvasStyles: function(types, obj) {
4456 types = $.splat(types);
4457 for(var attr in obj) {
4458 for(var i=0, val = $.splat(obj[attr]), l=types.length; i<l; i++) {
4459 this.setCanvasStyle(attr, val[i], types[i]);
4465 Method: removeCanvasStyle
4467 Remove canvas style properties from data.
4471 A variable number of canvas style strings.
4475 <Accessors.removeData>.
4477 removeCanvasStyle: function() {
4478 removeDataInternal.call(this, 'canvas', Array.prototype.slice.call(arguments));
4482 Method: getLabelData
4484 Returns the specified label data value property. This is useful for
4485 querying special/reserved <Graph.Node> label options (i.e.
4486 dollar prefixed properties that match with $label-<name of label style>).
4490 prop - (string) The name of the property. The dollar sign prefix is not needed. For
4491 example *getLabelData(size)* will return *data[$label-size]*.
4492 type - (string) The type of the data property queried. Default's *current*. You can access *start* and *end*
4493 data properties also.
4497 <Accessors.getData>.
4499 getLabelData: function(prop, type, force) {
4500 return getDataInternal.call(
4501 this, 'label', prop, type, force, this.Label);
4505 Method: setLabelData
4507 Sets the current label data with some specific value.
4508 This method is only useful for reserved (dollar prefixed) properties.
4512 prop - (string) Name of the property. Can be any canvas property like 'shadowBlur', 'shadowColor', 'strokeStyle', etc.
4513 value - (mixed) The value to set to the property.
4514 type - (string) Default's *current*. Whether to set *start*, *current* or *end* type properties.
4519 node.setLabelData('size', 30);
4522 If we were to make an animation of a node label size then we could do
4525 var node = viz.getNode('nodeId');
4526 //set start and end values
4527 node.setLabelData('size', 10, 'start');
4528 node.setLabelData('size', 30, 'end');
4529 //will animate nodes label size
4531 modes: ['label-property:size'],
4538 <Accessors.setData>.
4540 setLabelData: function(prop, value, type) {
4541 setDataInternal.call(this, 'label', prop, value, type);
4545 Method: setLabelDataset
4547 Convenience function to set multiple label data at once.
4551 types - (array|string) A set of 'current', 'end' or 'start' values.
4552 obj - (object) A hash containing the names and values of the properties to be altered.
4556 <Accessors.setDataset>.
4558 setLabelDataset: function(types, obj) {
4559 types = $.splat(types);
4560 for(var attr in obj) {
4561 for(var i=0, val = $.splat(obj[attr]), l=types.length; i<l; i++) {
4562 this.setLabelData(attr, val[i], types[i]);
4568 Method: removeLabelData
4570 Remove label properties from data.
4574 A variable number of label property strings.
4578 <Accessors.removeData>.
4580 removeLabelData: function() {
4581 removeDataInternal.call(this, 'label', Array.prototype.slice.call(arguments));
4593 <Accessors> methods.
4595 The following <Graph.Util> methods are implemented by <Graph.Node>
4597 - <Graph.Util.eachAdjacency>
4598 - <Graph.Util.eachLevel>
4599 - <Graph.Util.eachSubgraph>
4600 - <Graph.Util.eachSubnode>
4601 - <Graph.Util.anySubnode>
4602 - <Graph.Util.getSubnodes>
4603 - <Graph.Util.getParents>
4604 - <Graph.Util.isDescendantOf>
4606 MultiGraph.Node = new Class({
4608 initialize: function(opt, klass, Node, Edge, Label) {
4609 var innerOptions = {
4627 'startPos': new klass,
4631 $.extend(this, $.extend(innerOptions, opt));
4632 this.Config = this.Node = Node;
4640 Indicates if the node is adjacent to the node specified by id
4644 id - (string) A node id.
4648 node.adjacentTo('nodeId') == true;
4651 adjacentTo: function(node, port) {
4654 if (this.adjacencies[node.id] != undefined) {
4655 for(var i = 0, il = this.adjacencies[node.id].length; i < il; i++) {
4656 if (this.adjacencies[node.id][i]["portFrom"] == port)
4662 //return node.id in this.adjacencies;
4666 Method: adjacentWithDirectionTo
4668 Indicates if the node has a directed edge to the node specified by id
4672 id - (string) A node id.
4676 node.adjacentWithDirectionTo('nodeId') == true;
4679 adjacentWithDirectionTo: function(node) {
4680 var areNeighbors = node.id in this.adjacencies;
4681 if (!areNeighbors) {
4685 var direction = this.adjacencies[node.id].data.$direction;
4686 return direction[0] === this.id ;
4690 Method: getAdjacency
4692 Returns a <Graph.Adjacence> object connecting the current <Graph.Node> and the node having *id* as id.
4696 id - (string) A node id.
4698 getAdjacency: function(id) {
4699 return this.adjacencies[id];
4705 Returns the position of the node.
4709 type - (string) Default's *current*. Possible values are "start", "end" or "current".
4713 A <Complex> or <Polar> instance.
4717 var pos = node.getPos('end');
4720 getPos: function(type) {
4721 type = type || "current";
4722 if(type == "current") {
4724 } else if(type == "end") {
4726 } else if(type == "start") {
4727 return this.startPos;
4733 Sets the node's position.
4737 value - (object) A <Complex> or <Polar> instance.
4738 type - (string) Default's *current*. Possible values are "start", "end" or "current".
4742 node.setPos(new $jit.Complex(0, 0), 'end');
4745 setPos: function(value, type) {
4746 type = type || "current";
4748 if(type == "current") {
4750 } else if(type == "end") {
4752 } else if(type == "start") {
4753 pos = this.startPos;
4759 MultiGraph.Node.implement(Accessors);
4762 Class: Graph.Adjacence
4764 A <Graph> adjacence (or edge) connecting two <Graph.Nodes>.
4768 <Accessors> methods.
4772 <Graph>, <Graph.Node>
4776 nodeFrom - A <Graph.Node> connected by this edge.
4777 nodeTo - Another <Graph.Node> connected by this edge.
4778 data - Node data property containing a hash (i.e {}) with custom options.
4780 MultiGraph.Adjacence = new Class({
4782 initialize: function(nodeFrom, nodeTo, portFrom, portTo, data, Edge, Label) {
4783 this.nodeFrom = nodeFrom;
4784 this.nodeTo = nodeTo;
4785 this.data = data || {};
4786 this.startData = {};
4788 this.Config = this.Edge = Edge;
4791 this.portFrom = portFrom;
4792 this.portTo = portTo;
4796 MultiGraph.Adjacence.implement(Accessors);
4801 <Graph> traversal and processing utility object.
4805 For your convenience some of these methods have also been appended to <Graph> and <Graph.Node> classes.
4811 For internal use only. Provides a filtering function based on flags.
4813 filter: function(param) {
4814 if(!param || !($.type(param) == 'string')) return function() { return true; };
4815 var props = param.split(" ");
4816 return function(elem) {
4817 for(var i=0; i<props.length; i++) {
4818 if(elem[props[i]]) {
4828 Returns a <Graph.Node> by *id*.
4830 Also implemented by:
4836 graph - (object) A <Graph> instance.
4837 id - (string) A <Graph.Node> id.
4842 $jit.Graph.Util.getNode(graph, 'nodeid');
4844 graph.getNode('nodeid');
4847 getNode: function(graph, id) {
4848 return graph.nodes[id];
4854 Iterates over <Graph> nodes performing an *action*.
4856 Also implemented by:
4862 graph - (object) A <Graph> instance.
4863 action - (function) A callback function having a <Graph.Node> as first formal parameter.
4867 $jit.Graph.Util.eachNode(graph, function(node) {
4871 graph.eachNode(function(node) {
4876 eachNode: function(graph, action, flags) {
4877 var filter = this.filter(flags);
4878 for(var i in graph.nodes) {
4879 if(filter(graph.nodes[i])) action(graph.nodes[i]);
4886 Iterates over <Graph> nodes performing an *action*. It's an alias for <Graph.Util.eachNode>.
4888 Also implemented by:
4894 graph - (object) A <Graph> instance.
4895 action - (function) A callback function having a <Graph.Node> as first formal parameter.
4899 $jit.Graph.Util.each(graph, function(node) {
4903 graph.each(function(node) {
4908 each: function(graph, action, flags) {
4909 this.eachNode(graph, action, flags);
4912 eachAdjacencyBatch: function(node, action, flags) {
4913 var adj = node.adjacencies;
4915 for(var id in adj) {
4922 Method: eachAdjacency
4924 Iterates over <Graph.Node> adjacencies applying the *action* function.
4926 Also implemented by:
4932 node - (object) A <Graph.Node>.
4933 action - (function) A callback function having <Graph.Adjacence> as first formal parameter.
4937 $jit.Graph.Util.eachAdjacency(node, function(adj) {
4938 alert(adj.nodeTo.name);
4941 node.eachAdjacency(function(adj) {
4942 alert(adj.nodeTo.name);
4946 eachAdjacency: function(node, action, flags) {
4947 var adj = node.adjacencies, filter = this.filter(flags);
4949 for(var id in adj) {
4956 /*var adj = node.adjacencies, filter = this.filter(flags);
4957 for(var id in adj) {
4960 if(a.nodeFrom != node) {
4961 var tmp = a.nodeFrom;
4962 a.nodeFrom = a.nodeTo;
4971 Method: computeLevels
4973 Performs a BFS traversal setting the correct depth for each node.
4975 Also implemented by:
4981 The depth of each node can then be accessed by
4986 graph - (object) A <Graph>.
4987 id - (string) A starting node id for the BFS traversal.
4988 startDepth - (optional|number) A minimum depth value. Default's 0.
4991 computeLevels: function(graph, id, startDepth, flags) {
4992 startDepth = startDepth || 0;
4993 var filter = this.filter(flags);
4994 this.eachNode(graph, function(elem) {
4998 var root = graph.getNode(id);
4999 root._depth = startDepth;
5001 while(queue.length != 0) {
5002 var node = queue.pop();
5004 this.eachAdjacency(node, function(adj) {
5006 if(n._flag == false && filter(n) && !adj._hiding) {
5007 if(n._depth < 0) n._depth = node._depth + 1 + startDepth;
5017 Performs a BFS traversal applying *action* to each <Graph.Node>.
5019 Also implemented by:
5025 graph - (object) A <Graph>.
5026 id - (string) A starting node id for the BFS traversal.
5027 action - (function) A callback function having a <Graph.Node> as first formal parameter.
5031 $jit.Graph.Util.eachBFS(graph, 'mynodeid', function(node) {
5035 graph.eachBFS('mynodeid', function(node) {
5040 eachBFS: function(graph, id, action, flags) {
5041 var filter = this.filter(flags);
5043 var queue = [graph.getNode(id)];
5044 while(queue.length != 0) {
5045 var node = queue.pop();
5048 action(node, node._depth);
5049 this.eachAdjacency(node, function(adj) {
5051 if(n._flag == false && filter(n) && !adj._hiding) {
5062 Iterates over a node's subgraph applying *action* to the nodes of relative depth between *levelBegin* and *levelEnd*.
5063 In case you need to break the iteration, *action* should return false.
5065 Also implemented by:
5071 node - (object) A <Graph.Node>.
5072 levelBegin - (number) A relative level value.
5073 levelEnd - (number) A relative level value.
5074 action - (function) A callback function having a <Graph.Node> as first formal parameter.
5077 eachLevel: function(node, levelBegin, levelEnd, action, flags) {
5078 var d = node._depth, filter = this.filter(flags), that = this, shouldContinue = true;
5079 levelEnd = levelEnd === false? Number.MAX_VALUE -d : levelEnd;
5080 (function loopLevel(node, levelBegin, levelEnd) {
5081 if(!shouldContinue) return;
5082 var d = node._depth, ret;
5083 if(d >= levelBegin && d <= levelEnd && filter(node)) ret = action(node, d);
5084 if(typeof ret !== "undefined") shouldContinue = ret;
5085 if(shouldContinue && d < levelEnd) {
5086 that.eachAdjacency(node, function(adj) {
5088 if(n._depth > d) loopLevel(n, levelBegin, levelEnd);
5091 })(node, levelBegin + d, levelEnd + d);
5095 Method: eachSubgraph
5097 Iterates over a node's children recursively.
5099 Also implemented by:
5104 node - (object) A <Graph.Node>.
5105 action - (function) A callback function having a <Graph.Node> as first formal parameter.
5109 $jit.Graph.Util.eachSubgraph(node, function(node) {
5113 node.eachSubgraph(function(node) {
5118 eachSubgraph: function(node, action, flags) {
5119 this.eachLevel(node, 0, false, action, flags);
5125 Iterates over a node's children (without deeper recursion).
5127 Also implemented by:
5132 node - (object) A <Graph.Node>.
5133 action - (function) A callback function having a <Graph.Node> as first formal parameter.
5137 $jit.Graph.Util.eachSubnode(node, function(node) {
5141 node.eachSubnode(function(node) {
5146 eachSubnode: function(node, action, flags) {
5147 this.eachLevel(node, 1, 1, action, flags);
5153 Returns *true* if any subnode matches the given condition.
5155 Also implemented by:
5160 node - (object) A <Graph.Node>.
5161 cond - (function) A callback function returning a Boolean instance. This function has as first formal parameter a <Graph.Node>.
5165 $jit.Graph.Util.anySubnode(node, function(node) { return node.name == "mynodename"; });
5167 node.anySubnode(function(node) { return node.name == 'mynodename'; });
5170 anySubnode: function(node, cond, flags) {
5172 cond = cond || $.lambda(true);
5173 var c = $.type(cond) == 'string'? function(n) { return n[cond]; } : cond;
5174 this.eachSubnode(node, function(elem) {
5175 if(c(elem)) flag = true;
5183 Collects all subnodes for a specified node.
5184 The *level* parameter filters nodes having relative depth of *level* from the root node.
5186 Also implemented by:
5191 node - (object) A <Graph.Node>.
5192 level - (optional|number) Default's *0*. A starting relative depth for collecting nodes.
5198 getSubnodes: function(node, level, flags) {
5199 var ans = [], that = this;
5201 var levelStart, levelEnd;
5202 if($.type(level) == 'array') {
5203 levelStart = level[0];
5204 levelEnd = level[1];
5207 levelEnd = Number.MAX_VALUE - node._depth;
5209 this.eachLevel(node, levelStart, levelEnd, function(n) {
5219 Returns an Array of <Graph.Nodes> which are parents of the given node.
5221 Also implemented by:
5226 node - (object) A <Graph.Node>.
5229 An Array of <Graph.Nodes>.
5233 var pars = $jit.Graph.Util.getParents(node);
5235 var pars = node.getParents();
5237 if(pars.length > 0) {
5238 //do stuff with parents
5242 getParents: function(node) {
5244 this.eachAdjacency(node, function(adj) {
5246 if(n._depth < node._depth) ans.push(n);
5252 Method: isDescendantOf
5254 Returns a boolean indicating if some node is descendant of the node with the given id.
5256 Also implemented by:
5262 node - (object) A <Graph.Node>.
5263 id - (string) A <Graph.Node> id.
5267 $jit.Graph.Util.isDescendantOf(node, "nodeid"); //true|false
5269 node.isDescendantOf('nodeid');//true|false
5272 isDescendantOf: function(node, id) {
5273 if(node.id == id) return true;
5274 var pars = this.getParents(node), ans = false;
5275 for ( var i = 0; !ans && i < pars.length; i++) {
5276 ans = ans || this.isDescendantOf(pars[i], id);
5284 Cleans flags from nodes.
5286 Also implemented by:
5291 graph - A <Graph> instance.
5293 clean: function(graph) { this.eachNode(graph, function(elem) { elem._flag = false; }); },
5296 Method: getClosestNodeToOrigin
5298 Returns the closest node to the center of canvas.
5300 Also implemented by:
5306 graph - (object) A <Graph> instance.
5307 prop - (optional|string) Default's 'current'. A <Graph.Node> position property. Possible properties are 'start', 'current' or 'end'.
5310 getClosestNodeToOrigin: function(graph, prop, flags) {
5311 return this.getClosestNodeToPos(graph, Polar.KER, prop, flags);
5315 Method: getClosestNodeToPos
5317 Returns the closest node to the given position.
5319 Also implemented by:
5325 graph - (object) A <Graph> instance.
5326 pos - (object) A <Complex> or <Polar> instance.
5327 prop - (optional|string) Default's *current*. A <Graph.Node> position property. Possible properties are 'start', 'current' or 'end'.
5330 getClosestNodeToPos: function(graph, pos, prop, flags) {
5332 prop = prop || 'current';
5333 pos = pos && pos.getc(true) || Complex.KER;
5334 var distance = function(a, b) {
5335 var d1 = a.x - b.x, d2 = a.y - b.y;
5336 return d1 * d1 + d2 * d2;
5338 this.eachNode(graph, function(elem) {
5339 node = (node == null || distance(elem.getPos(prop).getc(true), pos) < distance(
5340 node.getPos(prop).getc(true), pos)) ? elem : node;
5346 //Append graph methods to <Graph>
5347 $.each(['get', 'getNode', 'each', 'eachNode', 'computeLevels', 'eachBFS', 'clean', 'getClosestNodeToPos', 'getClosestNodeToOrigin'], function(m) {
5348 MultiGraph.prototype[m] = function() {
5349 return MultiGraph.Util[m].apply(MultiGraph.Util, [this].concat(Array.prototype.slice.call(arguments)));
5353 //Append node methods to <Graph.Node>
5354 $.each(['eachAdjacencyBatch', 'eachAdjacency', 'eachLevel', 'eachSubgraph', 'eachSubnode', 'anySubnode', 'getSubnodes', 'getParents', 'isDescendantOf'], function(m) {
5355 MultiGraph.Node.prototype[m] = function() {
5356 return MultiGraph.Util[m].apply(MultiGraph.Util, [this].concat(Array.prototype.slice.call(arguments)));
5369 Perform <Graph> operations like adding/removing <Graph.Nodes> or <Graph.Adjacences>,
5370 morphing a <Graph> into another <Graph>, contracting or expanding subtrees, etc.
5382 initialize: function(viz) {
5389 Removes one or more <Graph.Nodes> from the visualization.
5390 It can also perform several animations like fading sequentially, fading concurrently, iterating or replotting.
5394 node - (string|array) The node's id. Can also be an array having many ids.
5395 opt - (object) Animation options. It's an object with optional properties described below
5396 type - (string) Default's *nothing*. Type of the animation. Can be "nothing", "replot", "fade:seq", "fade:con" or "iter".
5397 duration - Described in <Options.Fx>.
5398 fps - Described in <Options.Fx>.
5399 transition - Described in <Options.Fx>.
5400 hideLabels - (boolean) Default's *true*. Hide labels during the animation.
5404 var viz = new $jit.Viz(options);
5405 viz.op.removeNode('nodeId', {
5409 transition: $jit.Trans.Quart.easeOut
5412 viz.op.removeNode(['someId', 'otherId'], {
5419 removeNode: function(node, opt) {
5421 var options = $.merge(this.options, viz.controller, opt);
5422 var n = $.splat(node);
5423 var i, that, nodeObj;
5424 switch(options.type) {
5426 for(i=0; i<n.length; i++) {
5427 options.onBeforeRemoveNode(viz.graph.getNode(n[i]));
5428 viz.graph.removeNode(n[i]);
5433 this.removeNode(n, { type: 'nothing' });
5434 viz.labels.clearLabels();
5438 case 'fade:seq': case 'fade':
5440 //set alpha to 0 for nodes to remove.
5441 for(i=0; i<n.length; i++) {
5442 nodeObj = viz.graph.getNode(n[i]);
5443 nodeObj.setData('alpha', 0, 'end');
5445 viz.fx.animate($.merge(options, {
5446 modes: ['node-property:alpha'],
5447 onComplete: function() {
5448 that.removeNode(n, { type: 'nothing' });
5449 viz.labels.clearLabels();
5451 viz.fx.animate($.merge(options, {
5460 //set alpha to 0 for nodes to remove. Tag them for being ignored on computing positions.
5461 for(i=0; i<n.length; i++) {
5462 nodeObj = viz.graph.getNode(n[i]);
5463 nodeObj.setData('alpha', 0, 'end');
5464 nodeObj.ignore = true;
5467 viz.fx.animate($.merge(options, {
5468 modes: ['node-property:alpha', 'linear'],
5469 onComplete: function() {
5470 that.removeNode(n, { type: 'nothing' });
5471 options.onComplete && options.onComplete();
5479 condition: function() { return n.length != 0; },
5480 step: function() { that.removeNode(n.shift(), { type: 'nothing' }); viz.labels.clearLabels(); },
5481 onComplete: function() { options.onComplete && options.onComplete(); },
5482 duration: Math.ceil(options.duration / n.length)
5486 default: this.doError();
5493 Removes one or more <Graph.Adjacences> from the visualization.
5494 It can also perform several animations like fading sequentially, fading concurrently, iterating or replotting.
5498 vertex - (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'], ...]).
5499 opt - (object) Animation options. It's an object with optional properties described below
5500 type - (string) Default's *nothing*. Type of the animation. Can be "nothing", "replot", "fade:seq", "fade:con" or "iter".
5501 duration - Described in <Options.Fx>.
5502 fps - Described in <Options.Fx>.
5503 transition - Described in <Options.Fx>.
5504 hideLabels - (boolean) Default's *true*. Hide labels during the animation.
5508 var viz = new $jit.Viz(options);
5509 viz.op.removeEdge(['nodeId', 'otherId'], {
5513 transition: $jit.Trans.Quart.easeOut
5516 viz.op.removeEdge([['someId', 'otherId'], ['id3', 'id4']], {
5523 removeEdge: function(vertex, opt) {
5525 var options = $.merge(this.options, viz.controller, opt);
5526 var v = ($.type(vertex[0]) == 'string')? [vertex] : vertex;
5528 switch(options.type) {
5530 for(i=0; i<v.length; i++) viz.graph.removeAdjacence(v[i][0], v[i][1]);
5534 this.removeEdge(v, { type: 'nothing' });
5538 case 'fade:seq': case 'fade':
5540 //set alpha to 0 for edges to remove.
5541 for(i=0; i<v.length; i++) {
5542 adj = viz.graph.getAdjacence(v[i][0], v[i][1]);
5544 adj.setData('alpha', 0,'end');
5547 viz.fx.animate($.merge(options, {
5548 modes: ['edge-property:alpha'],
5549 onComplete: function() {
5550 that.removeEdge(v, { type: 'nothing' });
5552 viz.fx.animate($.merge(options, {
5561 //set alpha to 0 for nodes to remove. Tag them for being ignored when computing positions.
5562 for(i=0; i<v.length; i++) {
5563 adj = viz.graph.getAdjacence(v[i][0], v[i][1]);
5565 adj.setData('alpha',0 ,'end');
5570 viz.fx.animate($.merge(options, {
5571 modes: ['edge-property:alpha', 'linear'],
5572 onComplete: function() {
5573 that.removeEdge(v, { type: 'nothing' });
5574 options.onComplete && options.onComplete();
5582 condition: function() { return v.length != 0; },
5583 step: function() { that.removeEdge(v.shift(), { type: 'nothing' }); viz.labels.clearLabels(); },
5584 onComplete: function() { options.onComplete(); },
5585 duration: Math.ceil(options.duration / v.length)
5589 default: this.doError();
5596 Adds a new graph to the visualization.
5597 The JSON graph (or tree) must at least have a common node with the current graph plotted by the visualization.
5598 The resulting graph can be defined as follows <http://mathworld.wolfram.com/GraphSum.html>
5602 json - (object) A json tree or graph structure. See also <Loader.loadJSON>.
5603 opt - (object) Animation options. It's an object with optional properties described below
5604 type - (string) Default's *nothing*. Type of the animation. Can be "nothing", "replot", "fade:seq", "fade:con".
5605 duration - Described in <Options.Fx>.
5606 fps - Described in <Options.Fx>.
5607 transition - Described in <Options.Fx>.
5608 hideLabels - (boolean) Default's *true*. Hide labels during the animation.
5612 //...json contains a tree or graph structure...
5614 var viz = new $jit.Viz(options);
5619 transition: $jit.Trans.Quart.easeOut
5629 sum: function(json, opt) {
5631 var options = $.merge(this.options, viz.controller, opt), root = viz.root;
5633 viz.root = opt.id || viz.root;
5634 switch(options.type) {
5636 graph = viz.construct(json);
5637 graph.eachNode(function(elem) {
5638 elem.eachAdjacency(function(adj) {
5639 viz.graph.addAdjacence(adj.nodeFrom, adj.nodeTo, adj.data);
5646 this.sum(json, { type: 'nothing' });
5650 case 'fade:seq': case 'fade': case 'fade:con':
5652 graph = viz.construct(json);
5654 //set alpha to 0 for nodes to add.
5655 var fadeEdges = this.preprocessSum(graph);
5656 var modes = !fadeEdges? ['node-property:alpha'] : ['node-property:alpha', 'edge-property:alpha'];
5658 if(options.type != 'fade:con') {
5659 viz.fx.animate($.merge(options, {
5661 onComplete: function() {
5662 viz.fx.animate($.merge(options, {
5664 onComplete: function() {
5665 options.onComplete();
5671 viz.graph.eachNode(function(elem) {
5672 if (elem.id != root && elem.pos.isZero()) {
5673 elem.pos.set(elem.endPos);
5674 elem.startPos.set(elem.endPos);
5677 viz.fx.animate($.merge(options, {
5678 modes: ['linear'].concat(modes)
5683 default: this.doError();
5690 This method will transform the current visualized graph into the new JSON representation passed in the method.
5691 The JSON object must at least have the root node in common with the current visualized graph.
5695 json - (object) A json tree or graph structure. See also <Loader.loadJSON>.
5696 opt - (object) Animation options. It's an object with optional properties described below
5697 type - (string) Default's *nothing*. Type of the animation. Can be "nothing", "replot", "fade:con".
5698 duration - Described in <Options.Fx>.
5699 fps - Described in <Options.Fx>.
5700 transition - Described in <Options.Fx>.
5701 hideLabels - (boolean) Default's *true*. Hide labels during the animation.
5702 id - (string) The shared <Graph.Node> id between both graphs.
5704 extraModes - (optional|object) When morphing with an animation, dollar prefixed data parameters are added to
5705 *endData* and not *data* itself. This way you can animate dollar prefixed parameters during your morphing operation.
5706 For animating these extra-parameters you have to specify an object that has animation groups as keys and animation
5707 properties as values, just like specified in <Graph.Plot.animate>.
5711 //...json contains a tree or graph structure...
5713 var viz = new $jit.Viz(options);
5714 viz.op.morph(json, {
5718 transition: $jit.Trans.Quart.easeOut
5721 viz.op.morph(json, {
5725 //if the json data contains dollar prefixed params
5726 //like $width or $height these too can be animated
5727 viz.op.morph(json, {
5731 'node-property': ['width', 'height']
5736 morph: function(json, opt, extraModes) {
5737 extraModes = extraModes || {};
5739 var options = $.merge(this.options, viz.controller, opt), root = viz.root;
5741 //TODO(nico) this hack makes morphing work with the Hypertree.
5742 //Need to check if it has been solved and this can be removed.
5743 viz.root = opt.id || viz.root;
5744 switch(options.type) {
5746 graph = viz.construct(json);
5747 graph.eachNode(function(elem) {
5748 var nodeExists = viz.graph.hasNode(elem.id);
5749 elem.eachAdjacency(function(adj) {
5750 var adjExists = !!viz.graph.getAdjacence(adj.nodeFrom.id, adj.nodeTo.id);
5751 viz.graph.addAdjacence(adj.nodeFrom, adj.nodeTo, adj.data);
5752 //Update data properties if the node existed
5754 var addedAdj = viz.graph.getAdjacence(adj.nodeFrom.id, adj.nodeTo.id);
5755 for(var prop in (adj.data || {})) {
5756 addedAdj.data[prop] = adj.data[prop];
5760 //Update data properties if the node existed
5762 var addedNode = viz.graph.getNode(elem.id);
5763 for(var prop in (elem.data || {})) {
5764 addedNode.data[prop] = elem.data[prop];
5768 viz.graph.eachNode(function(elem) {
5769 elem.eachAdjacency(function(adj) {
5770 if(!graph.getAdjacence(adj.nodeFrom.id, adj.nodeTo.id)) {
5771 viz.graph.removeAdjacence(adj.nodeFrom.id, adj.nodeTo.id);
5774 if(!graph.hasNode(elem.id)) viz.graph.removeNode(elem.id);
5780 viz.labels.clearLabels(true);
5781 this.morph(json, { type: 'nothing' });
5786 case 'fade:seq': case 'fade': case 'fade:con':
5788 graph = viz.construct(json);
5789 //preprocessing for nodes to delete.
5790 //get node property modes to interpolate
5791 var nodeModes = ('node-property' in extraModes)
5792 && $.map($.splat(extraModes['node-property']),
5793 function(n) { return '$' + n; });
5794 viz.graph.eachNode(function(elem) {
5795 var graphNode = graph.getNode(elem.id);
5797 elem.setData('alpha', 1);
5798 elem.setData('alpha', 1, 'start');
5799 elem.setData('alpha', 0, 'end');
5802 //Update node data information
5803 var graphNodeData = graphNode.data;
5804 for(var prop in graphNodeData) {
5805 if(nodeModes && ($.indexOf(nodeModes, prop) > -1)) {
5806 elem.endData[prop] = graphNodeData[prop];
5808 elem.data[prop] = graphNodeData[prop];
5813 viz.graph.eachNode(function(elem) {
5814 if(elem.ignore) return;
5815 elem.eachAdjacency(function(adj) {
5816 if(adj.nodeFrom.ignore || adj.nodeTo.ignore) return;
5817 var nodeFrom = graph.getNode(adj.nodeFrom.id);
5818 var nodeTo = graph.getNode(adj.nodeTo.id);
5819 if(!nodeFrom.adjacentTo(nodeTo)) {
5820 var adj = viz.graph.getAdjacence(nodeFrom.id, nodeTo.id);
5822 adj.setData('alpha', 1);
5823 adj.setData('alpha', 1, 'start');
5824 adj.setData('alpha', 0, 'end');
5826 } else if (adj.data.$direction && adj.data.$direction[0] === nodeFrom.id) {
5827 // only check one direction (from -> to)
5828 if (!nodeFrom.adjacentWithDirectionTo(nodeTo)) {
5829 adj._reversing = true;
5834 //preprocessing for adding nodes.
5835 var fadeEdges = this.preprocessSum(graph);
5837 var modes = !fadeEdges? ['node-property:alpha'] :
5838 ['node-property:alpha',
5839 'edge-property:alpha'];
5840 //Append extra node-property animations (if any)
5841 modes[0] = modes[0] + (('node-property' in extraModes)?
5842 (':' + $.splat(extraModes['node-property']).join(':')) : '');
5843 //Append extra edge-property animations (if any)
5844 modes[1] = (modes[1] || 'edge-property:alpha') + (('edge-property' in extraModes)?
5845 (':' + $.splat(extraModes['edge-property']).join(':')) : '');
5846 //Add label-property animations (if any)
5847 if('label-property' in extraModes) {
5848 modes.push('label-property:' + $.splat(extraModes['label-property']).join(':'))
5850 //only use reposition if its implemented.
5851 if (viz.reposition) {
5856 this._updateDirectedEdges();
5858 viz.graph.eachNode(function(elem) {
5859 if (elem.id != root && elem.pos.getp().equals(Polar.KER)) {
5860 elem.pos.set(elem.endPos); elem.startPos.set(elem.endPos);
5863 viz.fx.animate($.merge(options, {
5864 modes: [extraModes.position || 'polar'].concat(modes),
5865 onComplete: function() {
5866 viz.graph.eachNode(function(elem) {
5867 if(elem.ignore) viz.graph.removeNode(elem.id);
5869 viz.graph.eachNode(function(elem) {
5870 elem.eachAdjacency(function(adj) {
5871 if(adj.ignore) viz.graph.removeAdjacence(adj.nodeFrom.id, adj.nodeTo.id);
5874 options.onComplete();
5883 _updateDirectedEdges: function () {
5884 var graph = this.viz.graph;
5885 graph.eachNode(function(node) {
5886 node.eachAdjacency(function (adj) {
5888 var isDirectedEdge = adj.data.$direction;
5889 if (isDirectedEdge && adj.nodeFrom.id !== adj.data.$direction[0]) {
5894 graph.removeAdjacence(adj.nodeFrom.id, adj.nodeTo.id);
5897 if (adj._reversing) {
5898 var from = adj.nodeFrom.id;
5899 var to = adj.nodeTo.id;
5901 // // swap instead of adding and removing
5902 var edge1 = graph.edges[from][to];
5903 var edge2 = graph.edges[to][from];
5905 edge1.data.$direction = [to, from];
5906 edge2.data.$direction = [to, from];
5908 adj._reversing = undefined;
5917 Collapses the subtree of the given node. The node will have a _collapsed=true_ property.
5921 node - (object) A <Graph.Node>.
5922 opt - (object) An object containing options described below
5923 type - (string) Whether to 'replot' or 'animate' the contraction.
5925 There are also a number of Animation options. For more information see <Options.Fx>.
5929 var viz = new $jit.Viz(options);
5930 viz.op.contract(node, {
5934 transition: $jit.Trans.Quart.easeOut
5939 contract: function(node, opt) {
5941 if(node.collapsed || !node.anySubnode($.lambda(true))) return;
5942 opt = $.merge(this.options, viz.config, opt || {}, {
5943 'modes': ['node-property:alpha:span', 'linear']
5945 node.collapsed = true;
5947 n.eachSubnode(function(ch) {
5949 ch.setData('alpha', 0, opt.type == 'animate'? 'end' : 'current');
5953 if(opt.type == 'animate') {
5956 viz.rotate(viz.rotated, 'none', {
5961 n.eachSubnode(function(ch) {
5962 ch.setPos(node.getPos('end'), 'end');
5966 viz.fx.animate(opt);
5967 } else if(opt.type == 'replot'){
5975 Expands the previously contracted subtree. The given node must have the _collapsed=true_ property.
5979 node - (object) A <Graph.Node>.
5980 opt - (object) An object containing options described below
5981 type - (string) Whether to 'replot' or 'animate'.
5983 There are also a number of Animation options. For more information see <Options.Fx>.
5987 var viz = new $jit.Viz(options);
5988 viz.op.expand(node, {
5992 transition: $jit.Trans.Quart.easeOut
5997 expand: function(node, opt) {
5998 if(!('collapsed' in node)) return;
6000 opt = $.merge(this.options, viz.config, opt || {}, {
6001 'modes': ['node-property:alpha:span', 'linear']
6003 delete node.collapsed;
6005 n.eachSubnode(function(ch) {
6007 ch.setData('alpha', 1, opt.type == 'animate'? 'end' : 'current');
6011 if(opt.type == 'animate') {
6014 viz.rotate(viz.rotated, 'none', {
6018 viz.fx.animate(opt);
6019 } else if(opt.type == 'replot'){
6024 preprocessSum: function(graph) {
6026 graph.eachNode(function(elem) {
6027 if(!viz.graph.hasNode(elem.id)) {
6028 viz.graph.addNode(elem);
6029 var n = viz.graph.getNode(elem.id);
6030 n.setData('alpha', 0);
6031 n.setData('alpha', 0, 'start');
6032 n.setData('alpha', 1, 'end');
6035 var fadeEdges = false;
6036 graph.eachNode(function(elem) {
6037 elem.eachAdjacency(function(adj) {
6038 var nodeFrom = viz.graph.getNode(adj.nodeFrom.id);
6039 var nodeTo = viz.graph.getNode(adj.nodeTo.id);
6040 if(!nodeFrom.adjacentTo(nodeTo)) {
6041 var adj = viz.graph.addAdjacence(nodeFrom, nodeTo, adj.data);
6042 if(nodeFrom.startAlpha == nodeFrom.endAlpha
6043 && nodeTo.startAlpha == nodeTo.endAlpha) {
6045 adj.setData('alpha', 0);
6046 adj.setData('alpha', 0, 'start');
6047 adj.setData('alpha', 1, 'end');
6061 Helpers are objects that contain rendering primitives (like rectangles, ellipses, etc), for plotting nodes and edges.
6062 Helpers also contain implementations of the *contains* method, a method returning a boolean indicating whether the mouse
6063 position is over the rendered shape.
6065 Helpers are very useful when implementing new NodeTypes, since you can access them through *this.nodeHelper* and
6066 *this.edgeHelper* <Graph.Plot> properties, providing you with simple primitives and mouse-position check functions.
6070 //implement a new node type
6071 $jit.Viz.Plot.NodeTypes.implement({
6073 'render': function(node, canvas) {
6074 this.nodeHelper.circle.render ...
6076 'contains': function(node, pos) {
6077 this.nodeHelper.circle.contains ...
6081 //implement an edge type
6082 $jit.Viz.Plot.EdgeTypes.implement({
6084 'render': function(node, canvas) {
6085 this.edgeHelper.circle.render ...
6088 'contains': function(node, pos) {
6089 this.edgeHelper.circle.contains ...
6100 Contains rendering and other type of primitives for simple shapes.
6102 var MultiNodeHelper = {
6105 'contains': $.lambda(false)
6108 'render': function(pos, canvas, animating) {
6109 var ctx = canvas.getCtx();
6110 var img = new Image();
6111 img.src='../img/Device_pc_3045_default_64.png';
6112 if(animating == false) {
6113 img.onload = function() {
6114 ctx.drawImage(img,pos.x-32,pos.y-32);
6117 ctx.drawImage(img,pos.x-32,pos.y-32);
6119 /*var width = 64, height = 55;
6120 ctx.fillStyle = "rgba(0,0,0,0)";
6121 ctx.fillRect(pos.x - width / 2, pos.y - height / 2, width, height);*/
6123 'contains': function(npos, pos) {
6124 var width = 64, height = 55;
6125 return Math.abs(pos.x - npos.x) <= width / 2
6126 && Math.abs(pos.y - npos.y) <= height / 2;
6130 'render': function(pos, canvas, animating) {
6131 var ctx = canvas.getCtx();
6132 var img = new Image();
6133 img.src='../img/Device_switch_3062_unknown_64.png';
6134 if(animating == false) {
6135 img.onload = function() {
6136 ctx.drawImage(img,pos.x-32,pos.y-32);
6139 ctx.drawImage(img,pos.x-32,pos.y-32);
6141 /*var width = 64, height = 45;
6142 ctx.fillStyle = "rgba(0,0,0,0)";
6143 ctx.fillRect(pos.x - width / 2, pos.y - height / 2, width, height);*/
6145 'contains': function(npos, pos) {
6146 var width = 64, height = 45;
6147 return Math.abs(pos.x - npos.x) <= width / 2
6148 && Math.abs(pos.y - npos.y) <= height / 2;
6156 Contains rendering primitives for simple edge shapes.
6158 var MultiEdgeHelper = {
6160 Object: EdgeHelper.line
6166 Renders a line into the canvas.
6170 from - (object) An *x*, *y* object with the starting position of the line.
6171 to - (object) An *x*, *y* object with the ending position of the line.
6172 canvas - (object) A <Canvas> instance.
6176 EdgeHelper.line.render({ x: 10, y: 30 }, { x: 10, y: 50 }, viz.canvas);
6179 'render': function(from, to, alpha, canvas){
6181 var ctx = canvas.getCtx();
6185 dx = from.x-to.x; dy = from.y-to.y;
6186 tan = Math.atan(dx/dy);
6187 cos = (-1)*Math.cos(tan);
6188 sin = Math.sin(tan);
6191 ctx.moveTo(from.x+(alpha*cos), from.y+(alpha*sin));
6192 ctx.lineTo(to.x+(alpha*cos), to.y+(alpha*sin));
6198 Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.
6202 posFrom - (object) An *x*, *y* object with a <Graph.Node> position.
6203 posTo - (object) An *x*, *y* object with a <Graph.Node> position.
6204 pos - (object) An *x*, *y* object with the position to check.
6205 epsilon - (number) The dimension of the shape.
6209 EdgeHelper.line.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, { x: 15, y: 35 }, 30);
6212 'contains': function(posFrom, posTo, alpha, pos, epsilon, canvas) {
6214 var ctx = canvas.getCtx();
6219 dx = posFrom.x-posTo.x; dy = posFrom.y-posTo.y;
6220 tan = Math.atan(dx/dy);
6221 cos = (-1)*Math.cos(tan);
6222 sin = Math.sin(tan);
6226 ctx.moveTo(posFrom.x+((alpha-width)*cos), posFrom.y+((alpha-width)*sin));
6227 ctx.lineTo(posTo.x+((alpha-width)*cos), posTo.y+((alpha-width)*sin));
6228 ctx.lineTo(posTo.x+((alpha+width)*cos), posTo.y+((alpha+width)*sin));
6229 ctx.lineTo(posFrom.x+((alpha+width)*cos), posFrom.y+((alpha+width)*sin));
6231 // check if point is in path
6232 ox = ctx.canvas.offsetWidth/2;
6233 oy = ctx.canvas.offsetHeight/2;
6234 sx = canvas.scaleOffsetX;
6235 sy = canvas.scaleOffsetY;
6236 tx = canvas.translateOffsetX;
6237 ty = canvas.translateOffsetY;
6238 if (ctx.isPointInPath((tx+ox+pos.x*sx), (ty+oy+pos.y*sy))) {
6251 * File: Graph.Plot.js
6257 <Graph> rendering and animation methods.
6261 nodeHelper - <NodeHelper> object.
6262 edgeHelper - <EdgeHelper> object.
6265 //Default initializer
6266 initialize: function(viz, klass){
6268 this.config = viz.config;
6269 this.node = viz.config.Node;
6270 this.edge = viz.config.Edge;
6271 this.animation = new Animation;
6272 this.nodeTypes = new klass.Plot.NodeTypes;
6273 this.edgeTypes = new klass.Plot.EdgeTypes;
6274 this.labels = viz.labels;
6278 nodeHelper: MultiNodeHelper,
6279 edgeHelper: MultiEdgeHelper,
6282 //node/edge property parsers
6290 'lineWidth': 'number',
6291 'angularWidth':'number',
6293 'valueArray':'array-number',
6294 'dimArray':'array-number',
6295 'vertices':'polygon'
6296 //'colorArray':'array-color'
6299 //canvas specific parsers
6301 'globalAlpha': 'number',
6302 'fillStyle': 'color',
6303 'strokeStyle': 'color',
6304 'lineWidth': 'number',
6305 'shadowBlur': 'number',
6306 'shadowColor': 'color',
6307 'shadowOffsetX': 'number',
6308 'shadowOffsetY': 'number',
6309 'miterLimit': 'number'
6318 //Number interpolator
6319 'compute': function(from, to, delta) {
6320 return from + (to - from) * delta;
6323 //Position interpolators
6324 'moebius': function(elem, props, delta, vector) {
6325 var v = vector.scale(-delta);
6327 var x = v.x, y = v.y;
6328 var ans = elem.startPos
6329 .getc().moebiusTransformation(v);
6330 elem.pos.setc(ans.x, ans.y);
6335 'linear': function(elem, props, delta) {
6336 var from = elem.startPos.getc(true);
6337 var to = elem.endPos.getc(true);
6338 elem.pos.setc(this.compute(from.x, to.x, delta),
6339 this.compute(from.y, to.y, delta));
6342 'polar': function(elem, props, delta) {
6343 var from = elem.startPos.getp(true);
6344 var to = elem.endPos.getp();
6345 var ans = to.interpolate(from, delta);
6346 elem.pos.setp(ans.theta, ans.rho);
6349 //Graph's Node/Edge interpolators
6350 'number': function(elem, prop, delta, getter, setter) {
6352 if(Array.isArray(elem)) { // for multi-links
6353 for(var e in elem) {
6355 from = ee[getter](prop, 'start');
6356 to = ee[getter](prop, 'end');
6362 if(Array.isArray(elem)) return;
6363 } else { // for nodes
6364 from = elem[getter](prop, 'start');
6365 to = elem[getter](prop, 'end');
6367 elem[setter](prop, this.compute(from, to, delta));
6370 'color': function(elem, prop, delta, getter, setter) {
6372 if(Array.isArray(elem)) { // for multi-links
6373 for(var e in elem) {
6375 from = $.hexToRgb(ee[getter](prop, 'start'));
6376 to = $.hexToRgb(ee[getter](prop, 'end'));
6377 if(from[0] != to[0] || from[1] != to[1] || from[2] != to[2]) {
6382 if(Array.isArray(elem)) return;
6383 } else { // for nodes
6384 var from = $.hexToRgb(elem[getter](prop, 'start'));
6385 var to = $.hexToRgb(elem[getter](prop, 'end'));
6387 var comp = this.compute;
6388 var val = $.rgbToHex([parseInt(comp(from[0], to[0], delta)),
6389 parseInt(comp(from[1], to[1], delta)),
6390 parseInt(comp(from[2], to[2], delta))]);
6391 elem[setter](prop, val);
6394 'array-number': function(elem, prop, delta, getter, setter) {
6395 var from = elem[getter](prop, 'start'),
6396 to = elem[getter](prop, 'end'),
6398 for(var i=0, l=from.length; i<l; i++) {
6399 var fromi = from[i], toi = to[i];
6401 for(var j=0, len=fromi.length, curi=[]; j<len; j++) {
6402 curi.push(this.compute(fromi[j], toi[j], delta));
6406 cur.push(this.compute(fromi, toi, delta));
6409 elem[setter](prop, cur);
6412 'node': function(elem, props, delta, map, getter, setter) {
6415 var len = props.length;
6416 for(var i=0; i<len; i++) {
6418 this[map[pi]](elem, pi, delta, getter, setter);
6421 for(var pi in map) {
6422 this[map[pi]](elem, pi, delta, getter, setter);
6427 'edge': function(elem, props, delta, mapKey, getter, setter) {
6428 var adjs = elem.adjacencies;
6429 for(var id in adjs) this['node'](adjs[id], props, delta, mapKey, getter, setter);
6432 'node-property': function(elem, props, delta) {
6433 this['node'](elem, props, delta, 'map', 'getData', 'setData');
6436 'edge-property': function(elem, props, delta) {
6437 this['edge'](elem, props, delta, 'map', 'getData', 'setData');
6440 'label-property': function(elem, props, delta) {
6441 this['node'](elem, props, delta, 'label', 'getLabelData', 'setLabelData');
6444 'node-style': function(elem, props, delta) {
6445 this['node'](elem, props, delta, 'canvas', 'getCanvasStyle', 'setCanvasStyle');
6448 'edge-style': function(elem, props, delta) {
6449 this['edge'](elem, props, delta, 'canvas', 'getCanvasStyle', 'setCanvasStyle');
6452 'polygon': function(elem, prop, delta, getter, setter) {
6453 var from = elem[getter](prop, 'start'),
6454 to = elem[getter](prop, 'end'),
6456 if (typeof from.offset == 'undefined') {
6458 from = $.map(to, function() {
6459 return new $jit.Complex(0, 0);
6464 if (from.length == 0) {
6465 from.push(new $jit.Complex(0, 0));
6467 while (from.length < to.length) {
6470 while (from.length > to.length) {
6471 to.push(to[0] || new $jit.Complex(0, 0));
6473 if (from.length == 0) return;
6474 var l = from.length;
6475 var minDist = 1e300;
6476 for (var offset = 0; offset < l; offset ++) {
6478 for (var i = 0; i < l; i++) {
6479 d +=Geometry.dist2(from[(offset + i) % l], to[i]);
6482 from.offset = offset;
6489 for (var i = 0, l = from.length; i < l; i++) {
6490 var fromi = from[(i + from.offset) % l], toi = to[i];
6491 cur.push(new $jit.Complex(
6492 this.compute(fromi.x, toi.x, delta),
6493 this.compute(fromi.y, toi.y, delta)
6496 elem[setter](prop, cur);
6504 Iteratively performs an action while refreshing the state of the visualization.
6508 options - (object) An object containing some sequence options described below
6509 condition - (function) A function returning a boolean instance in order to stop iterations.
6510 step - (function) A function to execute on each step of the iteration.
6511 onComplete - (function) A function to execute when the sequence finishes.
6512 duration - (number) Duration (in milliseconds) of each step.
6516 var rg = new $jit.RGraph(options);
6519 condition: function() {
6525 onComplete: function() {
6532 sequence: function(options) {
6535 condition: $.lambda(false),
6537 onComplete: $.empty,
6541 var interval = setInterval(function() {
6542 if(options.condition()) {
6545 clearInterval(interval);
6546 options.onComplete();
6548 that.viz.refresh(true);
6549 }, options.duration);
6555 Prepare graph position and other attribute values before performing an Animation.
6556 This method is used internally by the Toolkit.
6560 <Animation>, <Graph.Plot.animate>
6563 prepare: function(modes) {
6564 var graph = this.viz.graph,
6567 'getter': 'getData',
6571 'getter': 'getData',
6575 'getter': 'getCanvasStyle',
6576 'setter': 'setCanvasStyle'
6579 'getter': 'getCanvasStyle',
6580 'setter': 'setCanvasStyle'
6586 if($.type(modes) == 'array') {
6587 for(var i=0, len=modes.length; i < len; i++) {
6588 var elems = modes[i].split(':');
6589 m[elems.shift()] = elems;
6592 for(var p in modes) {
6593 if(p == 'position') {
6594 m[modes.position] = [];
6596 m[p] = $.splat(modes[p]);
6601 graph.eachNode(function(node) {
6602 node.startPos.set(node.pos);
6603 $.each(['node-property', 'node-style'], function(p) {
6606 for(var i=0, l=prop.length; i < l; i++) {
6607 node[accessors[p].setter](prop[i], node[accessors[p].getter](prop[i]), 'start');
6611 $.each(['edge-property', 'edge-style'], function(p) {
6614 node.eachAdjacency(function(adj) {
6615 for(var i=0, l=prop.length; i < l; i++) {
6616 adj[accessors[p].setter](prop[i], adj[accessors[p].getter](prop[i]), 'start');
6628 Animates a <Graph> by interpolating some <Graph.Node>, <Graph.Adjacence> or <Graph.Label> properties.
6632 opt - (object) Animation options. The object properties are described below
6633 duration - (optional) Described in <Options.Fx>.
6634 fps - (optional) Described in <Options.Fx>.
6635 hideLabels - (optional|boolean) Whether to hide labels during the animation.
6636 modes - (required|object) An object with animation modes (described below).
6640 Animation modes are strings representing different node/edge and graph properties that you'd like to animate.
6641 They are represented by an object that has as keys main categories of properties to animate and as values a list
6642 of these specific properties. The properties are described below
6644 position - Describes the way nodes' positions must be interpolated. Possible values are 'linear', 'polar' or 'moebius'.
6645 node-property - Describes which Node properties will be interpolated. These properties can be any of the ones defined in <Options.Node>.
6646 edge-property - Describes which Edge properties will be interpolated. These properties can be any the ones defined in <Options.Edge>.
6647 label-property - Describes which Label properties will be interpolated. These properties can be any of the ones defined in <Options.Label> like color or size.
6648 node-style - Describes which Node Canvas Styles will be interpolated. These are specific canvas properties like fillStyle, strokeStyle, lineWidth, shadowBlur, shadowColor, shadowOffsetX, shadowOffsetY, etc.
6649 edge-style - Describes which Edge Canvas Styles will be interpolated. These are specific canvas properties like fillStyle, strokeStyle, lineWidth, shadowBlur, shadowColor, shadowOffsetX, shadowOffsetY, etc.
6653 var viz = new $jit.Viz(options);
6654 //...tweak some Data, CanvasStyles or LabelData properties...
6657 'position': 'linear',
6658 'node-property': ['width', 'height'],
6659 'node-style': 'shadowColor',
6660 'label-property': 'size'
6664 //...can also be written like this...
6667 'node-property:width:height',
6668 'node-style:shadowColor',
6669 'label-property:size'],
6674 animate: function(opt, versor) {
6675 opt = $.merge(this.viz.config, opt || {});
6679 interp = this.Interpolator,
6680 animation = opt.type === 'nodefx'? this.nodeFxAnimation : this.animation;
6681 //prepare graph values
6682 var m = this.prepare(opt.modes);
6685 if(opt.hideLabels) this.labels.hideLabels(true);
6686 animation.setOptions($.extend(opt, {
6688 compute: function(delta) {
6689 graph.eachNode(function(node) {
6691 interp[p](node, m[p], delta, versor);
6694 that.plot(opt, this.$animating, delta);
6695 this.$animating = true;
6697 complete: function() {
6698 if(opt.hideLabels) that.labels.hideLabels(false);
6701 //TODO(nico): This shouldn't be here!
6702 //opt.onAfterCompute();
6710 Apply animation to node properties like color, width, height, dim, etc.
6714 options - Animation options. This object properties is described below
6715 elements - The Elements to be transformed. This is an object that has a properties
6719 //can also be an array of ids
6720 'id': 'id-of-node-to-transform',
6721 //properties to be modified. All properties are optional.
6723 'color': '#ccc', //some color
6724 'width': 10, //some width
6725 'height': 10, //some height
6726 'dim': 20, //some dim
6727 'lineWidth': 10 //some line width
6732 - _reposition_ Whether to recalculate positions and add a motion animation.
6733 This might be used when changing _width_ or _height_ properties in a <Layouts.Tree> like layout. Default's *false*.
6735 - _onComplete_ A method that is called when the animation completes.
6737 ...and all other <Graph.Plot.animate> options like _duration_, _fps_, _transition_, etc.
6741 var rg = new RGraph(canvas, config); //can be also Hypertree or ST
6748 'transition': Trans.Quart.easeOut
6753 nodeFx: function(opt) {
6754 if (this.nodeFxAnimation == undefined) {
6755 this.nodeFxAnimation = new Animation();
6760 animation = this.nodeFxAnimation,
6761 options = $.merge(this.viz.config, {
6768 opt = $.merge(options, opt || {}, {
6769 onBeforeCompute: $.empty,
6770 onAfterCompute: $.empty
6772 //check if an animation is running
6773 animation.stopTimer();
6774 var props = opt.elements.properties;
6775 //set end values for nodes
6776 if(!opt.elements.id) {
6777 graph.eachNode(function(n) {
6778 for(var prop in props) {
6779 n.setData(prop, props[prop], 'end');
6783 var ids = $.splat(opt.elements.id);
6784 $.each(ids, function(id) {
6785 var n = graph.getNode(id);
6787 for(var prop in props) {
6788 n.setData(prop, props[prop], 'end');
6795 for(var prop in props) propnames.push(prop);
6796 //add node properties modes
6797 var modes = ['node-property:' + propnames.join(':')];
6798 //set new node positions
6799 if(opt.reposition) {
6800 modes.push('linear');
6804 this.animate($.merge(opt, {
6818 opt - (optional) Plotting options. Most of them are described in <Options.Fx>.
6823 var viz = new $jit.Viz(options);
6828 plot: function(opt, animating) {
6831 canvas = viz.canvas,
6834 ctx = canvas.getCtx(),
6836 opt = opt || this.viz.controller;
6838 opt.clearCanvas && canvas.clear();
6840 var root = aGraph.getNode(id);
6843 var T = !!root.visited;
6846 var checkDupEdge = function(e) {
6848 for(var d in viz.graph.dup) {
6849 var ee = viz.graph.dup[d];
6850 if(e.nodeTo.id == ee.nodeTo.id && e.nodeFrom.id == ee.nodeFrom.id) {
6851 if(e.portTo == ee.portTo && e.portFrom == ee.portFrom) {
6853 break; // NOTE: does this break the outer for loop?
6863 aGraph.eachNode(function(node) {
6864 node.eachAdjacencyBatch(function(adj) {
6865 // plot this line if it hasn't been plotted
6866 var total = adj.length;
6868 if(!checkDupEdge(adj[0])) // plot this edge if it isn't a dup
6869 that.plotLine(adj[0], canvas, animating);
6871 for(var pos in adj) {
6874 // check if this edge is in duplicates
6875 if(checkDupEdge(a)) continue; // skip plot this edge if it's a dup
6877 that.plotBezierLine(a, pos, total, canvas, animating);
6883 var nodeAlpha = node.getData('alpha');
6884 node.eachAdjacency(function(adj) {
6885 var nodeTo = adj.nodeTo;
6886 if(!!nodeTo.visited === T && node.drawn && nodeTo.drawn) {
6887 !animating && opt.onBeforePlotLine(adj);
6888 that.plotLine(adj, canvas, animating);
6889 !animating && opt.onAfterPlotLine(adj);
6893 !animating && opt.onBeforePlotNode(node);
6894 that.plotNode(node, canvas, animating);
6895 !animating && opt.onAfterPlotNode(node);
6897 if(!that.labelsHidden && opt.withLabels) {
6898 if(node.drawn && nodeAlpha >= 0.95) {
6899 that.labels.plotLabel(canvas, node, opt);
6901 that.labels.hideLabel(node, false);
6908 // plot all of the nodes
6909 if (opt.modes != undefined && opt.modes.length > 0 && opt.modes[0] == "edge-property:color") {
6910 animating = true; // HACK
6912 aGraph.eachNode(function(node) {
6913 !animating && opt.onBeforePlotNode(node);
6914 that.plotNode(node, canvas, animating);
6915 !animating && opt.onAfterPlotNode(node);
6916 that.labels.plotLabel(canvas, node, opt);
6923 plotTree: function(node, opt, animating) {
6926 canvas = viz.canvas,
6927 config = this.config,
6928 ctx = canvas.getCtx();
6929 var nodeAlpha = node.getData('alpha');
6930 node.eachSubnode(function(elem) {
6931 if(opt.plotSubtree(node, elem) && elem.exist && elem.drawn) {
6932 var adj = node.getAdjacency(elem.id);
6933 !animating && opt.onBeforePlotLine(adj);
6934 that.plotLine(adj, canvas, animating);
6935 !animating && opt.onAfterPlotLine(adj);
6936 that.plotTree(elem, opt, animating);
6940 !animating && opt.onBeforePlotNode(node);
6941 this.plotNode(node, canvas, animating);
6942 !animating && opt.onAfterPlotNode(node);
6943 if(!opt.hideLabels && opt.withLabels && nodeAlpha >= 0.95)
6944 this.labels.plotLabel(canvas, node, opt);
6946 this.labels.hideLabel(node, false);
6948 this.labels.hideLabel(node, true);
6955 Plots a <Graph.Node>.
6959 node - (object) A <Graph.Node>.
6960 canvas - (object) A <Canvas> element.
6963 plotNode: function(node, canvas, animating) {
6964 var f = node.getData('type'),
6965 ctxObj = this.node.CanvasStyles;
6967 var width = node.getData('lineWidth'),
6968 color = node.getData('color'),
6969 alpha = node.getData('alpha'),
6970 ctx = canvas.getCtx();
6972 ctx.lineWidth = width;
6973 ctx.fillStyle = ctx.strokeStyle = color;
6974 ctx.globalAlpha = alpha;
6976 for(var s in ctxObj) {
6977 ctx[s] = node.getCanvasStyle(s);
6980 this.nodeTypes[f].render.call(this, node, canvas, animating);
6988 Plots a <Graph.Adjacence>.
6992 adj - (object) A <Graph.Adjacence>.
6993 canvas - (object) A <Canvas> instance.
6996 plotLine: function(adj, canvas, animating) {
6997 var f = adj.getData('type'),
6998 ctxObj = this.edge.CanvasStyles;
7000 var width = adj.getData('lineWidth'),
7001 color = adj.getData('color'),
7002 ctx = canvas.getCtx(),
7003 nodeFrom = adj.nodeFrom,
7004 nodeTo = adj.nodeTo;
7007 ctx.lineWidth = width;
7008 ctx.fillStyle = ctx.strokeStyle = color;
7009 ctx.globalAlpha = Math.min(nodeFrom.getData('alpha'),
7010 nodeTo.getData('alpha'),
7011 adj.getData('alpha'));
7013 for(var s in ctxObj) {
7014 ctx[s] = adj.getCanvasStyle(s);
7017 this.edgeTypes[f].render.call(this, adj, 0, canvas, animating);
7023 Method: plotBezierLine
7025 Plots a <MultiGraph.Adjacence>.
7028 adj - (object) A <MultiGraph.Adjacence>.
7029 pos - (int) Position of this line
7030 total - (int) Total number of lines contained within this batch
7032 plotBezierLine: function(adj, pos, total, canvas, animating) {
7033 var f = adj.getData('type'),
7034 ctxObj = this.edge.CanvasStyles;
7036 var width = adj.getData('lineWidth'),
7037 color = adj.getData('color'),
7038 ctx = canvas.getCtx(),
7039 nodeFrom = adj.nodeFrom,
7040 nodeTo = adj.nodeTo;
7043 ctx.lineWidth = width;
7044 ctx.fillStyle = ctx.strokeStyle = color;
7045 ctx.globalAlpha = Math.min(nodeFrom.getData('alpha'),
7046 nodeTo.getData('alpha'),
7047 adj.getData('alpha'));
7049 for(var s in ctxObj) {
7050 ctx[s] = adj.getCanvasStyle(s);
7053 var alpha = parseInt(pos,10);
7054 var start = (0.5-(total/2));
7057 this.edgeTypes[f].render.call(this, adj, alpha, canvas, animating);
7066 * File: Graph.Label.js
7073 An interface for plotting/hiding/showing labels.
7077 This is a generic interface for plotting/hiding/showing labels.
7078 The <Graph.Label> interface is implemented in multiple ways to provide
7079 different label types.
7081 For example, the Graph.Label interface is implemented as <Graph.Label.HTML> to provide
7082 HTML label elements. Also we provide the <Graph.Label.SVG> interface for SVG type labels.
7083 The <Graph.Label.Native> interface implements these methods with the native Canvas text rendering functions.
7085 All subclasses (<Graph.Label.HTML>, <Graph.Label.SVG> and <Graph.Label.Native>) implement the method plotLabel.
7088 MultiGraph.Label = {};
7091 Class: Graph.Label.Native
7093 Implements labels natively, using the Canvas text API.
7095 MultiGraph.Label.Native = new Class({
7096 initialize: function(viz) {
7103 Plots a label for a given node.
7107 canvas - (object) A <Canvas> instance.
7108 node - (object) A <Graph.Node>.
7109 controller - (object) A configuration object.
7114 var viz = new $jit.Viz(options);
7115 var node = viz.graph.getNode('nodeId');
7116 viz.labels.plotLabel(viz.canvas, node, viz.config);
7119 plotLabel: function(canvas, node, controller) {
7120 var ctx = canvas.getCtx();
7121 var pos = node.pos.getc(true);
7123 ctx.font = node.getLabelData('style') + ' ' + node.getLabelData('size') + 'px ' + node.getLabelData('family');
7124 ctx.textAlign = node.getLabelData('textAlign');
7125 ctx.fillStyle = ctx.strokeStyle = node.getLabelData('color');
7126 ctx.textBaseline = node.getLabelData('textBaseline');
7128 this.renderLabel(canvas, node, controller);
7134 Does the actual rendering of the label in the canvas. The default
7135 implementation renders the label close to the position of the node, this
7136 method should be overriden to position the labels differently.
7140 canvas - A <Canvas> instance.
7141 node - A <Graph.Node>.
7142 controller - A configuration object. See also <Hypertree>, <RGraph>, <ST>.
7144 renderLabel: function(canvas, node, controller) {
7145 var ctx = canvas.getCtx();
7146 var pos = node.pos.getc(true);
7147 ctx.fillText(node.name, pos.x, pos.y + node.getData("height") / 2);
7153 Hides the corresponding <Graph.Node> label.
7157 node - (object) A <Graph.Node>. Can also be an array of <Graph.Nodes>.
7158 show - (boolean) If *true*, nodes will be shown. Otherwise nodes will be hidden.
7162 var rg = new $jit.Viz(options);
7163 viz.labels.hideLabel(viz.graph.getNode('someid'), false);
7166 hideLabel: function(node, show) {
7167 node = $.splat(node);
7168 var al = show ? false : true;
7169 $.each(node, function(n) {
7177 Class: Graph.Label.DOM
7179 Abstract Class implementing some DOM label methods.
7183 <Graph.Label.HTML> and <Graph.Label.SVG>.
7186 MultiGraph.Label.DOM = new Class({
7187 //A flag value indicating if node labels are being displayed or not.
7188 labelsHidden: false,
7190 labelContainer: false,
7191 //Label elements hash.
7195 Method: getLabelContainer
7197 Lazy fetcher for the label container.
7201 The label container DOM element.
7206 var viz = new $jit.Viz(options);
7207 var labelContainer = viz.labels.getLabelContainer();
7208 alert(labelContainer.innerHTML);
7211 getLabelContainer: function() {
7212 return this.labelContainer ?
7213 this.labelContainer :
7214 this.labelContainer = document.getElementById(this.viz.config.labelContainer);
7220 Lazy fetcher for the label element.
7224 id - (string) The label id (which is also a <Graph.Node> id).
7233 var viz = new $jit.Viz(options);
7234 var label = viz.labels.getLabel('someid');
7235 alert(label.innerHTML);
7239 getLabel: function(id) {
7240 return (id in this.labels && this.labels[id] != null) ?
7242 this.labels[id] = document.getElementById(id);
7248 Hides all labels (by hiding the label container).
7252 hide - (boolean) A boolean value indicating if the label container must be hidden or not.
7256 var viz = new $jit.Viz(options);
7257 rg.labels.hideLabels(true);
7261 hideLabels: function (hide) {
7262 var container = this.getLabelContainer();
7264 container.style.display = 'none';
7266 container.style.display = '';
7267 this.labelsHidden = hide;
7273 Clears the label container.
7275 Useful when using a new visualization with the same canvas element/widget.
7279 force - (boolean) Forces deletion of all labels.
7283 var viz = new $jit.Viz(options);
7284 viz.labels.clearLabels();
7287 clearLabels: function(force) {
7288 for(var id in this.labels) {
7289 if (force || !this.viz.graph.hasNode(id)) {
7290 this.disposeLabel(id);
7291 delete this.labels[id];
7297 Method: disposeLabel
7303 id - (string) A label id (which generally is also a <Graph.Node> id).
7307 var viz = new $jit.Viz(options);
7308 viz.labels.disposeLabel('labelid');
7311 disposeLabel: function(id) {
7312 var elem = this.getLabel(id);
7313 if(elem && elem.parentNode) {
7314 elem.parentNode.removeChild(elem);
7321 Hides the corresponding <Graph.Node> label.
7325 node - (object) A <Graph.Node>. Can also be an array of <Graph.Nodes>.
7326 show - (boolean) If *true*, nodes will be shown. Otherwise nodes will be hidden.
7330 var rg = new $jit.Viz(options);
7331 viz.labels.hideLabel(viz.graph.getNode('someid'), false);
7334 hideLabel: function(node, show) {
7335 node = $.splat(node);
7336 var st = show ? "" : "none", lab, that = this;
7337 $.each(node, function(n) {
7338 lab = that.getLabel(n.id);
7340 lab.style.display = st;
7348 Returns _true_ or _false_ if the label for the node is contained in the canvas dom element or not.
7352 pos - A <Complex> instance (I'm doing duck typing here so any object with _x_ and _y_ parameters will do).
7353 canvas - A <Canvas> instance.
7357 A boolean value specifying if the label is contained in the <Canvas> DOM element or not.
7360 fitsInCanvas: function(pos, canvas) {
7361 var size = canvas.getSize();
7362 if (pos.w && pos.h) {
7363 if (pos.x >= size.width || pos.x < -pos.w
7364 || pos.y >= size.height || pos.y < -pos.h) return false;
7367 if (pos.x >= size.width || pos.x < 0
7368 || pos.y >= size.height || pos.y < 0) return false;
7375 Class: Graph.Label.HTML
7377 Implements HTML labels.
7381 All <Graph.Label.DOM> methods.
7384 MultiGraph.Label.HTML = new Class({
7385 Implements: MultiGraph.Label.DOM,
7390 Plots a label for a given node.
7394 canvas - (object) A <Canvas> instance.
7395 node - (object) A <Graph.Node>.
7396 controller - (object) A configuration object.
7401 var viz = new $jit.Viz(options);
7402 var node = viz.graph.getNode('nodeId');
7403 viz.labels.plotLabel(viz.canvas, node, viz.config);
7408 plotLabel: function(canvas, node, controller) {
7409 var id = node.id, tag = this.getLabel(id);
7411 if(!tag && !(tag = document.getElementById(id))) {
7412 tag = document.createElement('div');
7413 var container = this.getLabelContainer();
7415 tag.className = 'node';
7416 tag.style.position = 'absolute';
7417 controller.onCreateLabel(tag, node);
7418 container.appendChild(tag);
7419 this.labels[node.id] = tag;
7422 this.placeLabel(tag, node, controller);
7427 Class: Graph.Label.SVG
7429 Implements SVG labels.
7433 All <Graph.Label.DOM> methods.
7435 MultiGraph.Label.SVG = new Class({
7436 Implements: MultiGraph.Label.DOM,
7441 Plots a label for a given node.
7445 canvas - (object) A <Canvas> instance.
7446 node - (object) A <Graph.Node>.
7447 controller - (object) A configuration object.
7452 var viz = new $jit.Viz(options);
7453 var node = viz.graph.getNode('nodeId');
7454 viz.labels.plotLabel(viz.canvas, node, viz.config);
7459 plotLabel: function(canvas, node, controller) {
7460 var id = node.id, tag = this.getLabel(id);
7461 if(!tag && !(tag = document.getElementById(id))) {
7462 var ns = 'http://www.w3.org/2000/svg';
7463 tag = document.createElementNS(ns, 'svg:text');
7464 var tspan = document.createElementNS(ns, 'svg:tspan');
7465 tag.appendChild(tspan);
7466 var container = this.getLabelContainer();
7467 tag.setAttribute('id', id);
7468 tag.setAttribute('class', 'node');
7469 container.appendChild(tag);
7470 controller.onCreateLabel(tag, node);
7471 this.labels[node.id] = tag;
7473 this.placeLabel(tag, node, controller);
7480 * File: MultiLoader.js
7487 Provides methods for loading and serving JSON data.
7490 construct: function(json) {
7491 var ans = new MultiGraph(this.graphOptions, this.config.Node, this.config.Edge, this.config.Label);
7493 (function (ans, json) {
7494 var getNode = function(id) {
7495 for(var i=0, l=json.length; i<l; i++) {
7496 if(json[i].id == id) {
7500 // The node was not defined in the JSON
7506 return ans.addNode(newNode);
7509 for(var i=0, l=json.length; i<l; i++) {
7510 ans.addNode(json[i]);
7511 var adj = json[i].adjacencies;
7513 for(var j=0, lj=adj.length; j<lj; j++) {
7514 var node = adj[j], data = {};
7515 data = $.merge(node.data, {});
7517 ans.addAdjacence(json[i], getNode(node), data);
7529 Loads a JSON structure to the visualization. The JSON structure can be a JSON *tree* or *graph* structure.
7531 A JSON tree or graph structure consists of nodes, each having as properties
7533 id - (string) A unique identifier for the node
7534 name - (string) A node's name
7535 data - (object) The data optional property contains a hash (i.e {})
7536 where you can store all the information you want about this node.
7538 For JSON *Tree* structures, there's an extra optional property *children* of type Array which contains the node's children.
7544 "id": "aUniqueIdentifier",
7545 "name": "usually a nodes name",
7547 "some key": "some value",
7548 "some other key": "some other value"
7550 "children": [ *other nodes or empty* ]
7554 JSON *Graph* structures consist of an array of nodes, each specifying the nodes to which the current node is connected.
7555 For JSON *Graph* structures, the *children* property is replaced by the *adjacencies* property.
7557 There are two types of *Graph* structures, *simple* and *extended* graph structures.
7559 For *simple* Graph structures, the adjacencies property contains an array of strings, each specifying the
7560 id of the node connected to the main node.
7567 "id": "aUniqueIdentifier",
7568 "name": "usually a nodes name",
7570 "some key": "some value",
7571 "some other key": "some other value"
7573 "adjacencies": ["anotherUniqueIdentifier", "yetAnotherUniqueIdentifier", 'etc']
7576 'other nodes go here...'
7580 For *extended Graph structures*, the adjacencies property contains an array of Adjacency objects that have as properties
7582 nodeTo - (string) The other node connected by this adjacency.
7583 data - (object) A data property, where we can store custom key/value information.
7590 "id": "aUniqueIdentifier",
7591 "name": "usually a nodes name",
7593 "some key": "some value",
7594 "some other key": "some other value"
7599 data: {} //put whatever you want here
7601 'other adjacencies go here...'
7604 'other nodes go here...'
7608 About the data property:
7610 As described before, you can store custom data in the *data* property of JSON *nodes* and *adjacencies*.
7611 You can use almost any string as key for the data object. Some keys though are reserved by the toolkit, and
7612 have special meanings. This is the case for keys starting with a dollar sign, for example, *$width*.
7614 For JSON *node* objects, adding dollar prefixed properties that match the names of the options defined in
7615 <Options.Node> will override the general value for that option with that particular value. For this to work
7616 however, you do have to set *overridable = true* in <Options.Node>.
7618 The same thing is true for JSON adjacencies. Dollar prefixed data properties will alter values set in <Options.Edge>
7619 if <Options.Edge> has *overridable = true*.
7621 When loading JSON data into TreeMaps, the *data* property must contain a value for the *$area* key,
7622 since this is the value which will be taken into account when creating the layout.
7623 The same thing goes for the *$color* parameter.
7625 In JSON Nodes you can use also *$label-* prefixed properties to refer to <Options.Label> properties. For example,
7626 *$label-size* will refer to <Options.Label> size property. Also, in JSON nodes and adjacencies you can set
7627 canvas specific properties individually by using the *$canvas-* prefix. For example, *$canvas-shadowBlur* will refer
7628 to the *shadowBlur* property.
7630 These properties can also be accessed after loading the JSON data from <Graph.Nodes> and <Graph.Adjacences>
7631 by using <Accessors>. For more information take a look at the <Graph> and <Accessors> documentation.
7633 Finally, these properties can also be used to create advanced animations like with <Options.NodeStyles>. For more
7634 information about creating animations please take a look at the <Graph.Plot> and <Graph.Plot.animate> documentation.
7636 loadJSON Parameters:
7638 json - A JSON Tree or Graph structure.
7639 i - For Graph structures only. Sets the indexed node as root for the visualization.
7642 loadJSON: function(json, i) {
7644 //if they're canvas labels erase them.
7645 if(this.labels && this.labels.clearLabels) {
7646 this.labels.clearLabels(true);
7648 this.graph = this.construct(json);
7649 if($.type(json) != 'array'){
7650 this.root = json.id;
7652 this.root = json[i? i : 0].id;
7659 Returns a JSON tree/graph structure from the visualization's <Graph>.
7660 See <MultiLoader.loadJSON> for the graph formats available.
7664 <MultiLoader.loadJSON>
7668 type - (string) Default's "tree". The type of the JSON structure to be returned.
7669 Possible options are "tree" or "graph".
7671 toJSON: function(type) {
7672 type = type || "tree";
7673 if(type == 'tree') {
7675 var rootNode = this.graph.getNode(this.root);
7676 var ans = (function recTree(node) {
7679 ans.name = node.name;
7680 ans.data = node.data;
7682 node.eachSubnode(function(n) {
7683 ch.push(recTree(n));
7691 var T = !!this.graph.getNode(this.root).visited;
7692 this.graph.eachNode(function(node) {
7694 ansNode.id = node.id;
7695 ansNode.name = node.name;
7696 ansNode.data = node.data;
7698 node.eachAdjacency(function(adj) {
7699 var nodeTo = adj.nodeTo;
7700 if(!!nodeTo.visited === T) {
7702 ansAdj.nodeTo = nodeTo.id;
7703 ansAdj.data = adj.data;
7707 ansNode.adjacencies = adjs;
7726 A Graph Class that provides useful manipulation functions. You can find more manipulation methods in the <Graph.Util> object.
7728 An instance of this class can be accessed by using the *graph* parameter of any tree or graph visualization.
7733 //create new visualization
7734 var viz = new $jit.Viz(options);
7738 viz.graph; //<Graph> instance
7743 The following <Graph.Util> methods are implemented in <Graph>
7745 - <Graph.Util.getNode>
7746 - <Graph.Util.eachNode>
7747 - <Graph.Util.computeLevels>
7748 - <Graph.Util.eachBFS>
7749 - <Graph.Util.clean>
7750 - <Graph.Util.getClosestNodeToPos>
7751 - <Graph.Util.getClosestNodeToOrigin>
7755 $jit.Graph = new Class({
7757 initialize: function(opt, Node, Edge, Label) {
7758 var innerOptions = {
7765 this.opt = $.merge(innerOptions, opt || {});
7769 //add nodeList methods
7772 for(var p in Accessors) {
7773 that.nodeList[p] = (function(p) {
7775 var args = Array.prototype.slice.call(arguments);
7776 that.eachNode(function(n) {
7777 n[p].apply(n, args);
7788 Returns a <Graph.Node> by *id*.
7792 id - (string) A <Graph.Node> id.
7797 var node = graph.getNode('nodeId');
7800 getNode: function(id) {
7801 if(this.hasNode(id)) return this.nodes[id];
7808 An alias for <Graph.Util.getNode>. Returns a node by *id*.
7812 id - (string) A <Graph.Node> id.
7817 var node = graph.get('nodeId');
7821 return this.getNode(id);
7827 Returns a <Graph.Node> by *name*.
7831 name - (string) A <Graph.Node> name.
7836 var node = graph.getByName('someName');
7839 getByName: function(name) {
7840 for(var id in this.nodes) {
7841 var n = this.nodes[id];
7842 if(n.name == name) return n;
7848 Method: getAdjacence
7850 Returns a <Graph.Adjacence> object connecting nodes with ids *id* and *id2*.
7854 id - (string) A <Graph.Node> id.
7855 id2 - (string) A <Graph.Node> id.
7857 getAdjacence: function (id, id2) {
7858 if(id in this.edges) {
7859 return this.edges[id][id2];
7871 obj - An object with the properties described below
7873 id - (string) A node id
7874 name - (string) A node's name
7875 data - (object) A node's data hash
7881 addNode: function(obj) {
7882 if(!this.nodes[obj.id]) {
7883 var edges = this.edges[obj.id] = {};
7884 this.nodes[obj.id] = new Graph.Node($.extend({
7887 'data': $.merge(obj.data || {}, {}),
7888 'adjacencies': edges
7895 return this.nodes[obj.id];
7899 Method: addAdjacence
7901 Connects nodes specified by *obj* and *obj2*. If not found, nodes are created.
7905 obj - (object) A <Graph.Node> object.
7906 obj2 - (object) Another <Graph.Node> object.
7907 data - (object) A data object. Used to store some extra information in the <Graph.Adjacence> object created.
7911 <Graph.Node>, <Graph.Adjacence>
7913 addAdjacence: function (obj, obj2, data) {
7914 if(!this.hasNode(obj.id)) { this.addNode(obj); }
7915 if(!this.hasNode(obj2.id)) { this.addNode(obj2); }
7916 obj = this.nodes[obj.id]; obj2 = this.nodes[obj2.id];
7917 if(!obj.adjacentTo(obj2)) {
7918 var adjsObj = this.edges[obj.id] = this.edges[obj.id] || {};
7919 var adjsObj2 = this.edges[obj2.id] = this.edges[obj2.id] || {};
7920 adjsObj[obj2.id] = adjsObj2[obj.id] = new Graph.Adjacence(obj, obj2, data, this.Edge, this.Label);
7921 return adjsObj[obj2.id];
7923 return this.edges[obj.id][obj2.id];
7929 Removes a <Graph.Node> matching the specified *id*.
7933 id - (string) A node's id.
7936 removeNode: function(id) {
7937 if(this.hasNode(id)) {
7938 delete this.nodes[id];
7939 var adjs = this.edges[id];
7940 for(var to in adjs) {
7941 delete this.edges[to][id];
7943 delete this.edges[id];
7948 Method: removeAdjacence
7950 Removes a <Graph.Adjacence> matching *id1* and *id2*.
7954 id1 - (string) A <Graph.Node> id.
7955 id2 - (string) A <Graph.Node> id.
7957 removeAdjacence: function(id1, id2) {
7958 delete this.edges[id1][id2];
7959 delete this.edges[id2][id1];
7965 Returns a boolean indicating if the node belongs to the <Graph> or not.
7969 id - (string) Node id.
7971 hasNode: function(id) {
7972 return id in this.nodes;
7981 empty: function() { this.nodes = {}; this.edges = {};}
7985 var Graph = $jit.Graph;
7990 Defines a set of methods for data, canvas and label styles manipulation implemented by <Graph.Node> and <Graph.Adjacence> instances.
7996 var getDataInternal = function(prefix, prop, type, force, prefixConfig) {
7998 type = type || 'current';
7999 prefix = "$" + (prefix ? prefix + "-" : "");
8001 if(type == 'current') {
8003 } else if(type == 'start') {
8004 data = this.startData;
8005 } else if(type == 'end') {
8006 data = this.endData;
8009 var dollar = prefix + prop;
8012 return data[dollar];
8015 if(!this.Config.overridable)
8016 return prefixConfig[prop] || 0;
8018 return (dollar in data) ?
8019 data[dollar] : ((dollar in this.data) ? this.data[dollar] : (prefixConfig[prop] || 0));
8022 var setDataInternal = function(prefix, prop, value, type) {
8023 type = type || 'current';
8024 prefix = '$' + (prefix ? prefix + '-' : '');
8028 if(type == 'current') {
8030 } else if(type == 'start') {
8031 data = this.startData;
8032 } else if(type == 'end') {
8033 data = this.endData;
8036 data[prefix + prop] = value;
8039 var removeDataInternal = function(prefix, properties) {
8040 prefix = '$' + (prefix ? prefix + '-' : '');
8042 $.each(properties, function(prop) {
8043 var pref = prefix + prop;
8044 delete that.data[pref];
8045 delete that.endData[pref];
8046 delete that.startData[pref];
8054 Returns the specified data value property.
8055 This is useful for querying special/reserved <Graph.Node> data properties
8056 (i.e dollar prefixed properties).
8060 prop - (string) The name of the property. The dollar sign is not needed. For
8061 example *getData(width)* will return *data.$width*.
8062 type - (string) The type of the data property queried. Default's "current". You can access *start* and *end*
8063 data properties also. These properties are used when making animations.
8064 force - (boolean) Whether to obtain the true value of the property (equivalent to
8065 *data.$prop*) or to check for *node.overridable = true* first.
8069 The value of the dollar prefixed property or the global Node/Edge property
8070 value if *overridable=false*
8074 node.getData('width'); //will return node.data.$width if Node.overridable=true;
8077 getData: function(prop, type, force) {
8078 return getDataInternal.call(this, "", prop, type, force, this.Config);
8085 Sets the current data property with some specific value.
8086 This method is only useful for reserved (dollar prefixed) properties.
8090 prop - (string) The name of the property. The dollar sign is not necessary. For
8091 example *setData(width)* will set *data.$width*.
8092 value - (mixed) The value to store.
8093 type - (string) The type of the data property to store. Default's "current" but
8094 can also be "start" or "end".
8099 node.setData('width', 30);
8102 If we were to make an animation of a node/edge width then we could do
8105 var node = viz.getNode('nodeId');
8106 //set start and end values
8107 node.setData('width', 10, 'start');
8108 node.setData('width', 30, 'end');
8109 //will animate nodes width property
8111 modes: ['node-property:width'],
8116 setData: function(prop, value, type) {
8117 setDataInternal.call(this, "", prop, value, type);
8123 Convenience method to set multiple data values at once.
8127 types - (array|string) A set of 'current', 'end' or 'start' values.
8128 obj - (object) A hash containing the names and values of the properties to be altered.
8132 node.setDataset(['current', 'end'], {
8134 'color': ['#fff', '#ccc']
8137 node.setDataset('end', {
8148 setDataset: function(types, obj) {
8149 types = $.splat(types);
8150 for(var attr in obj) {
8151 for(var i=0, val = $.splat(obj[attr]), l=types.length; i<l; i++) {
8152 this.setData(attr, val[i], types[i]);
8160 Remove data properties.
8164 One or more property names as arguments. The dollar sign is not needed.
8168 node.removeData('width'); //now the default width value is returned
8171 removeData: function() {
8172 removeDataInternal.call(this, "", Array.prototype.slice.call(arguments));
8176 Method: getCanvasStyle
8178 Returns the specified canvas style data value property. This is useful for
8179 querying special/reserved <Graph.Node> canvas style data properties (i.e.
8180 dollar prefixed properties that match with $canvas-<name of canvas style>).
8184 prop - (string) The name of the property. The dollar sign is not needed. For
8185 example *getCanvasStyle(shadowBlur)* will return *data[$canvas-shadowBlur]*.
8186 type - (string) The type of the data property queried. Default's *current*. You can access *start* and *end*
8187 data properties also.
8191 node.getCanvasStyle('shadowBlur');
8198 getCanvasStyle: function(prop, type, force) {
8199 return getDataInternal.call(
8200 this, 'canvas', prop, type, force, this.Config.CanvasStyles);
8204 Method: setCanvasStyle
8206 Sets the canvas style data property with some specific value.
8207 This method is only useful for reserved (dollar prefixed) properties.
8211 prop - (string) Name of the property. Can be any canvas property like 'shadowBlur', 'shadowColor', 'strokeStyle', etc.
8212 value - (mixed) The value to set to the property.
8213 type - (string) Default's *current*. Whether to set *start*, *current* or *end* type properties.
8218 node.setCanvasStyle('shadowBlur', 30);
8221 If we were to make an animation of a node/edge shadowBlur canvas style then we could do
8224 var node = viz.getNode('nodeId');
8225 //set start and end values
8226 node.setCanvasStyle('shadowBlur', 10, 'start');
8227 node.setCanvasStyle('shadowBlur', 30, 'end');
8228 //will animate nodes canvas style property for nodes
8230 modes: ['node-style:shadowBlur'],
8237 <Accessors.setData>.
8239 setCanvasStyle: function(prop, value, type) {
8240 setDataInternal.call(this, 'canvas', prop, value, type);
8244 Method: setCanvasStyles
8246 Convenience method to set multiple styles at once.
8250 types - (array|string) A set of 'current', 'end' or 'start' values.
8251 obj - (object) A hash containing the names and values of the properties to be altered.
8255 <Accessors.setDataset>.
8257 setCanvasStyles: function(types, obj) {
8258 types = $.splat(types);
8259 for(var attr in obj) {
8260 for(var i=0, val = $.splat(obj[attr]), l=types.length; i<l; i++) {
8261 this.setCanvasStyle(attr, val[i], types[i]);
8267 Method: removeCanvasStyle
8269 Remove canvas style properties from data.
8273 A variable number of canvas style strings.
8277 <Accessors.removeData>.
8279 removeCanvasStyle: function() {
8280 removeDataInternal.call(this, 'canvas', Array.prototype.slice.call(arguments));
8284 Method: getLabelData
8286 Returns the specified label data value property. This is useful for
8287 querying special/reserved <Graph.Node> label options (i.e.
8288 dollar prefixed properties that match with $label-<name of label style>).
8292 prop - (string) The name of the property. The dollar sign prefix is not needed. For
8293 example *getLabelData(size)* will return *data[$label-size]*.
8294 type - (string) The type of the data property queried. Default's *current*. You can access *start* and *end*
8295 data properties also.
8299 <Accessors.getData>.
8301 getLabelData: function(prop, type, force) {
8302 return getDataInternal.call(
8303 this, 'label', prop, type, force, this.Label);
8307 Method: setLabelData
8309 Sets the current label data with some specific value.
8310 This method is only useful for reserved (dollar prefixed) properties.
8314 prop - (string) Name of the property. Can be any canvas property like 'shadowBlur', 'shadowColor', 'strokeStyle', etc.
8315 value - (mixed) The value to set to the property.
8316 type - (string) Default's *current*. Whether to set *start*, *current* or *end* type properties.
8321 node.setLabelData('size', 30);
8324 If we were to make an animation of a node label size then we could do
8327 var node = viz.getNode('nodeId');
8328 //set start and end values
8329 node.setLabelData('size', 10, 'start');
8330 node.setLabelData('size', 30, 'end');
8331 //will animate nodes label size
8333 modes: ['label-property:size'],
8340 <Accessors.setData>.
8342 setLabelData: function(prop, value, type) {
8343 setDataInternal.call(this, 'label', prop, value, type);
8347 Method: setLabelDataset
8349 Convenience function to set multiple label data at once.
8353 types - (array|string) A set of 'current', 'end' or 'start' values.
8354 obj - (object) A hash containing the names and values of the properties to be altered.
8358 <Accessors.setDataset>.
8360 setLabelDataset: function(types, obj) {
8361 types = $.splat(types);
8362 for(var attr in obj) {
8363 for(var i=0, val = $.splat(obj[attr]), l=types.length; i<l; i++) {
8364 this.setLabelData(attr, val[i], types[i]);
8370 Method: removeLabelData
8372 Remove label properties from data.
8376 A variable number of label property strings.
8380 <Accessors.removeData>.
8382 removeLabelData: function() {
8383 removeDataInternal.call(this, 'label', Array.prototype.slice.call(arguments));
8395 <Accessors> methods.
8397 The following <Graph.Util> methods are implemented by <Graph.Node>
8399 - <Graph.Util.eachAdjacency>
8400 - <Graph.Util.eachLevel>
8401 - <Graph.Util.eachSubgraph>
8402 - <Graph.Util.eachSubnode>
8403 - <Graph.Util.anySubnode>
8404 - <Graph.Util.getSubnodes>
8405 - <Graph.Util.getParents>
8406 - <Graph.Util.isDescendantOf>
8408 Graph.Node = new Class({
8410 initialize: function(opt, klass, Node, Edge, Label) {
8411 var innerOptions = {
8429 'startPos': new klass,
8433 $.extend(this, $.extend(innerOptions, opt));
8434 this.Config = this.Node = Node;
8442 Indicates if the node is adjacent to the node specified by id
8446 id - (string) A node id.
8450 node.adjacentTo('nodeId') == true;
8453 adjacentTo: function(node) {
8454 return node.id in this.adjacencies;
8458 Method: adjacentWithDirectionTo
8460 Indicates if the node has a directed edge to the node specified by id
8464 id - (string) A node id.
8468 node.adjacentWithDirectionTo('nodeId') == true;
8471 adjacentWithDirectionTo: function(node) {
8472 var areNeighbors = node.id in this.adjacencies;
8473 if (!areNeighbors) {
8477 var direction = this.adjacencies[node.id].data.$direction;
8478 return direction[0] === this.id ;
8482 Method: getAdjacency
8484 Returns a <Graph.Adjacence> object connecting the current <Graph.Node> and the node having *id* as id.
8488 id - (string) A node id.
8490 getAdjacency: function(id) {
8491 return this.adjacencies[id];
8497 Returns the position of the node.
8501 type - (string) Default's *current*. Possible values are "start", "end" or "current".
8505 A <Complex> or <Polar> instance.
8509 var pos = node.getPos('end');
8512 getPos: function(type) {
8513 type = type || "current";
8514 if(type == "current") {
8516 } else if(type == "end") {
8518 } else if(type == "start") {
8519 return this.startPos;
8525 Sets the node's position.
8529 value - (object) A <Complex> or <Polar> instance.
8530 type - (string) Default's *current*. Possible values are "start", "end" or "current".
8534 node.setPos(new $jit.Complex(0, 0), 'end');
8537 setPos: function(value, type) {
8538 type = type || "current";
8540 if(type == "current") {
8542 } else if(type == "end") {
8544 } else if(type == "start") {
8545 pos = this.startPos;
8551 Graph.Node.implement(Accessors);
8554 Class: Graph.Adjacence
8556 A <Graph> adjacence (or edge) connecting two <Graph.Nodes>.
8560 <Accessors> methods.
8564 <Graph>, <Graph.Node>
8568 nodeFrom - A <Graph.Node> connected by this edge.
8569 nodeTo - Another <Graph.Node> connected by this edge.
8570 data - Node data property containing a hash (i.e {}) with custom options.
8572 Graph.Adjacence = new Class({
8574 initialize: function(nodeFrom, nodeTo, data, Edge, Label) {
8575 this.nodeFrom = nodeFrom;
8576 this.nodeTo = nodeTo;
8577 this.data = data || {};
8578 this.startData = {};
8580 this.Config = this.Edge = Edge;
8585 Graph.Adjacence.implement(Accessors);
8590 <Graph> traversal and processing utility object.
8594 For your convenience some of these methods have also been appended to <Graph> and <Graph.Node> classes.
8600 For internal use only. Provides a filtering function based on flags.
8602 filter: function(param) {
8603 if(!param || !($.type(param) == 'string')) return function() { return true; };
8604 var props = param.split(" ");
8605 return function(elem) {
8606 for(var i=0; i<props.length; i++) {
8607 if(elem[props[i]]) {
8617 Returns a <Graph.Node> by *id*.
8619 Also implemented by:
8625 graph - (object) A <Graph> instance.
8626 id - (string) A <Graph.Node> id.
8631 $jit.Graph.Util.getNode(graph, 'nodeid');
8633 graph.getNode('nodeid');
8636 getNode: function(graph, id) {
8637 return graph.nodes[id];
8643 Iterates over <Graph> nodes performing an *action*.
8645 Also implemented by:
8651 graph - (object) A <Graph> instance.
8652 action - (function) A callback function having a <Graph.Node> as first formal parameter.
8656 $jit.Graph.Util.eachNode(graph, function(node) {
8660 graph.eachNode(function(node) {
8665 eachNode: function(graph, action, flags) {
8666 var filter = this.filter(flags);
8667 for(var i in graph.nodes) {
8668 if(filter(graph.nodes[i])) action(graph.nodes[i]);
8675 Iterates over <Graph> nodes performing an *action*. It's an alias for <Graph.Util.eachNode>.
8677 Also implemented by:
8683 graph - (object) A <Graph> instance.
8684 action - (function) A callback function having a <Graph.Node> as first formal parameter.
8688 $jit.Graph.Util.each(graph, function(node) {
8692 graph.each(function(node) {
8697 each: function(graph, action, flags) {
8698 this.eachNode(graph, action, flags);
8702 Method: eachAdjacency
8704 Iterates over <Graph.Node> adjacencies applying the *action* function.
8706 Also implemented by:
8712 node - (object) A <Graph.Node>.
8713 action - (function) A callback function having <Graph.Adjacence> as first formal parameter.
8717 $jit.Graph.Util.eachAdjacency(node, function(adj) {
8718 alert(adj.nodeTo.name);
8721 node.eachAdjacency(function(adj) {
8722 alert(adj.nodeTo.name);
8726 eachAdjacency: function(node, action, flags) {
8727 var adj = node.adjacencies, filter = this.filter(flags);
8728 for(var id in adj) {
8731 if(a.nodeFrom != node) {
8732 var tmp = a.nodeFrom;
8733 a.nodeFrom = a.nodeTo;
8742 Method: computeLevels
8744 Performs a BFS traversal setting the correct depth for each node.
8746 Also implemented by:
8752 The depth of each node can then be accessed by
8757 graph - (object) A <Graph>.
8758 id - (string) A starting node id for the BFS traversal.
8759 startDepth - (optional|number) A minimum depth value. Default's 0.
8762 computeLevels: function(graph, id, startDepth, flags) {
8763 startDepth = startDepth || 0;
8764 var filter = this.filter(flags);
8765 this.eachNode(graph, function(elem) {
8769 var root = graph.getNode(id);
8770 root._depth = startDepth;
8772 while(queue.length != 0) {
8773 var node = queue.pop();
8775 this.eachAdjacency(node, function(adj) {
8777 if(n._flag == false && filter(n) && !adj._hiding) {
8778 if(n._depth < 0) n._depth = node._depth + 1 + startDepth;
8788 Performs a BFS traversal applying *action* to each <Graph.Node>.
8790 Also implemented by:
8796 graph - (object) A <Graph>.
8797 id - (string) A starting node id for the BFS traversal.
8798 action - (function) A callback function having a <Graph.Node> as first formal parameter.
8802 $jit.Graph.Util.eachBFS(graph, 'mynodeid', function(node) {
8806 graph.eachBFS('mynodeid', function(node) {
8811 eachBFS: function(graph, id, action, flags) {
8812 var filter = this.filter(flags);
8814 var queue = [graph.getNode(id)];
8815 while(queue.length != 0) {
8816 var node = queue.pop();
8819 action(node, node._depth);
8820 this.eachAdjacency(node, function(adj) {
8822 if(n._flag == false && filter(n) && !adj._hiding) {
8833 Iterates over a node's subgraph applying *action* to the nodes of relative depth between *levelBegin* and *levelEnd*.
8834 In case you need to break the iteration, *action* should return false.
8836 Also implemented by:
8842 node - (object) A <Graph.Node>.
8843 levelBegin - (number) A relative level value.
8844 levelEnd - (number) A relative level value.
8845 action - (function) A callback function having a <Graph.Node> as first formal parameter.
8848 eachLevel: function(node, levelBegin, levelEnd, action, flags) {
8849 var d = node._depth, filter = this.filter(flags), that = this, shouldContinue = true;
8850 levelEnd = levelEnd === false? Number.MAX_VALUE -d : levelEnd;
8851 (function loopLevel(node, levelBegin, levelEnd) {
8852 if(!shouldContinue) return;
8853 var d = node._depth, ret;
8854 if(d >= levelBegin && d <= levelEnd && filter(node)) ret = action(node, d);
8855 if(typeof ret !== "undefined") shouldContinue = ret;
8856 if(shouldContinue && d < levelEnd) {
8857 that.eachAdjacency(node, function(adj) {
8859 if(n._depth > d) loopLevel(n, levelBegin, levelEnd);
8862 })(node, levelBegin + d, levelEnd + d);
8866 Method: eachSubgraph
8868 Iterates over a node's children recursively.
8870 Also implemented by:
8875 node - (object) A <Graph.Node>.
8876 action - (function) A callback function having a <Graph.Node> as first formal parameter.
8880 $jit.Graph.Util.eachSubgraph(node, function(node) {
8884 node.eachSubgraph(function(node) {
8889 eachSubgraph: function(node, action, flags) {
8890 this.eachLevel(node, 0, false, action, flags);
8896 Iterates over a node's children (without deeper recursion).
8898 Also implemented by:
8903 node - (object) A <Graph.Node>.
8904 action - (function) A callback function having a <Graph.Node> as first formal parameter.
8908 $jit.Graph.Util.eachSubnode(node, function(node) {
8912 node.eachSubnode(function(node) {
8917 eachSubnode: function(node, action, flags) {
8918 this.eachLevel(node, 1, 1, action, flags);
8924 Returns *true* if any subnode matches the given condition.
8926 Also implemented by:
8931 node - (object) A <Graph.Node>.
8932 cond - (function) A callback function returning a Boolean instance. This function has as first formal parameter a <Graph.Node>.
8936 $jit.Graph.Util.anySubnode(node, function(node) { return node.name == "mynodename"; });
8938 node.anySubnode(function(node) { return node.name == 'mynodename'; });
8941 anySubnode: function(node, cond, flags) {
8943 cond = cond || $.lambda(true);
8944 var c = $.type(cond) == 'string'? function(n) { return n[cond]; } : cond;
8945 this.eachSubnode(node, function(elem) {
8946 if(c(elem)) flag = true;
8954 Collects all subnodes for a specified node.
8955 The *level* parameter filters nodes having relative depth of *level* from the root node.
8957 Also implemented by:
8962 node - (object) A <Graph.Node>.
8963 level - (optional|number) Default's *0*. A starting relative depth for collecting nodes.
8969 getSubnodes: function(node, level, flags) {
8970 var ans = [], that = this;
8972 var levelStart, levelEnd;
8973 if($.type(level) == 'array') {
8974 levelStart = level[0];
8975 levelEnd = level[1];
8978 levelEnd = Number.MAX_VALUE - node._depth;
8980 this.eachLevel(node, levelStart, levelEnd, function(n) {
8990 Returns an Array of <Graph.Nodes> which are parents of the given node.
8992 Also implemented by:
8997 node - (object) A <Graph.Node>.
9000 An Array of <Graph.Nodes>.
9004 var pars = $jit.Graph.Util.getParents(node);
9006 var pars = node.getParents();
9008 if(pars.length > 0) {
9009 //do stuff with parents
9013 getParents: function(node) {
9015 this.eachAdjacency(node, function(adj) {
9017 if(n._depth < node._depth) ans.push(n);
9023 Method: isDescendantOf
9025 Returns a boolean indicating if some node is descendant of the node with the given id.
9027 Also implemented by:
9033 node - (object) A <Graph.Node>.
9034 id - (string) A <Graph.Node> id.
9038 $jit.Graph.Util.isDescendantOf(node, "nodeid"); //true|false
9040 node.isDescendantOf('nodeid');//true|false
9043 isDescendantOf: function(node, id) {
9044 if(node.id == id) return true;
9045 var pars = this.getParents(node), ans = false;
9046 for ( var i = 0; !ans && i < pars.length; i++) {
9047 ans = ans || this.isDescendantOf(pars[i], id);
9055 Cleans flags from nodes.
9057 Also implemented by:
9062 graph - A <Graph> instance.
9064 clean: function(graph) { this.eachNode(graph, function(elem) { elem._flag = false; }); },
9067 Method: getClosestNodeToOrigin
9069 Returns the closest node to the center of canvas.
9071 Also implemented by:
9077 graph - (object) A <Graph> instance.
9078 prop - (optional|string) Default's 'current'. A <Graph.Node> position property. Possible properties are 'start', 'current' or 'end'.
9081 getClosestNodeToOrigin: function(graph, prop, flags) {
9082 return this.getClosestNodeToPos(graph, Polar.KER, prop, flags);
9086 Method: getClosestNodeToPos
9088 Returns the closest node to the given position.
9090 Also implemented by:
9096 graph - (object) A <Graph> instance.
9097 pos - (object) A <Complex> or <Polar> instance.
9098 prop - (optional|string) Default's *current*. A <Graph.Node> position property. Possible properties are 'start', 'current' or 'end'.
9101 getClosestNodeToPos: function(graph, pos, prop, flags) {
9103 prop = prop || 'current';
9104 pos = pos && pos.getc(true) || Complex.KER;
9105 var distance = function(a, b) {
9106 var d1 = a.x - b.x, d2 = a.y - b.y;
9107 return d1 * d1 + d2 * d2;
9109 this.eachNode(graph, function(elem) {
9110 node = (node == null || distance(elem.getPos(prop).getc(true), pos) < distance(
9111 node.getPos(prop).getc(true), pos)) ? elem : node;
9117 //Append graph methods to <Graph>
9118 $.each(['get', 'getNode', 'each', 'eachNode', 'computeLevels', 'eachBFS', 'clean', 'getClosestNodeToPos', 'getClosestNodeToOrigin'], function(m) {
9119 Graph.prototype[m] = function() {
9120 return Graph.Util[m].apply(Graph.Util, [this].concat(Array.prototype.slice.call(arguments)));
9124 //Append node methods to <Graph.Node>
9125 $.each(['eachAdjacency', 'eachLevel', 'eachSubgraph', 'eachSubnode', 'anySubnode', 'getSubnodes', 'getParents', 'isDescendantOf'], function(m) {
9126 Graph.Node.prototype[m] = function() {
9127 return Graph.Util[m].apply(Graph.Util, [this].concat(Array.prototype.slice.call(arguments)));
9135 * Implements base Tree and Graph layouts.
9139 * Implements base Tree and Graph layouts like Radial, Tree, etc.
9146 * Parent object for common layouts.
9149 var Layouts = $jit.Layouts = {};
9152 //Some util shared layout functions are defined here.
9156 compute: function(graph, prop, opt) {
9157 this.initializeLabel(opt);
9158 var label = this.label, style = label.style;
9159 graph.eachNode(function(n) {
9160 var autoWidth = n.getData('autoWidth'),
9161 autoHeight = n.getData('autoHeight');
9162 if(autoWidth || autoHeight) {
9163 //delete dimensions since these are
9164 //going to be overridden now.
9165 delete n.data.$width;
9166 delete n.data.$height;
9169 var width = n.getData('width'),
9170 height = n.getData('height');
9171 //reset label dimensions
9172 style.width = autoWidth? 'auto' : width + 'px';
9173 style.height = autoHeight? 'auto' : height + 'px';
9175 //TODO(nico) should let the user choose what to insert here.
9176 label.innerHTML = n.name;
9178 var offsetWidth = label.offsetWidth,
9179 offsetHeight = label.offsetHeight;
9180 var type = n.getData('type');
9181 if($.indexOf(['circle', 'square', 'triangle', 'star'], type) === -1) {
9182 n.setData('width', offsetWidth);
9183 n.setData('height', offsetHeight);
9185 var dim = offsetWidth > offsetHeight? offsetWidth : offsetHeight;
9186 n.setData('width', dim);
9187 n.setData('height', dim);
9188 n.setData('dim', dim);
9194 initializeLabel: function(opt) {
9196 this.label = document.createElement('div');
9197 document.body.appendChild(this.label);
9199 this.setLabelStyles(opt);
9202 setLabelStyles: function(opt) {
9203 $.extend(this.label.style, {
9204 'visibility': 'hidden',
9205 'position': 'absolute',
9209 this.label.className = 'jit-autoadjust-label';
9215 * File: Layouts.MultiTopology.js
9220 * Class: Layouts.MultiTopology
9222 * Implements a Multi Topology Layout.
9223 * Adapted from Layouts.ForceDirected
9231 * Marcus Cobden <http://marcuscobden.co.uk>
9234 Layouts.MultiTopology = new Class({
9236 getOptions: function(random) {
9237 var s = this.canvas.getSize();
9238 var w = s.width, h = s.height;
9241 this.graph.eachNode(function(n) {
9244 var k2 = w * h / count, k = Math.sqrt(k2);
9245 var l = this.config.levelDistance;
9251 nodef: function(x) { return k2 / (x || 1); },
9252 edgef: function(x) { return /* x * x / k; */ k * (x - l); }
9256 compute: function(property, incremental) {
9257 var prop = $.splat(property || ['current', 'start', 'end']);
9258 var opt = this.getOptions();
9259 NodeDim.compute(this.graph, prop, this.config);
9260 this.graph.computeLevels(this.root, 0, "ignore");
9261 this.graph.eachNode(function(n) {
9262 $.each(prop, function(p) {
9263 var pos = n.getPos(p);
9264 if(pos.equals(Complex.KER)) {
9265 pos.x = opt.width/5 * (Math.random() - 0.5);
9266 pos.y = opt.height/5 * (Math.random() - 0.5);
9268 //initialize disp vector
9270 $.each(prop, function(p) {
9271 n.disp[p] = $C(0, 0);
9275 this.computePositions(prop, opt, incremental);
9278 computePositions: function(property, opt, incremental) {
9279 var times = this.config.iterations, i = 0, that = this;
9282 for(var total=incremental.iter, j=0; j<total; j++) {
9284 if(times) opt.t *= (1 - i++/(times -1));
9285 that.computePositionStep(property, opt);
9286 if(times && i >= times) {
9287 incremental.onComplete();
9291 incremental.onStep(Math.round(i / (times -1) * 100));
9292 setTimeout(iter, 1);
9295 for(; i < times; i++) {
9296 opt.t = opt.tstart * (1 - i/(times -1));
9297 this.computePositionStep(property, opt);
9302 computePositionStep: function(property, opt) {
9303 var graph = this.graph;
9304 var min = Math.min, max = Math.max;
9305 var dpos = $C(0, 0);
9306 //calculate repulsive forces
9307 graph.eachNode(function(v) {
9309 $.each(property, function(p) {
9310 v.disp[p].x = 0; v.disp[p].y = 0;
9312 graph.eachNode(function(u) {
9314 $.each(property, function(p) {
9315 var vp = v.getPos(p), up = u.getPos(p);
9316 dpos.x = vp.x - up.x;
9317 dpos.y = vp.y - up.y;
9318 var norm = dpos.norm() || 1;
9320 .$scale(opt.nodef(norm) / norm));
9325 //calculate attractive forces
9326 var T = !!graph.getNode(this.root).visited;
9327 graph.eachNode(function(node) {
9328 node.eachAdjacency(function(adj) {
9329 var nodeTo = adj.nodeTo;
9330 if(!!nodeTo.visited === T) {
9331 $.each(property, function(p) {
9332 var vp = node.getPos(p), up = nodeTo.getPos(p);
9333 dpos.x = vp.x - up.x;
9334 dpos.y = vp.y - up.y;
9335 var norm = dpos.norm() || 1;
9336 node.disp[p].$add(dpos.$scale(-opt.edgef(norm) / norm));
9337 nodeTo.disp[p].$add(dpos.$scale(-1));
9343 //arrange positions to fit the canvas
9344 var t = opt.t, w2 = opt.width / 2, h2 = opt.height / 2;
9345 graph.eachNode(function(u) {
9346 $.each(property, function(p) {
9347 var disp = u.disp[p];
9348 var norm = disp.norm() || 1;
9349 var p = u.getPos(p);
9350 p.$add($C(disp.x * min(Math.abs(disp.x), t) / norm,
9351 disp.y * min(Math.abs(disp.y), t) / norm));
9352 p.x = min(w2, max(-w2, p.x));
9353 p.y = min(h2, max(-h2, p.y));
9361 * File: MultiTopology.js
9365 Class: MultiTopology
9367 A network graph visualization adapted from the Force-Directed graph
9371 All <MultiLoader> methods
9373 Constructor Options:
9375 Inherits options from
9378 - <Options.Controller>
9384 - <Options.NodeStyles>
9385 - <Options.Navigation>
9387 Additionally, there are two parameters
9389 levelDistance - (number) Default's *50*. The natural length desired for the edges.
9390 iterations - (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*.
9392 Instance Properties:
9394 canvas - Access a <Canvas> instance.
9395 graph - Access a <Graph> instance.
9396 op - Access a <ForceDirected.Op> instance.
9397 fx - Access a <ForceDirected.Plot> instance.
9398 labels - Access a <ForceDirected.Label> interface implementation.
9402 $jit.MultiTopology = new Class( {
9404 Implements: [ MultiLoader, MultiExtras, Layouts.MultiTopology ],
9406 initialize: function(controller) {
9407 var $MultiTopology = $jit.MultiTopology;
9414 this.controller = this.config = $.merge(Options("Canvas", "Node", "Edge",
9415 "Fx", "Tips", "NodeStyles", "Events", "Navigation", "Controller", "Label"), config, controller);
9417 var canvasConfig = this.config;
9418 if(canvasConfig.useCanvas) {
9419 this.canvas = canvasConfig.useCanvas;
9420 this.config.labelContainer = this.canvas.id + '-label';
9422 if(canvasConfig.background) {
9423 canvasConfig.background = $.merge({
9425 }, canvasConfig.background);
9427 this.canvas = new Canvas(this, canvasConfig);
9428 this.config.labelContainer = (typeof canvasConfig.injectInto == 'string'? canvasConfig.injectInto : canvasConfig.injectInto.id) + '-label';
9431 this.graphOptions = {
9439 this.graph = new MultiGraph(this.graphOptions, this.config.Node,
9441 this.labels = new $MultiTopology.Label[canvasConfig.Label.type](this);
9442 this.fx = new $MultiTopology.Plot(this, $MultiTopology);
9443 this.op = new $MultiTopology.Op(this);
9446 // initialize extras
9447 this.initializeExtras();
9453 Computes positions and plots the tree.
9455 refresh: function() {
9460 reposition: function() {
9461 this.compute('end');
9465 Method: computeIncremental
9467 Performs the Force Directed algorithm incrementally.
9471 ForceDirected algorithms can perform many computations and lead to JavaScript taking too much time to complete.
9472 This method splits the algorithm into smaller parts allowing the user to track the evolution of the algorithm and
9473 avoiding browser messages such as "This script is taking too long to complete".
9477 opt - (object) The object properties are described below
9479 iter - (number) Default's *20*. Split the algorithm into pieces of _iter_ iterations. For example, if the _iterations_ configuration property
9480 of your <ForceDirected> class is 100, then you could set _iter_ to 20 to split the main algorithm into 5 smaller pieces.
9482 property - (string) Default's *end*. Whether to update starting, current or ending node positions. Possible values are 'end', 'start', 'current'.
9483 You can also set an array of these properties. If you'd like to keep the current node positions but to perform these
9484 computations for final animation positions then you can just choose 'end'.
9486 onStep - (function) A callback function called when each "small part" of the algorithm completed. This function gets as first formal
9487 parameter a percentage value.
9489 onComplete - A callback function called when the algorithm completed.
9493 In this example I calculate the end positions and then animate the graph to those positions
9496 var fd = new $jit.ForceDirected(...);
9497 fd.computeIncremental({
9500 onStep: function(perc) {
9501 Log.write("loading " + perc + "%");
9503 onComplete: function() {
9510 In this example I calculate all positions and (re)plot the graph
9513 var fd = new ForceDirected(...);
9514 fd.computeIncremental({
9516 property: ['end', 'start', 'current'],
9517 onStep: function(perc) {
9518 Log.write("loading " + perc + "%");
9520 onComplete: function() {
9528 computeIncremental: function(opt) {
9536 this.config.onBeforeCompute(this.graph.getNode(this.root));
9537 this.compute(opt.property, opt);
9543 Plots the ForceDirected graph. This is a shortcut to *fx.plot*.
9552 Animates the graph from the current positions to the 'end' node positions.
9554 animate: function(opt) {
9555 this.fx.animate($.merge( {
9561 $jit.MultiTopology.$extend = true;
9563 (function(MultiTopology) {
9566 Class: ForceDirected.Op
9568 Custom extension of <Graph.Op>.
9572 All <Graph.Op> methods
9579 MultiTopology.Op = new Class( {
9581 Implements: MultiGraph.Op
9586 Class: ForceDirected.Plot
9588 Custom extension of <Graph.Plot>.
9592 All <Graph.Plot> methods
9599 MultiTopology.Plot = new Class( {
9601 Implements: MultiGraph.Plot
9606 Class: ForceDirected.Label
9608 Custom extension of <Graph.Label>.
9609 Contains custom <Graph.Label.SVG>, <Graph.Label.HTML> and <Graph.Label.Native> extensions.
9613 All <Graph.Label> methods and subclasses.
9617 <Graph.Label>, <Graph.Label.Native>, <Graph.Label.HTML>, <Graph.Label.SVG>.
9620 MultiTopology.Label = {};
9623 ForceDirected.Label.Native
9625 Custom extension of <Graph.Label.Native>.
9629 All <Graph.Label.Native> methods
9633 <Graph.Label.Native>
9636 MultiTopology.Label.Native = new Class( {
9637 Implements: MultiGraph.Label.Native
9641 ForceDirected.Label.SVG
9643 Custom extension of <Graph.Label.SVG>.
9647 All <Graph.Label.SVG> methods
9654 MultiTopology.Label.SVG = new Class( {
9655 Implements: MultiGraph.Label.SVG,
9657 initialize: function(viz) {
9664 Overrides abstract method placeLabel in <Graph.Label>.
9668 tag - A DOM label element.
9669 node - A <Graph.Node>.
9670 controller - A configuration/controller object passed to the visualization.
9673 placeLabel: function(tag, node, controller) {
9674 var pos = node.pos.getc(true),
9675 canvas = this.viz.canvas,
9676 ox = canvas.translateOffsetX,
9677 oy = canvas.translateOffsetY,
9678 sx = canvas.scaleOffsetX,
9679 sy = canvas.scaleOffsetY,
9680 radius = canvas.getSize();
9682 x: Math.round(pos.x * sx + ox + radius.width / 2),
9683 y: Math.round(pos.y * sy + oy + radius.height / 2)
9685 tag.setAttribute('x', labelPos.x);
9686 tag.setAttribute('y', labelPos.y);
9688 controller.onPlaceLabel(tag, node);
9693 ForceDirected.Label.HTML
9695 Custom extension of <Graph.Label.HTML>.
9699 All <Graph.Label.HTML> methods.
9706 MultiTopology.Label.HTML = new Class( {
9707 Implements: MultiGraph.Label.HTML,
9709 initialize: function(viz) {
9715 Overrides abstract method placeLabel in <Graph.Plot>.
9719 tag - A DOM label element.
9720 node - A <Graph.Node>.
9721 controller - A configuration/controller object passed to the visualization.
9724 placeLabel: function(tag, node, controller) {
9725 var pos = node.pos.getc(true),
9726 canvas = this.viz.canvas,
9727 ox = canvas.translateOffsetX,
9728 oy = canvas.translateOffsetY,
9729 sx = canvas.scaleOffsetX,
9730 sy = canvas.scaleOffsetY,
9731 radius = canvas.getSize();
9733 x: Math.round(pos.x * sx + ox + radius.width / 2),
9734 y: Math.round(pos.y * sy + oy + radius.height / 2)
9736 var style = tag.style;
9737 style.left = labelPos.x + 'px';
9738 style.top = labelPos.y + 'px';
9739 style.display = this.fitsInCanvas(labelPos, canvas) ? '' : 'none';
9741 controller.onPlaceLabel(tag, node);
9746 Class: ForceDirected.Plot.NodeTypes
9748 This class contains a list of <Graph.Node> built-in types.
9749 Node types implemented are 'none', 'circle', 'triangle', 'rectangle', 'star', 'ellipse' and 'square'.
9751 You can add your custom node types, customizing your visualization to the extreme.
9756 ForceDirected.Plot.NodeTypes.implement({
9758 'render': function(node, canvas) {
9759 //print your custom node to canvas
9762 'contains': function(node, pos) {
9763 //return true if pos is inside the node or false otherwise
9770 MultiTopology.Plot.NodeTypes = new Class({
9773 'contains': $.lambda(false)
9776 'render': function(node, canvas, animating) {
9777 var pos = node.pos.getc(true);
9778 this.nodeHelper.host.render(pos, canvas, animating);
9780 'contains': function(node, pos) {
9781 var npos = node.pos.getc(true);
9782 return this.nodeHelper.host.contains(npos, pos);
9786 'render': function(node, canvas, animating) {
9787 var pos = node.pos.getc(true);
9788 this.nodeHelper.swtch.render(pos, canvas, animating);
9790 'contains': function(node, pos) {
9791 var npos = node.pos.getc(true);
9792 return this.nodeHelper.swtch.contains(npos, pos);
9798 Class: ForceDirected.Plot.EdgeTypes
9800 This class contains a list of <Graph.Adjacence> built-in types.
9801 Edge types implemented are 'none', 'line' and 'arrow'.
9803 You can add your custom edge types, customizing your visualization to the extreme.
9808 ForceDirected.Plot.EdgeTypes.implement({
9810 'render': function(adj, canvas) {
9811 //print your custom edge to canvas
9814 'contains': function(adj, pos) {
9815 //return true if pos is inside the arc or false otherwise
9822 MultiTopology.Plot.EdgeTypes = new Class({
9825 'render': function(adj, alpha, canvas) {
9826 var from = adj.nodeFrom.pos.getc(true),
9827 to = adj.nodeTo.pos.getc(true);
9828 this.edgeHelper.line.render(from, to, alpha, canvas);
9830 'contains': function(adj, alpha, pos, canvas) {
9831 var from = adj.nodeFrom.pos.getc(true),
9832 to = adj.nodeTo.pos.getc(true);
9833 return this.edgeHelper.line.contains(from, to, alpha, pos, this.edge.epsilon, canvas);
9838 })($jit.MultiTopology);