Generate json and initial experiments with a treemap.
[p5sagit/Devel-Size.git] / static / jit.js
1 /*
2 Copyright (c) 2011 Sencha Inc. - Author: Nicolas Garcia Belmonte (http://philogb.github.com/)
3
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:
10
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13
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
20 THE SOFTWARE.
21
22  */
23  (function () { 
24
25 /*
26   File: Core.js
27
28  */
29
30 /*
31  Object: $jit
32  
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.
36  */
37 window.$jit = function(w) {
38   w = w || window;
39   for(var k in $jit) {
40     if($jit[k].$extend) {
41       w[k] = $jit[k];
42     }
43   }
44 };
45
46 $jit.version = '2.0.1';
47 /*
48   Object: $jit.id
49   
50   Works just like *document.getElementById*
51   
52   Example:
53   (start code js)
54   var element = $jit.id('elementId');
55   (end code)
56
57 */
58
59 /*
60  Object: $jit.util
61  
62  Contains utility functions.
63  
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>.
67  
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.
71  
72  */
73 var $ = function(d) {
74   return document.getElementById(d);
75 };
76
77 $.empty = function() {
78 };
79
80 /*
81   Method: extend
82   
83   Augment an object by appending another object's properties.
84   
85   Parameters:
86   
87   original - (object) The object to be extended.
88   extended - (object) An object which properties are going to be appended to the original object.
89   
90   Example:
91   (start code js)
92   $jit.util.extend({ 'a': 1, 'b': 2 }, { 'b': 3, 'c': 4 }); //{ 'a':1, 'b': 3, 'c': 4 }
93   (end code)
94 */
95 $.extend = function(original, extended) {
96   for ( var key in (extended || {}))
97     original[key] = extended[key];
98   return original;
99 };
100
101 $.lambda = function(value) {
102   return (typeof value == 'function') ? value : function() {
103     return value;
104   };
105 };
106
107 $.time = Date.now || function() {
108   return +new Date;
109 };
110
111 /*
112   Method: splat
113   
114   Returns an array wrapping *obj* if *obj* is not an array. Returns *obj* otherwise.
115   
116   Parameters:
117   
118   obj - (mixed) The object to be wrapped in an array.
119   
120   Example:
121   (start code js)
122   $jit.util.splat(3);   //[3]
123   $jit.util.splat([3]); //[3]
124   (end code)
125 */
126 $.splat = function(obj) {
127   var type = $.type(obj);
128   return type ? ((type != 'array') ? [ obj ] : obj) : [];
129 };
130
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   return (elem && elem.nodeName && elem.nodeType == 1)? 'element' : type;
136 };
137 $.type.s = Object.prototype.toString;
138
139 /*
140   Method: each
141   
142   Iterates through an iterable applying *f*.
143   
144   Parameters:
145   
146   iterable - (array) The original array.
147   fn - (function) The function to apply to the array elements.
148   
149   Example:
150   (start code js)
151   $jit.util.each([3, 4, 5], function(n) { alert('number ' + n); });
152   (end code)
153 */
154 $.each = function(iterable, fn) {
155   var type = $.type(iterable);
156   if (type == 'object') {
157     for ( var key in iterable)
158       fn(iterable[key], key);
159   } else {
160     for ( var i = 0, l = iterable.length; i < l; i++)
161       fn(iterable[i], i);
162   }
163 };
164
165 $.indexOf = function(array, item) {
166   if(Array.indexOf) return array.indexOf(item);
167   for(var i=0,l=array.length; i<l; i++) {
168     if(array[i] === item) return i;
169   }
170   return -1;
171 };
172
173 /*
174   Method: map
175   
176   Maps or collects an array by applying *f*.
177   
178   Parameters:
179   
180   array - (array) The original array.
181   f - (function) The function to apply to the array elements.
182   
183   Example:
184   (start code js)
185   $jit.util.map([3, 4, 5], function(n) { return n*n; }); //[9, 16, 25]
186   (end code)
187 */
188 $.map = function(array, f) {
189   var ans = [];
190   $.each(array, function(elem, i) {
191     ans.push(f(elem, i));
192   });
193   return ans;
194 };
195
196 /*
197   Method: reduce
198   
199   Iteratively applies the binary function *f* storing the result in an accumulator.
200   
201   Parameters:
202   
203   array - (array) The original array.
204   f - (function) The function to apply to the array elements.
205   opt - (optional|mixed) The starting value for the acumulator.
206   
207   Example:
208   (start code js)
209   $jit.util.reduce([3, 4, 5], function(x, y) { return x + y; }, 0); //12
210   (end code)
211 */
212 $.reduce = function(array, f, opt) {
213   var l = array.length;
214   if(l==0) return opt;
215   var acum = arguments.length == 3? opt : array[--l];
216   while(l--) {
217     acum = f(acum, array[l]);
218   }
219   return acum;
220 };
221
222 /*
223   Method: merge
224   
225   Merges n-objects and their sub-objects creating a new, fresh object.
226   
227   Parameters:
228   
229   An arbitrary number of objects.
230   
231   Example:
232   (start code js)
233   $jit.util.merge({ 'a': 1, 'b': 2 }, { 'b': 3, 'c': 4 }); //{ 'a':1, 'b': 3, 'c': 4 }
234   (end code)
235 */
236 $.merge = function() {
237   var mix = {};
238   for ( var i = 0, l = arguments.length; i < l; i++) {
239     var object = arguments[i];
240     if ($.type(object) != 'object')
241       continue;
242     for ( var key in object) {
243       var op = object[key], mp = mix[key];
244       mix[key] = (mp && $.type(op) == 'object' && $.type(mp) == 'object') ? $
245           .merge(mp, op) : $.unlink(op);
246     }
247   }
248   return mix;
249 };
250
251 $.unlink = function(object) {
252   var unlinked;
253   switch ($.type(object)) {
254   case 'object':
255     unlinked = {};
256     for ( var p in object)
257       unlinked[p] = $.unlink(object[p]);
258     break;
259   case 'array':
260     unlinked = [];
261     for ( var i = 0, l = object.length; i < l; i++)
262       unlinked[i] = $.unlink(object[i]);
263     break;
264   default:
265     return object;
266   }
267   return unlinked;
268 };
269
270 $.zip = function() {
271   if(arguments.length === 0) return [];
272   for(var j=0, ans=[], l=arguments.length, ml=arguments[0].length; j<ml; j++) {
273     for(var i=0, row=[]; i<l; i++) {
274       row.push(arguments[i][j]);
275     }
276     ans.push(row);
277   }
278   return ans;
279 };
280
281 /*
282   Method: rgbToHex
283   
284   Converts an RGB array into a Hex string.
285   
286   Parameters:
287   
288   srcArray - (array) An array with R, G and B values
289   
290   Example:
291   (start code js)
292   $jit.util.rgbToHex([255, 255, 255]); //'#ffffff'
293   (end code)
294 */
295 $.rgbToHex = function(srcArray, array) {
296   if (srcArray.length < 3)
297     return null;
298   if (srcArray.length == 4 && srcArray[3] == 0 && !array)
299     return 'transparent';
300   var hex = [];
301   for ( var i = 0; i < 3; i++) {
302     var bit = (srcArray[i] - 0).toString(16);
303     hex.push(bit.length == 1 ? '0' + bit : bit);
304   }
305   return array ? hex : '#' + hex.join('');
306 };
307
308 /*
309   Method: hexToRgb
310   
311   Converts an Hex color string into an RGB array.
312   
313   Parameters:
314   
315   hex - (string) A color hex string.
316   
317   Example:
318   (start code js)
319   $jit.util.hexToRgb('#fff'); //[255, 255, 255]
320   (end code)
321 */
322 $.hexToRgb = function(hex) {
323   if (hex.length != 7) {
324     hex = hex.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
325     hex.shift();
326     if (hex.length != 3)
327       return null;
328     var rgb = [];
329     for ( var i = 0; i < 3; i++) {
330       var value = hex[i];
331       if (value.length == 1)
332         value += value;
333       rgb.push(parseInt(value, 16));
334     }
335     return rgb;
336   } else {
337     hex = parseInt(hex.slice(1), 16);
338     return [ hex >> 16, hex >> 8 & 0xff, hex & 0xff ];
339   }
340 };
341
342 $.destroy = function(elem) {
343   $.clean(elem);
344   if (elem.parentNode)
345     elem.parentNode.removeChild(elem);
346   if (elem.clearAttributes)
347     elem.clearAttributes();
348 };
349
350 $.clean = function(elem) {
351   for (var ch = elem.childNodes, i = 0, l = ch.length; i < l; i++) {
352     $.destroy(ch[i]);
353   }
354 };
355
356 /*
357   Method: addEvent
358   
359   Cross-browser add event listener.
360   
361   Parameters:
362   
363   obj - (obj) The Element to attach the listener to.
364   type - (string) The listener type. For example 'click', or 'mousemove'.
365   fn - (function) The callback function to be used when the event is fired.
366   
367   Example:
368   (start code js)
369   $jit.util.addEvent(elem, 'click', function(){ alert('hello'); });
370   (end code)
371 */
372 $.addEvent = function(obj, type, fn) {
373   if (obj.addEventListener)
374     obj.addEventListener(type, fn, false);
375   else
376     obj.attachEvent('on' + type, fn);
377 };
378
379 $.addEvents = function(obj, typeObj) {
380   for(var type in typeObj) {
381     $.addEvent(obj, type, typeObj[type]);
382   }
383 };
384
385 $.hasClass = function(obj, klass) {
386   return (' ' + obj.className + ' ').indexOf(' ' + klass + ' ') > -1;
387 };
388
389 $.addClass = function(obj, klass) {
390   if (!$.hasClass(obj, klass))
391     obj.className = (obj.className + " " + klass);
392 };
393
394 $.removeClass = function(obj, klass) {
395   obj.className = obj.className.replace(new RegExp(
396       '(^|\\s)' + klass + '(?:\\s|$)'), '$1');
397 };
398
399 $.getPos = function(elem) {
400   var offset = getOffsets(elem);
401   var scroll = getScrolls(elem);
402   return {
403     x: offset.x - scroll.x,
404     y: offset.y - scroll.y
405   };
406
407   function getOffsets(elem) {
408     var position = {
409       x: 0,
410       y: 0
411     };
412     while (elem && !isBody(elem)) {
413       position.x += elem.offsetLeft;
414       position.y += elem.offsetTop;
415       elem = elem.offsetParent;
416     }
417     return position;
418   }
419
420   function getScrolls(elem) {
421     var position = {
422       x: 0,
423       y: 0
424     };
425     while (elem && !isBody(elem)) {
426       position.x += elem.scrollLeft;
427       position.y += elem.scrollTop;
428       elem = elem.parentNode;
429     }
430     return position;
431   }
432
433   function isBody(element) {
434     return (/^(?:body|html)$/i).test(element.tagName);
435   }
436 };
437
438 $.event = {
439   get: function(e, win) {
440     win = win || window;
441     return e || win.event;
442   },
443   getWheel: function(e) {
444     return e.wheelDelta? e.wheelDelta / 120 : -(e.detail || 0) / 3;
445   },
446   isRightClick: function(e) {
447     return (e.which == 3 || e.button == 2);
448   },
449   getPos: function(e, win) {
450     // get mouse position
451     win = win || window;
452     e = e || win.event;
453     var doc = win.document;
454     doc = doc.documentElement || doc.body;
455     //TODO(nico): make touch event handling better
456     if(e.touches && e.touches.length) {
457       e = e.touches[0];
458     }
459     var page = {
460       x: e.pageX || (e.clientX + doc.scrollLeft),
461       y: e.pageY || (e.clientY + doc.scrollTop)
462     };
463     return page;
464   },
465   stop: function(e) {
466     if (e.stopPropagation) e.stopPropagation();
467     e.cancelBubble = true;
468     if (e.preventDefault) e.preventDefault();
469     else e.returnValue = false;
470   }
471 };
472
473 $jit.util = $jit.id = $;
474
475 var Class = function(properties) {
476   properties = properties || {};
477   var klass = function() {
478     for ( var key in this) {
479       if (typeof this[key] != 'function')
480         this[key] = $.unlink(this[key]);
481     }
482     this.constructor = klass;
483     if (Class.prototyping)
484       return this;
485     var instance = this.initialize ? this.initialize.apply(this, arguments)
486         : this;
487     //typize
488     this.$$family = 'class';
489     return instance;
490   };
491
492   for ( var mutator in Class.Mutators) {
493     if (!properties[mutator])
494       continue;
495     properties = Class.Mutators[mutator](properties, properties[mutator]);
496     delete properties[mutator];
497   }
498
499   $.extend(klass, this);
500   klass.constructor = Class;
501   klass.prototype = properties;
502   return klass;
503 };
504
505 Class.Mutators = {
506
507   Implements: function(self, klasses) {
508     $.each($.splat(klasses), function(klass) {
509       Class.prototyping = klass;
510       var instance = (typeof klass == 'function') ? new klass : klass;
511       for ( var prop in instance) {
512         if (!(prop in self)) {
513           self[prop] = instance[prop];
514         }
515       }
516       delete Class.prototyping;
517     });
518     return self;
519   }
520
521 };
522
523 $.extend(Class, {
524
525   inherit: function(object, properties) {
526     for ( var key in properties) {
527       var override = properties[key];
528       var previous = object[key];
529       var type = $.type(override);
530       if (previous && type == 'function') {
531         if (override != previous) {
532           Class.override(object, key, override);
533         }
534       } else if (type == 'object') {
535         object[key] = $.merge(previous, override);
536       } else {
537         object[key] = override;
538       }
539     }
540     return object;
541   },
542
543   override: function(object, name, method) {
544     var parent = Class.prototyping;
545     if (parent && object[name] != parent[name])
546       parent = null;
547     var override = function() {
548       var previous = this.parent;
549       this.parent = parent ? parent[name] : object[name];
550       var value = method.apply(this, arguments);
551       this.parent = previous;
552       return value;
553     };
554     object[name] = override;
555   }
556
557 });
558
559 Class.prototype.implement = function() {
560   var proto = this.prototype;
561   $.each(Array.prototype.slice.call(arguments || []), function(properties) {
562     Class.inherit(proto, properties);
563   });
564   return this;
565 };
566
567 $jit.Class = Class;
568
569 /*
570   Object: $jit.json
571   
572   Provides JSON utility functions.
573   
574   Most of these functions are JSON-tree traversal and manipulation functions.
575 */
576 $jit.json = {
577   /*
578      Method: prune
579   
580      Clears all tree nodes having depth greater than maxLevel.
581   
582      Parameters:
583   
584         tree - (object) A JSON tree object. For more information please see <Loader.loadJSON>.
585         maxLevel - (number) An integer specifying the maximum level allowed for this tree. All nodes having depth greater than max level will be deleted.
586
587   */
588   prune: function(tree, maxLevel) {
589     this.each(tree, function(elem, i) {
590       if (i == maxLevel && elem.children) {
591         delete elem.children;
592         elem.children = [];
593       }
594     });
595   },
596   /*
597      Method: getParent
598   
599      Returns the parent node of the node having _id_ as id.
600   
601      Parameters:
602   
603         tree - (object) A JSON tree object. See also <Loader.loadJSON>.
604         id - (string) The _id_ of the child node whose parent will be returned.
605
606     Returns:
607
608         A tree JSON node if any, or false otherwise.
609   
610   */
611   getParent: function(tree, id) {
612     if (tree.id == id)
613       return false;
614     var ch = tree.children;
615     if (ch && ch.length > 0) {
616       for ( var i = 0; i < ch.length; i++) {
617         if (ch[i].id == id)
618           return tree;
619         else {
620           var ans = this.getParent(ch[i], id);
621           if (ans)
622             return ans;
623         }
624       }
625     }
626     return false;
627   },
628   /*
629      Method: getSubtree
630   
631      Returns the subtree that matches the given id.
632   
633      Parameters:
634   
635         tree - (object) A JSON tree object. See also <Loader.loadJSON>.
636         id - (string) A node *unique* identifier.
637   
638      Returns:
639   
640         A subtree having a root node matching the given id. Returns null if no subtree matching the id is found.
641
642   */
643   getSubtree: function(tree, id) {
644     if (tree.id == id)
645       return tree;
646     for ( var i = 0, ch = tree.children; ch && i < ch.length; i++) {
647       var t = this.getSubtree(ch[i], id);
648       if (t != null)
649         return t;
650     }
651     return null;
652   },
653   /*
654      Method: eachLevel
655   
656       Iterates on tree nodes with relative depth less or equal than a specified level.
657   
658      Parameters:
659   
660         tree - (object) A JSON tree or subtree. See also <Loader.loadJSON>.
661         initLevel - (number) An integer specifying the initial relative level. Usually zero.
662         toLevel - (number) An integer specifying a top level. This method will iterate only through nodes with depth less than or equal this number.
663         action - (function) A function that receives a node and an integer specifying the actual level of the node.
664           
665     Example:
666    (start code js)
667      $jit.json.eachLevel(tree, 0, 3, function(node, depth) {
668         alert(node.name + ' ' + depth);
669      });
670    (end code)
671   */
672   eachLevel: function(tree, initLevel, toLevel, action) {
673     if (initLevel <= toLevel) {
674       action(tree, initLevel);
675       if(!tree.children) return;
676       for ( var i = 0, ch = tree.children; i < ch.length; i++) {
677         this.eachLevel(ch[i], initLevel + 1, toLevel, action);
678       }
679     }
680   },
681   /*
682      Method: each
683   
684       A JSON tree iterator.
685   
686      Parameters:
687   
688         tree - (object) A JSON tree or subtree. See also <Loader.loadJSON>.
689         action - (function) A function that receives a node.
690
691     Example:
692     (start code js)
693       $jit.json.each(tree, function(node) {
694         alert(node.name);
695       });
696     (end code)
697           
698   */
699   each: function(tree, action) {
700     this.eachLevel(tree, 0, Number.MAX_VALUE, action);
701   }
702 };
703
704
705 /*
706      An object containing multiple type of transformations. 
707 */
708
709 $jit.Trans = {
710   $extend: true,
711   
712   linear: function(p){
713     return p;
714   }
715 };
716
717 var Trans = $jit.Trans;
718
719 (function(){
720
721   var makeTrans = function(transition, params){
722     params = $.splat(params);
723     return $.extend(transition, {
724       easeIn: function(pos){
725         return transition(pos, params);
726       },
727       easeOut: function(pos){
728         return 1 - transition(1 - pos, params);
729       },
730       easeInOut: function(pos){
731         return (pos <= 0.5)? transition(2 * pos, params) / 2 : (2 - transition(
732             2 * (1 - pos), params)) / 2;
733       }
734     });
735   };
736
737   var transitions = {
738
739     Pow: function(p, x){
740       return Math.pow(p, x[0] || 6);
741     },
742
743     Expo: function(p){
744       return Math.pow(2, 8 * (p - 1));
745     },
746
747     Circ: function(p){
748       return 1 - Math.sin(Math.acos(p));
749     },
750
751     Sine: function(p){
752       return 1 - Math.sin((1 - p) * Math.PI / 2);
753     },
754
755     Back: function(p, x){
756       x = x[0] || 1.618;
757       return Math.pow(p, 2) * ((x + 1) * p - x);
758     },
759
760     Bounce: function(p){
761       var value;
762       for ( var a = 0, b = 1; 1; a += b, b /= 2) {
763         if (p >= (7 - 4 * a) / 11) {
764           value = b * b - Math.pow((11 - 6 * a - 11 * p) / 4, 2);
765           break;
766         }
767       }
768       return value;
769     },
770
771     Elastic: function(p, x){
772       return Math.pow(2, 10 * --p)
773           * Math.cos(20 * p * Math.PI * (x[0] || 1) / 3);
774     }
775
776   };
777
778   $.each(transitions, function(val, key){
779     Trans[key] = makeTrans(val);
780   });
781
782   $.each( [
783       'Quad', 'Cubic', 'Quart', 'Quint'
784   ], function(elem, i){
785     Trans[elem] = makeTrans(function(p){
786       return Math.pow(p, [
787         i + 2
788       ]);
789     });
790   });
791
792 })();
793
794 /*
795    A Class that can perform animations for generic objects.
796
797    If you are looking for animation transitions please take a look at the <Trans> object.
798
799    Used by:
800
801    <Graph.Plot>
802    
803    Based on:
804    
805    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>.
806
807 */
808
809 var Animation = new Class( {
810
811   initialize: function(options){
812     this.setOptions(options);
813   },
814
815   setOptions: function(options){
816     var opt = {
817       duration: 2500,
818       fps: 40,
819       transition: Trans.Quart.easeInOut,
820       compute: $.empty,
821       complete: $.empty,
822       link: 'ignore'
823     };
824     this.opt = $.merge(opt, options || {});
825     return this;
826   },
827
828   step: function(){
829     var time = $.time(), opt = this.opt;
830     if (time < this.time + opt.duration) {
831       var delta = opt.transition((time - this.time) / opt.duration);
832       opt.compute(delta);
833     } else {
834       this.timer = clearInterval(this.timer);
835       opt.compute(1);
836       opt.complete();
837     }
838   },
839
840   start: function(){
841     if (!this.check())
842       return this;
843     this.time = 0;
844     this.startTimer();
845     return this;
846   },
847
848   startTimer: function(){
849     var that = this, fps = this.opt.fps;
850     if (this.timer)
851       return false;
852     this.time = $.time() - this.time;
853     this.timer = setInterval((function(){
854       that.step();
855     }), Math.round(1000 / fps));
856     return true;
857   },
858
859   pause: function(){
860     this.stopTimer();
861     return this;
862   },
863
864   resume: function(){
865     this.startTimer();
866     return this;
867   },
868
869   stopTimer: function(){
870     if (!this.timer)
871       return false;
872     this.time = $.time() - this.time;
873     this.timer = clearInterval(this.timer);
874     return true;
875   },
876
877   check: function(){
878     if (!this.timer)
879       return true;
880     if (this.opt.link == 'cancel') {
881       this.stopTimer();
882       return true;
883     }
884     return false;
885   }
886 });
887
888
889 var Options = function() {
890   var args = arguments;
891   for(var i=0, l=args.length, ans={}; i<l; i++) {
892     var opt = Options[args[i]];
893     if(opt.$extend) {
894       $.extend(ans, opt);
895     } else {
896       ans[args[i]] = opt;  
897     }
898   }
899   return ans;
900 };
901
902 /*
903  * File: Options.AreaChart.js
904  *
905 */
906
907 /*
908   Object: Options.AreaChart
909   
910   <AreaChart> options. 
911   Other options included in the AreaChart are <Options.Canvas>, <Options.Label>, <Options.Margin>, <Options.Tips> and <Options.Events>.
912   
913   Syntax:
914   
915   (start code js)
916
917   Options.AreaChart = {
918     animate: true,
919     labelOffset: 3,
920     type: 'stacked',
921     selectOnHover: true,
922     showAggregates: true,
923     showLabels: true,
924     filterOnClick: false,
925     restoreOnRightClick: false
926   };
927   
928   (end code)
929   
930   Example:
931   
932   (start code js)
933
934   var areaChart = new $jit.AreaChart({
935     animate: true,
936     type: 'stacked:gradient',
937     selectOnHover: true,
938     filterOnClick: true,
939     restoreOnRightClick: true
940   });
941   
942   (end code)
943
944   Parameters:
945   
946   animate - (boolean) Default's *true*. Whether to add animated transitions when filtering/restoring stacks.
947   labelOffset - (number) Default's *3*. Adds margin between the label and the default place where it should be drawn.
948   type - (string) Default's *'stacked'*. Stack style. Posible values are 'stacked', 'stacked:gradient' to add gradients.
949   selectOnHover - (boolean) Default's *true*. If true, it will add a mark to the hovered stack.
950   showAggregates - (boolean, function) Default's *true*. Display the values of the stacks. Can also be a function that returns *true* or *false* to display or filter some values. That same function can also return a string with the formatted value.
951   showLabels - (boolean, function) Default's *true*. Display the name of the slots. Can also be a function that returns *true* or *false* to display or not each label.
952   filterOnClick - (boolean) Default's *true*. Select the clicked stack by hiding all other stacks.
953   restoreOnRightClick - (boolean) Default's *true*. Show all stacks by right clicking.
954   
955 */
956   
957 Options.AreaChart = {
958   $extend: true,
959
960   animate: true,
961   labelOffset: 3, // label offset
962   type: 'stacked', // gradient
963   Tips: {
964     enable: false,
965     onShow: $.empty,
966     onHide: $.empty
967   },
968   Events: {
969     enable: false,
970     onClick: $.empty
971   },
972   selectOnHover: true,
973   showAggregates: true,
974   showLabels: true,
975   filterOnClick: false,
976   restoreOnRightClick: false
977 };
978
979 /*
980  * File: Options.Margin.js
981  *
982 */
983
984 /*
985   Object: Options.Margin
986   
987   Canvas drawing margins. 
988   
989   Syntax:
990   
991   (start code js)
992
993   Options.Margin = {
994     top: 0,
995     left: 0,
996     right: 0,
997     bottom: 0
998   };
999   
1000   (end code)
1001   
1002   Example:
1003   
1004   (start code js)
1005
1006   var viz = new $jit.Viz({
1007     Margin: {
1008       right: 10,
1009       bottom: 20
1010     }
1011   });
1012   
1013   (end code)
1014
1015   Parameters:
1016   
1017   top - (number) Default's *0*. Top margin.
1018   left - (number) Default's *0*. Left margin.
1019   right - (number) Default's *0*. Right margin.
1020   bottom - (number) Default's *0*. Bottom margin.
1021   
1022 */
1023
1024 Options.Margin = {
1025   $extend: false,
1026   
1027   top: 0,
1028   left: 0,
1029   right: 0,
1030   bottom: 0
1031 };
1032
1033 /*
1034  * File: Options.Canvas.js
1035  *
1036 */
1037
1038 /*
1039   Object: Options.Canvas
1040   
1041   These are Canvas general options, like where to append it in the DOM, its dimensions, background, 
1042   and other more advanced options.
1043   
1044   Syntax:
1045   
1046   (start code js)
1047
1048   Options.Canvas = {
1049     injectInto: 'id',
1050     type: '2D', //'3D'
1051     width: false,
1052     height: false,
1053     useCanvas: false,
1054     withLabels: true,
1055     background: false
1056   };  
1057   (end code)
1058   
1059   Example:
1060   
1061   (start code js)
1062   var viz = new $jit.Viz({
1063     injectInto: 'someContainerId',
1064     width: 500,
1065     height: 700
1066   });
1067   (end code)
1068   
1069   Parameters:
1070   
1071   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.
1072   type - (string) Context type. Default's 2D but can be 3D for webGL enabled browsers.
1073   width - (number) Default's to the *container's offsetWidth*. The width of the canvas.
1074   height - (number) Default's to the *container's offsetHeight*. The height of the canvas.
1075   useCanvas - (boolean|object) Default's *false*. You can pass another <Canvas> instance to be used by the visualization.
1076   withLabels - (boolean) Default's *true*. Whether to use a label container for the visualization.
1077   background - (boolean|object) Default's *false*. An object containing information about the rendering of a background canvas.
1078 */
1079
1080 Options.Canvas = {
1081     $extend: true,
1082     
1083     injectInto: 'id',
1084     type: '2D',
1085     width: false,
1086     height: false,
1087     useCanvas: false,
1088     withLabels: true,
1089     background: false,
1090     
1091     Scene: {
1092       Lighting: {
1093         enable: false,
1094         ambient: [1, 1, 1],
1095         directional: {
1096           direction: { x: -100, y: -100, z: -100 },
1097           color: [0.5, 0.3, 0.1]
1098         }
1099       }
1100     }
1101 };
1102
1103 /*
1104  * File: Options.Tree.js
1105  *
1106 */
1107
1108 /*
1109   Object: Options.Tree
1110   
1111   Options related to (strict) Tree layout algorithms. These options are used by the <ST> visualization.
1112   
1113   Syntax:
1114   
1115   (start code js)
1116   Options.Tree = {
1117     orientation: "left",
1118     subtreeOffset: 8,
1119     siblingOffset: 5,
1120     indent:10,
1121     multitree: false,
1122     align:"center"
1123   };
1124   (end code)
1125   
1126   Example:
1127   
1128   (start code js)
1129   var st = new $jit.ST({
1130     orientation: 'left',
1131     subtreeOffset: 1,
1132     siblingOFfset: 5,
1133     multitree: true
1134   });
1135   (end code)
1136
1137   Parameters:
1138     
1139   subtreeOffset - (number) Default's 8. Separation offset between subtrees.
1140   siblingOffset - (number) Default's 5. Separation offset between siblings.
1141   orientation - (string) Default's 'left'. Tree orientation layout. Possible values are 'left', 'top', 'right', 'bottom'.
1142   align - (string) Default's *center*. Whether the tree alignment is 'left', 'center' or 'right'.
1143   indent - (number) Default's 10. Used when *align* is left or right and shows an indentation between parent and children.
1144   multitree - (boolean) Default's *false*. Used with the node $orn data property for creating multitrees.
1145      
1146 */
1147 Options.Tree = {
1148     $extend: true,
1149     
1150     orientation: "left",
1151     subtreeOffset: 8,
1152     siblingOffset: 5,
1153     indent:10,
1154     multitree: false,
1155     align:"center"
1156 };
1157
1158
1159 /*
1160  * File: Options.Node.js
1161  *
1162 */
1163
1164 /*
1165   Object: Options.Node
1166
1167   Provides Node rendering options for Tree and Graph based visualizations.
1168
1169   Syntax:
1170     
1171   (start code js)
1172   Options.Node = {
1173     overridable: false,
1174     type: 'circle',
1175     color: '#ccb',
1176     alpha: 1,
1177     dim: 3,
1178     height: 20,
1179     width: 90,
1180     autoHeight: false,
1181     autoWidth: false,
1182     lineWidth: 1,
1183     transform: true,
1184     align: "center",
1185     angularWidth:1,
1186     span:1,
1187     CanvasStyles: {}
1188   };
1189   (end code)
1190   
1191   Example:
1192   
1193   (start code js)
1194   var viz = new $jit.Viz({
1195     Node: {
1196       overridable: true,
1197       width: 30,
1198       autoHeight: true,
1199       type: 'rectangle'
1200     }
1201   });
1202   (end code)
1203   
1204   Parameters:
1205
1206   overridable - (boolean) Default's *false*. Determine whether or not general node properties can be overridden by a particular <Graph.Node>.
1207   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.
1208   color - (string) Default's *#ccb*. Node color.
1209   alpha - (number) Default's *1*. The Node's alpha value. *1* is for full opacity.
1210   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).
1211   height - (number) Default's *20*. Used by 'rectangle' and 'ellipse' node types. The height of the node shape.
1212   width - (number) Default's *90*. Used by 'rectangle' and 'ellipse' node types. The width of the node shape.
1213   autoHeight - (boolean) Default's *false*. Whether to set an auto height for the node depending on the content of the Node's label.
1214   autoWidth - (boolean) Default's *false*. Whether to set an auto width for the node depending on the content of the Node's label.
1215   lineWidth - (number) Default's *1*. Used only by some Node shapes. The line width of the strokes of a node.
1216   transform - (boolean) Default's *true*. Only used by the <Hypertree> visualization. Whether to scale the nodes according to the moebius transformation.
1217   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.
1218   angularWidth - (number) Default's *1*. Used in radial layouts (like <RGraph> or <Sunburst> visualizations). The amount of relative 'space' set for a node.
1219   span - (number) Default's *1*. Used in radial layouts (like <RGraph> or <Sunburst> visualizations). The angle span amount set for a node.
1220   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.
1221
1222 */
1223 Options.Node = {
1224   $extend: false,
1225   
1226   overridable: false,
1227   type: 'circle',
1228   color: '#ccb',
1229   alpha: 1,
1230   dim: 3,
1231   height: 20,
1232   width: 90,
1233   autoHeight: false,
1234   autoWidth: false,
1235   lineWidth: 1,
1236   transform: true,
1237   align: "center",
1238   angularWidth:1,
1239   span:1,
1240   //Raw canvas styles to be
1241   //applied to the context instance
1242   //before plotting a node
1243   CanvasStyles: {}
1244 };
1245
1246
1247 /*
1248  * File: Options.Edge.js
1249  *
1250 */
1251
1252 /*
1253   Object: Options.Edge
1254
1255   Provides Edge rendering options for Tree and Graph based visualizations.
1256
1257   Syntax:
1258     
1259   (start code js)
1260   Options.Edge = {
1261     overridable: false,
1262     type: 'line',
1263     color: '#ccb',
1264     lineWidth: 1,
1265     dim:15,
1266     alpha: 1,
1267     CanvasStyles: {}
1268   };
1269   (end code)
1270   
1271   Example:
1272   
1273   (start code js)
1274   var viz = new $jit.Viz({
1275     Edge: {
1276       overridable: true,
1277       type: 'line',
1278       color: '#fff',
1279       CanvasStyles: {
1280         shadowColor: '#ccc',
1281         shadowBlur: 10
1282       }
1283     }
1284   });
1285   (end code)
1286   
1287   Parameters:
1288     
1289    overridable - (boolean) Default's *false*. Determine whether or not general edges properties can be overridden by a particular <Graph.Adjacence>.
1290    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.
1291    color - (string) Default's '#ccb'. Edge color.
1292    lineWidth - (number) Default's *1*. Line/Edge width.
1293    alpha - (number) Default's *1*. The Edge's alpha value. *1* is for full opacity.
1294    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.
1295    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*.
1296    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.
1297
1298   See also:
1299    
1300    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.
1301 */
1302 Options.Edge = {
1303   $extend: false,
1304   
1305   overridable: false,
1306   type: 'line',
1307   color: '#ccb',
1308   lineWidth: 1,
1309   dim:15,
1310   alpha: 1,
1311   epsilon: 7,
1312
1313   //Raw canvas styles to be
1314   //applied to the context instance
1315   //before plotting an edge
1316   CanvasStyles: {}
1317 };
1318
1319
1320 /*
1321  * File: Options.Fx.js
1322  *
1323 */
1324
1325 /*
1326   Object: Options.Fx
1327
1328   Provides animation options like duration of the animations, frames per second and animation transitions.  
1329
1330   Syntax:
1331   
1332   (start code js)
1333     Options.Fx = {
1334       fps:40,
1335       duration: 2500,
1336       transition: $jit.Trans.Quart.easeInOut,
1337       clearCanvas: true
1338     };
1339   (end code)
1340   
1341   Example:
1342   
1343   (start code js)
1344   var viz = new $jit.Viz({
1345     duration: 1000,
1346     fps: 35,
1347     transition: $jit.Trans.linear
1348   });
1349   (end code)
1350   
1351   Parameters:
1352   
1353   clearCanvas - (boolean) Default's *true*. Whether to clear the frame/canvas when the viz is plotted or animated.
1354   duration - (number) Default's *2500*. Duration of the animation in milliseconds.
1355   fps - (number) Default's *40*. Frames per second.
1356   transition - (object) Default's *$jit.Trans.Quart.easeInOut*. The transition used for the animations. See below for a more detailed explanation.
1357   
1358   Object: $jit.Trans
1359   
1360   This object is used for specifying different animation transitions in all visualizations.
1361
1362   There are many different type of animation transitions.
1363
1364   linear:
1365
1366   Displays a linear transition
1367
1368   >Trans.linear
1369   
1370   (see Linear.png)
1371
1372   Quad:
1373
1374   Displays a Quadratic transition.
1375
1376   >Trans.Quad.easeIn
1377   >Trans.Quad.easeOut
1378   >Trans.Quad.easeInOut
1379   
1380  (see Quad.png)
1381
1382  Cubic:
1383
1384  Displays a Cubic transition.
1385
1386  >Trans.Cubic.easeIn
1387  >Trans.Cubic.easeOut
1388  >Trans.Cubic.easeInOut
1389
1390  (see Cubic.png)
1391
1392  Quart:
1393
1394  Displays a Quartetic transition.
1395
1396  >Trans.Quart.easeIn
1397  >Trans.Quart.easeOut
1398  >Trans.Quart.easeInOut
1399
1400  (see Quart.png)
1401
1402  Quint:
1403
1404  Displays a Quintic transition.
1405
1406  >Trans.Quint.easeIn
1407  >Trans.Quint.easeOut
1408  >Trans.Quint.easeInOut
1409
1410  (see Quint.png)
1411
1412  Expo:
1413
1414  Displays an Exponential transition.
1415
1416  >Trans.Expo.easeIn
1417  >Trans.Expo.easeOut
1418  >Trans.Expo.easeInOut
1419
1420  (see Expo.png)
1421
1422  Circ:
1423
1424  Displays a Circular transition.
1425
1426  >Trans.Circ.easeIn
1427  >Trans.Circ.easeOut
1428  >Trans.Circ.easeInOut
1429
1430  (see Circ.png)
1431
1432  Sine:
1433
1434  Displays a Sineousidal transition.
1435
1436  >Trans.Sine.easeIn
1437  >Trans.Sine.easeOut
1438  >Trans.Sine.easeInOut
1439
1440  (see Sine.png)
1441
1442  Back:
1443
1444  >Trans.Back.easeIn
1445  >Trans.Back.easeOut
1446  >Trans.Back.easeInOut
1447
1448  (see Back.png)
1449
1450  Bounce:
1451
1452  Bouncy transition.
1453
1454  >Trans.Bounce.easeIn
1455  >Trans.Bounce.easeOut
1456  >Trans.Bounce.easeInOut
1457
1458  (see Bounce.png)
1459
1460  Elastic:
1461
1462  Elastic curve.
1463
1464  >Trans.Elastic.easeIn
1465  >Trans.Elastic.easeOut
1466  >Trans.Elastic.easeInOut
1467
1468  (see Elastic.png)
1469  
1470  Based on:
1471      
1472  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>.
1473
1474
1475 */
1476 Options.Fx = {
1477   $extend: true,
1478   
1479   fps:40,
1480   duration: 2500,
1481   transition: $jit.Trans.Quart.easeInOut,
1482   clearCanvas: true
1483 };
1484
1485 /*
1486  * File: Options.Label.js
1487  *
1488 */
1489 /*
1490   Object: Options.Label
1491
1492   Provides styling for Labels such as font size, family, etc. Also sets Node labels as HTML, SVG or Native canvas elements.  
1493
1494   Syntax:
1495   
1496   (start code js)
1497     Options.Label = {
1498       overridable: false,
1499       type: 'HTML', //'SVG', 'Native'
1500       style: ' ',
1501       size: 10,
1502       family: 'sans-serif',
1503       textAlign: 'center',
1504       textBaseline: 'alphabetic',
1505       color: '#fff'
1506     };
1507   (end code)
1508   
1509   Example:
1510   
1511   (start code js)
1512   var viz = new $jit.Viz({
1513     Label: {
1514       type: 'Native',
1515       size: 11,
1516       color: '#ccc'
1517     }
1518   });
1519   (end code)
1520   
1521   Parameters:
1522     
1523   overridable - (boolean) Default's *false*. Determine whether or not general label properties can be overridden by a particular <Graph.Node>.
1524   type - (string) Default's *HTML*. The type for the labels. Can be 'HTML', 'SVG' or 'Native' canvas labels.
1525   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.
1526   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.
1527   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.
1528   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.
1529 */
1530 Options.Label = {
1531   $extend: false,
1532   
1533   overridable: false,
1534   type: 'HTML', //'SVG', 'Native'
1535   style: ' ',
1536   size: 10,
1537   family: 'sans-serif',
1538   textAlign: 'center',
1539   textBaseline: 'alphabetic',
1540   color: '#fff'
1541 };
1542
1543
1544 /*
1545  * File: Options.Tips.js
1546  *
1547  */
1548
1549 /*
1550   Object: Options.Tips
1551   
1552   Tips options
1553   
1554   Syntax:
1555     
1556   (start code js)
1557   Options.Tips = {
1558     enable: false,
1559     type: 'auto',
1560     offsetX: 20,
1561     offsetY: 20,
1562     onShow: $.empty,
1563     onHide: $.empty
1564   };
1565   (end code)
1566   
1567   Example:
1568   
1569   (start code js)
1570   var viz = new $jit.Viz({
1571     Tips: {
1572       enable: true,
1573       type: 'Native',
1574       offsetX: 10,
1575       offsetY: 10,
1576       onShow: function(tip, node) {
1577         tip.innerHTML = node.name;
1578       }
1579     }
1580   });
1581   (end code)
1582
1583   Parameters:
1584
1585   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. 
1586   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.
1587   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.
1588   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.
1589   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.
1590   onHide() - This callack is used when hiding a tooltip.
1591
1592 */
1593 Options.Tips = {
1594   $extend: false,
1595   
1596   enable: false,
1597   type: 'auto',
1598   offsetX: 20,
1599   offsetY: 20,
1600   force: false,
1601   onShow: $.empty,
1602   onHide: $.empty
1603 };
1604
1605
1606 /*
1607  * File: Options.NodeStyles.js
1608  *
1609  */
1610
1611 /*
1612   Object: Options.NodeStyles
1613   
1614   Apply different styles when a node is hovered or selected.
1615   
1616   Syntax:
1617     
1618   (start code js)
1619   Options.NodeStyles = {
1620     enable: false,
1621     type: 'auto',
1622     stylesHover: false,
1623     stylesClick: false
1624   };
1625   (end code)
1626   
1627   Example:
1628   
1629   (start code js)
1630   var viz = new $jit.Viz({
1631     NodeStyles: {
1632       enable: true,
1633       type: 'Native',
1634       stylesHover: {
1635         dim: 30,
1636         color: '#fcc'
1637       },
1638       duration: 600
1639     }
1640   });
1641   (end code)
1642
1643   Parameters:
1644   
1645   enable - (boolean) Default's *false*. Whether to enable this option.
1646   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>.
1647   stylesHover - (boolean|object) Default's *false*. An object with node styles just like the ones defined for <Options.Node> or *false* otherwise.
1648   stylesClick - (boolean|object) Default's *false*. An object with node styles just like the ones defined for <Options.Node> or *false* otherwise.
1649 */
1650
1651 Options.NodeStyles = {
1652   $extend: false,
1653   
1654   enable: false,
1655   type: 'auto',
1656   stylesHover: false,
1657   stylesClick: false
1658 };
1659
1660
1661 /*
1662  * File: Options.Events.js
1663  *
1664 */
1665
1666 /*
1667   Object: Options.Events
1668   
1669   Configuration for adding mouse/touch event handlers to Nodes.
1670   
1671   Syntax:
1672   
1673   (start code js)
1674   Options.Events = {
1675     enable: false,
1676     enableForEdges: false,
1677     type: 'auto',
1678     onClick: $.empty,
1679     onRightClick: $.empty,
1680     onMouseMove: $.empty,
1681     onMouseEnter: $.empty,
1682     onMouseLeave: $.empty,
1683     onDragStart: $.empty,
1684     onDragMove: $.empty,
1685     onDragCancel: $.empty,
1686     onDragEnd: $.empty,
1687     onTouchStart: $.empty,
1688     onTouchMove: $.empty,
1689     onTouchEnd: $.empty,
1690     onTouchCancel: $.empty,
1691     onMouseWheel: $.empty
1692   };
1693   (end code)
1694   
1695   Example:
1696   
1697   (start code js)
1698   var viz = new $jit.Viz({
1699     Events: {
1700       enable: true,
1701       onClick: function(node, eventInfo, e) {
1702         viz.doSomething();
1703       },
1704       onMouseEnter: function(node, eventInfo, e) {
1705         viz.canvas.getElement().style.cursor = 'pointer';
1706       },
1707       onMouseLeave: function(node, eventInfo, e) {
1708         viz.canvas.getElement().style.cursor = '';
1709       }
1710     }
1711   });
1712   (end code)
1713   
1714   Parameters:
1715   
1716   enable - (boolean) Default's *false*. Whether to enable the Event system.
1717   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*.
1718   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.
1719   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. 
1720   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. 
1721   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.
1722   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. 
1723   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. 
1724   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. 
1725   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. 
1726   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. 
1727   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. 
1728   onTouchStart(node, eventInfo, e) - Behaves just like onDragStart. 
1729   onTouchMove(node, eventInfo, e) - Behaves just like onDragMove. 
1730   onTouchEnd(node, eventInfo, e) - Behaves just like onDragEnd. 
1731   onTouchCancel(node, eventInfo, e) - Behaves just like onDragCancel.
1732   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.
1733 */
1734
1735 Options.Events = {
1736   $extend: false,
1737   
1738   enable: false,
1739   enableForEdges: false,
1740   type: 'auto',
1741   onClick: $.empty,
1742   onRightClick: $.empty,
1743   onMouseMove: $.empty,
1744   onMouseEnter: $.empty,
1745   onMouseLeave: $.empty,
1746   onDragStart: $.empty,
1747   onDragMove: $.empty,
1748   onDragCancel: $.empty,
1749   onDragEnd: $.empty,
1750   onTouchStart: $.empty,
1751   onTouchMove: $.empty,
1752   onTouchEnd: $.empty,
1753   onMouseWheel: $.empty
1754 };
1755
1756 /*
1757  * File: Options.Navigation.js
1758  *
1759 */
1760
1761 /*
1762   Object: Options.Navigation
1763   
1764   Panning and zooming options for Graph/Tree based visualizations. These options are implemented 
1765   by all visualizations except charts (<AreaChart>, <BarChart> and <PieChart>).
1766   
1767   Syntax:
1768   
1769   (start code js)
1770
1771   Options.Navigation = {
1772     enable: false,
1773     type: 'auto',
1774     panning: false, //true, 'avoid nodes'
1775     zooming: false
1776   };
1777   
1778   (end code)
1779   
1780   Example:
1781     
1782   (start code js)
1783   var viz = new $jit.Viz({
1784     Navigation: {
1785       enable: true,
1786       panning: 'avoid nodes',
1787       zooming: 20
1788     }
1789   });
1790   (end code)
1791   
1792   Parameters:
1793   
1794   enable - (boolean) Default's *false*. Whether to enable Navigation capabilities.
1795   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.
1796   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>.
1797   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.
1798   
1799 */
1800
1801 Options.Navigation = {
1802   $extend: false,
1803   
1804   enable: false,
1805   type: 'auto',
1806   panning: false, //true | 'avoid nodes'
1807   zooming: false
1808 };
1809
1810 /*
1811  * File: Options.Controller.js
1812  *
1813 */
1814
1815 /*
1816   Object: Options.Controller
1817   
1818   Provides controller methods. Controller methods are callback functions that get called at different stages 
1819   of the animation, computing or plotting of the visualization.
1820   
1821   Implemented by:
1822     
1823   All visualizations except charts (<AreaChart>, <BarChart> and <PieChart>).
1824   
1825   Syntax:
1826   
1827   (start code js)
1828
1829   Options.Controller = {
1830     onBeforeCompute: $.empty,
1831     onAfterCompute:  $.empty,
1832     onCreateLabel:   $.empty,
1833     onPlaceLabel:    $.empty,
1834     onComplete:      $.empty,
1835     onBeforePlotLine:$.empty,
1836     onAfterPlotLine: $.empty,
1837     onBeforePlotNode:$.empty,
1838     onAfterPlotNode: $.empty,
1839     request:         false
1840   };
1841   
1842   (end code)
1843   
1844   Example:
1845     
1846   (start code js)
1847   var viz = new $jit.Viz({
1848     onBeforePlotNode: function(node) {
1849       if(node.selected) {
1850         node.setData('color', '#ffc');
1851       } else {
1852         node.removeData('color');
1853       }
1854     },
1855     onBeforePlotLine: function(adj) {
1856       if(adj.nodeFrom.selected && adj.nodeTo.selected) {
1857         adj.setData('color', '#ffc');
1858       } else {
1859         adj.removeData('color');
1860       }
1861     },
1862     onAfterCompute: function() {
1863       alert("computed!");
1864     }
1865   });
1866   (end code)
1867   
1868   Parameters:
1869
1870    onBeforeCompute(node) - This method is called right before performing all computations and animations. The selected <Graph.Node> is passed as parameter.
1871    onAfterCompute() - This method is triggered after all animations or computations ended.
1872    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.
1873    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.
1874    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.
1875    onAfterPlotNode(node) - This method is triggered right after plotting each <Graph.Node>.
1876    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.
1877    onAfterPlotLine(adj) - This method is triggered right after plotting a <Graph.Adjacence>.
1878
1879     *Used in <ST>, <TM.Base> and <Icicle> visualizations*
1880     
1881     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.  
1882  
1883  */
1884 Options.Controller = {
1885   $extend: true,
1886   
1887   onBeforeCompute: $.empty,
1888   onAfterCompute:  $.empty,
1889   onCreateLabel:   $.empty,
1890   onPlaceLabel:    $.empty,
1891   onComplete:      $.empty,
1892   onBeforePlotLine:$.empty,
1893   onAfterPlotLine: $.empty,
1894   onBeforePlotNode:$.empty,
1895   onAfterPlotNode: $.empty,
1896   request:         false
1897 };
1898
1899
1900 /*
1901  * File: Extras.js
1902  * 
1903  * Provides Extras such as Tips and Style Effects.
1904  * 
1905  * Description:
1906  * 
1907  * Provides the <Tips> and <NodeStyles> classes and functions.
1908  *
1909  */
1910
1911 /*
1912  * Manager for mouse events (clicking and mouse moving).
1913  * 
1914  * This class is used for registering objects implementing onClick
1915  * and onMousemove methods. These methods are called when clicking or
1916  * moving the mouse around  the Canvas.
1917  * For now, <Tips> and <NodeStyles> are classes implementing these methods.
1918  * 
1919  */
1920 var ExtrasInitializer = {
1921   initialize: function(className, viz) {
1922     this.viz = viz;
1923     this.canvas = viz.canvas;
1924     this.config = viz.config[className];
1925     this.nodeTypes = viz.fx.nodeTypes;
1926     var type = this.config.type;
1927     this.dom = type == 'auto'? (viz.config.Label.type != 'Native') : (type != 'Native');
1928     this.labelContainer = this.dom && viz.labels.getLabelContainer();
1929     this.isEnabled() && this.initializePost();
1930   },
1931   initializePost: $.empty,
1932   setAsProperty: $.lambda(false),
1933   isEnabled: function() {
1934     return this.config.enable;
1935   },
1936   isLabel: function(e, win, group) {
1937     e = $.event.get(e, win);
1938     var labelContainer = this.labelContainer,
1939         target = e.target || e.srcElement,
1940         related = e.relatedTarget;
1941     if(group) {
1942       return related && related == this.viz.canvas.getCtx().canvas 
1943           && !!target && this.isDescendantOf(target, labelContainer);
1944     } else {
1945       return this.isDescendantOf(target, labelContainer);
1946     }
1947   },
1948   isDescendantOf: function(elem, par) {
1949     while(elem && elem.parentNode) {
1950       if(elem.parentNode == par)
1951         return elem;
1952       elem = elem.parentNode;
1953     }
1954     return false;
1955   }
1956 };
1957
1958 var EventsInterface = {
1959   onMouseUp: $.empty,
1960   onMouseDown: $.empty,
1961   onMouseMove: $.empty,
1962   onMouseOver: $.empty,
1963   onMouseOut: $.empty,
1964   onMouseWheel: $.empty,
1965   onTouchStart: $.empty,
1966   onTouchMove: $.empty,
1967   onTouchEnd: $.empty,
1968   onTouchCancel: $.empty
1969 };
1970
1971 var MouseEventsManager = new Class({
1972   initialize: function(viz) {
1973     this.viz = viz;
1974     this.canvas = viz.canvas;
1975     this.node = false;
1976     this.edge = false;
1977     this.registeredObjects = [];
1978     this.attachEvents();
1979   },
1980   
1981   attachEvents: function() {
1982     var htmlCanvas = this.canvas.getElement(), 
1983         that = this;
1984     htmlCanvas.oncontextmenu = $.lambda(false);
1985     $.addEvents(htmlCanvas, {
1986       'mouseup': function(e, win) {
1987         var event = $.event.get(e, win);
1988         that.handleEvent('MouseUp', e, win, 
1989             that.makeEventObject(e, win), 
1990             $.event.isRightClick(event));
1991       },
1992       'mousedown': function(e, win) {
1993         var event = $.event.get(e, win);
1994         that.handleEvent('MouseDown', e, win, that.makeEventObject(e, win), 
1995             $.event.isRightClick(event));
1996       },
1997       'mousemove': function(e, win) {
1998         that.handleEvent('MouseMove', e, win, that.makeEventObject(e, win));
1999       },
2000       'mouseover': function(e, win) {
2001         that.handleEvent('MouseOver', e, win, that.makeEventObject(e, win));
2002       },
2003       'mouseout': function(e, win) {
2004         that.handleEvent('MouseOut', e, win, that.makeEventObject(e, win));
2005       },
2006       'touchstart': function(e, win) {
2007         that.handleEvent('TouchStart', e, win, that.makeEventObject(e, win));
2008       },
2009       'touchmove': function(e, win) {
2010         that.handleEvent('TouchMove', e, win, that.makeEventObject(e, win));
2011       },
2012       'touchend': function(e, win) {
2013         that.handleEvent('TouchEnd', e, win, that.makeEventObject(e, win));
2014       }
2015     });
2016     //attach mousewheel event
2017     var handleMouseWheel = function(e, win) {
2018       var event = $.event.get(e, win);
2019       var wheel = $.event.getWheel(event);
2020       that.handleEvent('MouseWheel', e, win, wheel);
2021     };
2022     //TODO(nico): this is a horrible check for non-gecko browsers!
2023     if(!document.getBoxObjectFor && window.mozInnerScreenX == null) {
2024       $.addEvent(htmlCanvas, 'mousewheel', handleMouseWheel);
2025     } else {
2026       htmlCanvas.addEventListener('DOMMouseScroll', handleMouseWheel, false);
2027     }
2028   },
2029   
2030   register: function(obj) {
2031     this.registeredObjects.push(obj);
2032   },
2033   
2034   handleEvent: function() {
2035     var args = Array.prototype.slice.call(arguments),
2036         type = args.shift();
2037     for(var i=0, regs=this.registeredObjects, l=regs.length; i<l; i++) {
2038       regs[i]['on' + type].apply(regs[i], args);
2039     }
2040   },
2041   
2042   makeEventObject: function(e, win) {
2043     var that = this,
2044         graph = this.viz.graph,
2045         fx = this.viz.fx,
2046         ntypes = fx.nodeTypes,
2047         etypes = fx.edgeTypes;
2048     return {
2049       pos: false,
2050       node: false,
2051       edge: false,
2052       contains: false,
2053       getNodeCalled: false,
2054       getEdgeCalled: false,
2055       getPos: function() {
2056         //TODO(nico): check why this can't be cache anymore when using edge detection
2057         //if(this.pos) return this.pos;
2058         var canvas = that.viz.canvas,
2059             s = canvas.getSize(),
2060             p = canvas.getPos(),
2061             ox = canvas.translateOffsetX,
2062             oy = canvas.translateOffsetY,
2063             sx = canvas.scaleOffsetX,
2064             sy = canvas.scaleOffsetY,
2065             pos = $.event.getPos(e, win);
2066         this.pos = {
2067           x: (pos.x - p.x - s.width/2 - ox) * 1/sx,
2068           y: (pos.y - p.y - s.height/2 - oy) * 1/sy
2069         };
2070         return this.pos;
2071       },
2072       getNode: function() {
2073         if(this.getNodeCalled) return this.node;
2074         this.getNodeCalled = true;
2075         for(var id in graph.nodes) {
2076           var n = graph.nodes[id],
2077               geom = n && ntypes[n.getData('type')],
2078               contains = geom && geom.contains && geom.contains.call(fx, n, this.getPos());
2079           if(contains) {
2080             this.contains = contains;
2081             return that.node = this.node = n;
2082           }
2083         }
2084         return that.node = this.node = false;
2085       },
2086       getEdge: function() {
2087         if(this.getEdgeCalled) return this.edge;
2088         this.getEdgeCalled = true;
2089         var hashset = {};
2090         for(var id in graph.edges) {
2091           var edgeFrom = graph.edges[id];
2092           hashset[id] = true;
2093           for(var edgeId in edgeFrom) {
2094             if(edgeId in hashset) continue;
2095             var e = edgeFrom[edgeId],
2096                 geom = e && etypes[e.getData('type')],
2097                 contains = geom && geom.contains && geom.contains.call(fx, e, this.getPos());
2098             if(contains) {
2099               this.contains = contains;
2100               return that.edge = this.edge = e;
2101             }
2102           }
2103         }
2104         return that.edge = this.edge = false;
2105       },
2106       getContains: function() {
2107         if(this.getNodeCalled) return this.contains;
2108         this.getNode();
2109         return this.contains;
2110       }
2111     };
2112   }
2113 });
2114
2115 /* 
2116  * Provides the initialization function for <NodeStyles> and <Tips> implemented 
2117  * by all main visualizations.
2118  *
2119  */
2120 var Extras = {
2121   initializeExtras: function() {
2122     var mem = new MouseEventsManager(this), that = this;
2123     $.each(['NodeStyles', 'Tips', 'Navigation', 'Events'], function(k) {
2124       var obj = new Extras.Classes[k](k, that);
2125       if(obj.isEnabled()) {
2126         mem.register(obj);
2127       }
2128       if(obj.setAsProperty()) {
2129         that[k.toLowerCase()] = obj;
2130       }
2131     });
2132   }   
2133 };
2134
2135 Extras.Classes = {};
2136 /*
2137   Class: Events
2138    
2139   This class defines an Event API to be accessed by the user.
2140   The methods implemented are the ones defined in the <Options.Events> object.
2141 */
2142
2143 Extras.Classes.Events = new Class({
2144   Implements: [ExtrasInitializer, EventsInterface],
2145   
2146   initializePost: function() {
2147     this.fx = this.viz.fx;
2148     this.ntypes = this.viz.fx.nodeTypes;
2149     this.etypes = this.viz.fx.edgeTypes;
2150     
2151     this.hovered = false;
2152     this.pressed = false;
2153     this.touched = false;
2154
2155     this.touchMoved = false;
2156     this.moved = false;
2157     
2158   },
2159   
2160   setAsProperty: $.lambda(true),
2161   
2162   onMouseUp: function(e, win, event, isRightClick) {
2163     var evt = $.event.get(e, win);
2164     if(!this.moved) {
2165       if(isRightClick) {
2166         this.config.onRightClick(this.hovered, event, evt);
2167       } else {
2168         this.config.onClick(this.pressed, event, evt);
2169       }
2170     }
2171     if(this.pressed) {
2172       if(this.moved) {
2173         this.config.onDragEnd(this.pressed, event, evt);
2174       } else {
2175         this.config.onDragCancel(this.pressed, event, evt);
2176       }
2177       this.pressed = this.moved = false;
2178     }
2179   },
2180
2181   onMouseOut: function(e, win, event) {
2182    //mouseout a label
2183    var evt = $.event.get(e, win), label;
2184    if(this.dom && (label = this.isLabel(e, win, true))) {
2185      this.config.onMouseLeave(this.viz.graph.getNode(label.id),
2186                               event, evt);
2187      this.hovered = false;
2188      return;
2189    }
2190    //mouseout canvas
2191    var rt = evt.relatedTarget,
2192        canvasWidget = this.canvas.getElement();
2193    while(rt && rt.parentNode) {
2194      if(canvasWidget == rt.parentNode) return;
2195      rt = rt.parentNode;
2196    }
2197    if(this.hovered) {
2198      this.config.onMouseLeave(this.hovered,
2199          event, evt);
2200      this.hovered = false;
2201    }
2202   },
2203   
2204   onMouseOver: function(e, win, event) {
2205     //mouseover a label
2206     var evt = $.event.get(e, win), label;
2207     if(this.dom && (label = this.isLabel(e, win, true))) {
2208       this.hovered = this.viz.graph.getNode(label.id);
2209       this.config.onMouseEnter(this.hovered,
2210                                event, evt);
2211     }
2212   },
2213   
2214   onMouseMove: function(e, win, event) {
2215    var label, evt = $.event.get(e, win);
2216    if(this.pressed) {
2217      this.moved = true;
2218      this.config.onDragMove(this.pressed, event, evt);
2219      return;
2220    }
2221    if(this.dom) {
2222      this.config.onMouseMove(this.hovered,
2223          event, evt);
2224    } else {
2225      if(this.hovered) {
2226        var hn = this.hovered;
2227        var geom = hn.nodeFrom? this.etypes[hn.getData('type')] : this.ntypes[hn.getData('type')];
2228        var contains = geom && geom.contains 
2229          && geom.contains.call(this.fx, hn, event.getPos());
2230        if(contains) {
2231          this.config.onMouseMove(hn, event, evt);
2232          return;
2233        } else {
2234          this.config.onMouseLeave(hn, event, evt);
2235          this.hovered = false;
2236        }
2237      }
2238      if(this.hovered = (event.getNode() || (this.config.enableForEdges && event.getEdge()))) {
2239        this.config.onMouseEnter(this.hovered, event, evt);
2240      } else {
2241        this.config.onMouseMove(false, event, evt);
2242      }
2243    }
2244   },
2245   
2246   onMouseWheel: function(e, win, delta) {
2247     this.config.onMouseWheel(delta, $.event.get(e, win));
2248   },
2249   
2250   onMouseDown: function(e, win, event) {
2251     var evt = $.event.get(e, win), label;
2252     if(this.dom) {
2253       if(label = this.isLabel(e, win)) {
2254         this.pressed = this.viz.graph.getNode(label.id);
2255       }
2256     } else {
2257       this.pressed = event.getNode() || (this.config.enableForEdges && event.getEdge());
2258     }
2259     this.pressed && this.config.onDragStart(this.pressed, event, evt);
2260   },
2261   
2262   onTouchStart: function(e, win, event) {
2263     var evt = $.event.get(e, win), label;
2264     if(this.dom && (label = this.isLabel(e, win))) {
2265       this.touched = this.viz.graph.getNode(label.id);
2266     } else {
2267       this.touched = event.getNode() || (this.config.enableForEdges && event.getEdge());
2268     }
2269     this.touched && this.config.onTouchStart(this.touched, event, evt);
2270   },
2271   
2272   onTouchMove: function(e, win, event) {
2273     var evt = $.event.get(e, win);
2274     if(this.touched) {
2275       this.touchMoved = true;
2276       this.config.onTouchMove(this.touched, event, evt);
2277     }
2278   },
2279   
2280   onTouchEnd: function(e, win, event) {
2281     var evt = $.event.get(e, win);
2282     if(this.touched) {
2283       if(this.touchMoved) {
2284         this.config.onTouchEnd(this.touched, event, evt);
2285       } else {
2286         this.config.onTouchCancel(this.touched, event, evt);
2287       }
2288       this.touched = this.touchMoved = false;
2289     }
2290   }
2291 });
2292
2293 /*
2294    Class: Tips
2295     
2296    A class containing tip related functions. This class is used internally.
2297    
2298    Used by:
2299    
2300    <ST>, <Sunburst>, <Hypertree>, <RGraph>, <TM>, <ForceDirected>, <Icicle>
2301    
2302    See also:
2303    
2304    <Options.Tips>
2305 */
2306
2307 Extras.Classes.Tips = new Class({
2308   Implements: [ExtrasInitializer, EventsInterface],
2309   
2310   initializePost: function() {
2311     //add DOM tooltip
2312     if(document.body) {
2313       var tip = $('_tooltip') || document.createElement('div');
2314       tip.id = '_tooltip';
2315       tip.className = 'tip';
2316       $.extend(tip.style, {
2317         position: 'absolute',
2318         display: 'none',
2319         zIndex: 13000
2320       });
2321       document.body.appendChild(tip);
2322       this.tip = tip;
2323       this.node = false;
2324     }
2325   },
2326   
2327   setAsProperty: $.lambda(true),
2328   
2329   onMouseOut: function(e, win) {
2330     //mouseout a label
2331     var evt = $.event.get(e, win);
2332     if(this.dom && this.isLabel(e, win, true)) {
2333       this.hide(true);
2334       return;
2335     }
2336     //mouseout canvas
2337     var rt = e.relatedTarget,
2338         canvasWidget = this.canvas.getElement();
2339     while(rt && rt.parentNode) {
2340       if(canvasWidget == rt.parentNode) return;
2341       rt = rt.parentNode;
2342     }
2343     this.hide(false);
2344   },
2345   
2346   onMouseOver: function(e, win) {
2347     //mouseover a label
2348     var label;
2349     if(this.dom && (label = this.isLabel(e, win, false))) {
2350       this.node = this.viz.graph.getNode(label.id);
2351       this.config.onShow(this.tip, this.node, label);
2352     }
2353   },
2354   
2355   onMouseMove: function(e, win, opt) {
2356     if(this.dom && this.isLabel(e, win)) {
2357       this.setTooltipPosition($.event.getPos(e, win));
2358     }
2359     if(!this.dom) {
2360       var node = opt.getNode();
2361       if(!node) {
2362         this.hide(true);
2363         return;
2364       }
2365       if(this.config.force || !this.node || this.node.id != node.id) {
2366         this.node = node;
2367         this.config.onShow(this.tip, node, opt.getContains());
2368       }
2369       this.setTooltipPosition($.event.getPos(e, win));
2370     }
2371   },
2372   
2373   setTooltipPosition: function(pos) {
2374     var tip = this.tip, 
2375         style = tip.style, 
2376         cont = this.config;
2377     style.display = '';
2378     //get window dimensions
2379     var win = {
2380       'height': document.body.clientHeight,
2381       'width': document.body.clientWidth
2382     };
2383     //get tooltip dimensions
2384     var obj = {
2385       'width': tip.offsetWidth,
2386       'height': tip.offsetHeight  
2387     };
2388     //set tooltip position
2389     var x = cont.offsetX, y = cont.offsetY;
2390     style.top = ((pos.y + y + obj.height > win.height)?  
2391         (pos.y - obj.height - y) : pos.y + y) + 'px';
2392     style.left = ((pos.x + obj.width + x > win.width)? 
2393         (pos.x - obj.width - x) : pos.x + x) + 'px';
2394   },
2395   
2396   hide: function(triggerCallback) {
2397     this.tip.style.display = 'none';
2398     triggerCallback && this.config.onHide();
2399   }
2400 });
2401
2402 /*
2403   Class: NodeStyles
2404    
2405   Change node styles when clicking or hovering a node. This class is used internally.
2406   
2407   Used by:
2408   
2409   <ST>, <Sunburst>, <Hypertree>, <RGraph>, <TM>, <ForceDirected>, <Icicle>
2410   
2411   See also:
2412   
2413   <Options.NodeStyles>
2414 */
2415 Extras.Classes.NodeStyles = new Class({
2416   Implements: [ExtrasInitializer, EventsInterface],
2417   
2418   initializePost: function() {
2419     this.fx = this.viz.fx;
2420     this.types = this.viz.fx.nodeTypes;
2421     this.nStyles = this.config;
2422     this.nodeStylesOnHover = this.nStyles.stylesHover;
2423     this.nodeStylesOnClick = this.nStyles.stylesClick;
2424     this.hoveredNode = false;
2425     this.fx.nodeFxAnimation = new Animation();
2426     
2427     this.down = false;
2428     this.move = false;
2429   },
2430   
2431   onMouseOut: function(e, win) {
2432     this.down = this.move = false;
2433     if(!this.hoveredNode) return;
2434     //mouseout a label
2435     if(this.dom && this.isLabel(e, win, true)) {
2436       this.toggleStylesOnHover(this.hoveredNode, false);
2437     }
2438     //mouseout canvas
2439     var rt = e.relatedTarget,
2440         canvasWidget = this.canvas.getElement();
2441     while(rt && rt.parentNode) {
2442       if(canvasWidget == rt.parentNode) return;
2443       rt = rt.parentNode;
2444     }
2445     this.toggleStylesOnHover(this.hoveredNode, false);
2446     this.hoveredNode = false;
2447   },
2448   
2449   onMouseOver: function(e, win) {
2450     //mouseover a label
2451     var label;
2452     if(this.dom && (label = this.isLabel(e, win, true))) {
2453       var node = this.viz.graph.getNode(label.id);
2454       if(node.selected) return;
2455       this.hoveredNode = node;
2456       this.toggleStylesOnHover(this.hoveredNode, true);
2457     }
2458   },
2459   
2460   onMouseDown: function(e, win, event, isRightClick) {
2461     if(isRightClick) return;
2462     var label;
2463     if(this.dom && (label = this.isLabel(e, win))) {
2464       this.down = this.viz.graph.getNode(label.id);
2465     } else if(!this.dom) {
2466       this.down = event.getNode();
2467     }
2468     this.move = false;
2469   },
2470   
2471   onMouseUp: function(e, win, event, isRightClick) {
2472     if(isRightClick) return;
2473     if(!this.move) {
2474       this.onClick(event.getNode());
2475     }
2476     this.down = this.move = false;
2477   },
2478   
2479   getRestoredStyles: function(node, type) {
2480     var restoredStyles = {}, 
2481         nStyles = this['nodeStylesOn' + type];
2482     for(var prop in nStyles) {
2483       restoredStyles[prop] = node.styles['$' + prop];
2484     }
2485     return restoredStyles;
2486   },
2487   
2488   toggleStylesOnHover: function(node, set) {
2489     if(this.nodeStylesOnHover) {
2490       this.toggleStylesOn('Hover', node, set);
2491     }
2492   },
2493
2494   toggleStylesOnClick: function(node, set) {
2495     if(this.nodeStylesOnClick) {
2496       this.toggleStylesOn('Click', node, set);
2497     }
2498   },
2499   
2500   toggleStylesOn: function(type, node, set) {
2501     var viz = this.viz;
2502     var nStyles = this.nStyles;
2503     if(set) {
2504       var that = this;
2505       if(!node.styles) {
2506         node.styles = $.merge(node.data, {});
2507       }
2508       for(var s in this['nodeStylesOn' + type]) {
2509         var $s = '$' + s;
2510         if(!($s in node.styles)) {
2511             node.styles[$s] = node.getData(s); 
2512         }
2513       }
2514       viz.fx.nodeFx($.extend({
2515         'elements': {
2516           'id': node.id,
2517           'properties': that['nodeStylesOn' + type]
2518          },
2519          transition: Trans.Quart.easeOut,
2520          duration:300,
2521          fps:40
2522       }, this.config));
2523     } else {
2524       var restoredStyles = this.getRestoredStyles(node, type);
2525       viz.fx.nodeFx($.extend({
2526         'elements': {
2527           'id': node.id,
2528           'properties': restoredStyles
2529          },
2530          transition: Trans.Quart.easeOut,
2531          duration:300,
2532          fps:40
2533       }, this.config));
2534     }
2535   },
2536
2537   onClick: function(node) {
2538     if(!node) return;
2539     var nStyles = this.nodeStylesOnClick;
2540     if(!nStyles) return;
2541     //if the node is selected then unselect it
2542     if(node.selected) {
2543       this.toggleStylesOnClick(node, false);
2544       delete node.selected;
2545     } else {
2546       //unselect all selected nodes...
2547       this.viz.graph.eachNode(function(n) {
2548         if(n.selected) {
2549           for(var s in nStyles) {
2550             n.setData(s, n.styles['$' + s], 'end');
2551           }
2552           delete n.selected;
2553         }
2554       });
2555       //select clicked node
2556       this.toggleStylesOnClick(node, true);
2557       node.selected = true;
2558       delete node.hovered;
2559       this.hoveredNode = false;
2560     }
2561   },
2562   
2563   onMouseMove: function(e, win, event) {
2564     //if mouse button is down and moving set move=true
2565     if(this.down) this.move = true;
2566     //already handled by mouseover/out
2567     if(this.dom && this.isLabel(e, win)) return;
2568     var nStyles = this.nodeStylesOnHover;
2569     if(!nStyles) return;
2570     
2571     if(!this.dom) {
2572       if(this.hoveredNode) {
2573         var geom = this.types[this.hoveredNode.getData('type')];
2574         var contains = geom && geom.contains && geom.contains.call(this.fx, 
2575             this.hoveredNode, event.getPos());
2576         if(contains) return;
2577       }
2578       var node = event.getNode();
2579       //if no node is being hovered then just exit
2580       if(!this.hoveredNode && !node) return;
2581       //if the node is hovered then exit
2582       if(node.hovered) return;
2583       //select hovered node
2584       if(node && !node.selected) {
2585         //check if an animation is running and exit it
2586         this.fx.nodeFxAnimation.stopTimer();
2587         //unselect all hovered nodes...
2588         this.viz.graph.eachNode(function(n) {
2589           if(n.hovered && !n.selected) {
2590             for(var s in nStyles) {
2591               n.setData(s, n.styles['$' + s], 'end');
2592             }
2593             delete n.hovered;
2594           }
2595         });
2596         //select hovered node
2597         node.hovered = true;
2598         this.hoveredNode = node;
2599         this.toggleStylesOnHover(node, true);
2600       } else if(this.hoveredNode && !this.hoveredNode.selected) {
2601         //check if an animation is running and exit it
2602         this.fx.nodeFxAnimation.stopTimer();
2603         //unselect hovered node
2604         this.toggleStylesOnHover(this.hoveredNode, false);
2605         delete this.hoveredNode.hovered;
2606         this.hoveredNode = false;
2607       }
2608     }
2609   }
2610 });
2611
2612 Extras.Classes.Navigation = new Class({
2613   Implements: [ExtrasInitializer, EventsInterface],
2614   
2615   initializePost: function() {
2616     this.pos = false;
2617     this.pressed = false;
2618   },
2619   
2620   onMouseWheel: function(e, win, scroll) {
2621     if(!this.config.zooming) return;
2622     $.event.stop($.event.get(e, win));
2623     var val = this.config.zooming / 1000,
2624         ans = 1 + scroll * val;
2625     this.canvas.scale(ans, ans);
2626   },
2627   
2628   onMouseDown: function(e, win, eventInfo) {
2629     if(!this.config.panning) return;
2630     if(this.config.panning == 'avoid nodes' && (this.dom? this.isLabel(e, win) : eventInfo.getNode())) return;
2631     this.pressed = true;
2632     this.pos = eventInfo.getPos();
2633     var canvas = this.canvas,
2634         ox = canvas.translateOffsetX,
2635         oy = canvas.translateOffsetY,
2636         sx = canvas.scaleOffsetX,
2637         sy = canvas.scaleOffsetY;
2638     this.pos.x *= sx;
2639     this.pos.x += ox;
2640     this.pos.y *= sy;
2641     this.pos.y += oy;
2642   },
2643   
2644   onMouseMove: function(e, win, eventInfo) {
2645     if(!this.config.panning) return;
2646     if(!this.pressed) return;
2647     if(this.config.panning == 'avoid nodes' && (this.dom? this.isLabel(e, win) : eventInfo.getNode())) return;
2648     var thispos = this.pos, 
2649         currentPos = eventInfo.getPos(),
2650         canvas = this.canvas,
2651         ox = canvas.translateOffsetX,
2652         oy = canvas.translateOffsetY,
2653         sx = canvas.scaleOffsetX,
2654         sy = canvas.scaleOffsetY;
2655     currentPos.x *= sx;
2656     currentPos.y *= sy;
2657     currentPos.x += ox;
2658     currentPos.y += oy;
2659     var x = currentPos.x - thispos.x,
2660         y = currentPos.y - thispos.y;
2661     this.pos = currentPos;
2662     this.canvas.translate(x * 1/sx, y * 1/sy);
2663   },
2664   
2665   onMouseUp: function(e, win, eventInfo, isRightClick) {
2666     if(!this.config.panning) return;
2667     this.pressed = false;
2668   }
2669 });
2670
2671
2672 /*
2673  * File: Canvas.js
2674  *
2675  */
2676
2677 /*
2678  Class: Canvas
2679  
2680         A canvas widget used by all visualizations. The canvas object can be accessed by doing *viz.canvas*. If you want to 
2681         know more about <Canvas> options take a look at <Options.Canvas>.
2682  
2683  A canvas widget is a set of DOM elements that wrap the native canvas DOM Element providing a consistent API and behavior 
2684  across all browsers. It can also include Elements to add DOM (SVG or HTML) label support to all visualizations.
2685  
2686  Example:
2687  
2688  Suppose we have this HTML
2689  
2690  (start code xml)
2691         <div id="infovis"></div>
2692  (end code)
2693  
2694  Now we create a new Visualization
2695  
2696  (start code js)
2697         var viz = new $jit.Viz({
2698                 //Where to inject the canvas. Any div container will do.
2699                 'injectInto':'infovis',
2700                  //width and height for canvas. 
2701                  //Default's to the container offsetWidth and Height.
2702                  'width': 900,
2703                  'height':500
2704          });
2705  (end code)
2706
2707  The generated HTML will look like this
2708  
2709  (start code xml)
2710  <div id="infovis">
2711         <div id="infovis-canvaswidget" style="position:relative;">
2712         <canvas id="infovis-canvas" width=900 height=500
2713         style="position:absolute; top:0; left:0; width:900px; height:500px;" />
2714         <div id="infovis-label"
2715         style="overflow:visible; position:absolute; top:0; left:0; width:900px; height:0px">
2716         </div>
2717         </div>
2718  </div>
2719  (end code)
2720  
2721  As you can see, the generated HTML consists of a canvas DOM Element of id *infovis-canvas* and a div label container
2722  of id *infovis-label*, wrapped in a main div container of id *infovis-canvaswidget*.
2723  */
2724
2725 var Canvas;
2726 (function() {
2727   //check for native canvas support
2728   var canvasType = typeof HTMLCanvasElement,
2729       supportsCanvas = (canvasType == 'object' || canvasType == 'function');
2730   //create element function
2731   function $E(tag, props) {
2732     var elem = document.createElement(tag);
2733     for(var p in props) {
2734       if(typeof props[p] == "object") {
2735         $.extend(elem[p], props[p]);
2736       } else {
2737         elem[p] = props[p];
2738       }
2739     }
2740     if (tag == "canvas" && !supportsCanvas && G_vmlCanvasManager) {
2741       elem = G_vmlCanvasManager.initElement(document.body.appendChild(elem));
2742     }
2743     return elem;
2744   }
2745   //canvas widget which we will call just Canvas
2746   $jit.Canvas = Canvas = new Class({
2747     canvases: [],
2748     pos: false,
2749     element: false,
2750     labelContainer: false,
2751     translateOffsetX: 0,
2752     translateOffsetY: 0,
2753     scaleOffsetX: 1,
2754     scaleOffsetY: 1,
2755     
2756     initialize: function(viz, opt) {
2757       this.viz = viz;
2758       this.opt = this.config = opt;
2759       var id = $.type(opt.injectInto) == 'string'? 
2760           opt.injectInto:opt.injectInto.id,
2761           type = opt.type,
2762           idLabel = id + "-label", 
2763           wrapper = $(id),
2764           width = opt.width || wrapper.offsetWidth,
2765           height = opt.height || wrapper.offsetHeight;
2766       this.id = id;
2767       //canvas options
2768       var canvasOptions = {
2769         injectInto: id,
2770         width: width,
2771         height: height
2772       };
2773       //create main wrapper
2774       this.element = $E('div', {
2775         'id': id + '-canvaswidget',
2776         'style': {
2777           'position': 'relative',
2778           'width': width + 'px',
2779           'height': height + 'px'
2780         }
2781       });
2782       //create label container
2783       this.labelContainer = this.createLabelContainer(opt.Label.type, 
2784           idLabel, canvasOptions);
2785       //create primary canvas
2786       this.canvases.push(new Canvas.Base[type]({
2787         config: $.extend({idSuffix: '-canvas'}, canvasOptions),
2788         plot: function(base) {
2789           viz.fx.plot();
2790         },
2791         resize: function() {
2792           viz.refresh();
2793         }
2794       }));
2795       //create secondary canvas
2796       var back = opt.background;
2797       if(back) {
2798         var backCanvas = new Canvas.Background[back.type](viz, $.extend(back, canvasOptions));
2799         this.canvases.push(new Canvas.Base[type](backCanvas));
2800       }
2801       //insert canvases
2802       var len = this.canvases.length;
2803       while(len--) {
2804         this.element.appendChild(this.canvases[len].canvas);
2805         if(len > 0) {
2806           this.canvases[len].plot();
2807         }
2808       }
2809       this.element.appendChild(this.labelContainer);
2810       wrapper.appendChild(this.element);
2811       //Update canvas position when the page is scrolled.
2812       var timer = null, that = this;
2813       $.addEvent(window, 'scroll', function() {
2814         clearTimeout(timer);
2815         timer = setTimeout(function() {
2816           that.getPos(true); //update canvas position
2817         }, 500);
2818       });
2819     },
2820     /*
2821       Method: getCtx
2822       
2823       Returns the main canvas context object
2824       
2825       Example:
2826       
2827       (start code js)
2828        var ctx = canvas.getCtx();
2829        //Now I can use the native canvas context
2830        //and for example change some canvas styles
2831        ctx.globalAlpha = 1;
2832       (end code)
2833     */
2834     getCtx: function(i) {
2835       return this.canvases[i || 0].getCtx();
2836     },
2837     /*
2838       Method: getConfig
2839       
2840       Returns the current Configuration for this Canvas Widget.
2841       
2842       Example:
2843       
2844       (start code js)
2845        var config = canvas.getConfig();
2846       (end code)
2847     */
2848     getConfig: function() {
2849       return this.opt;
2850     },
2851     /*
2852       Method: getElement
2853
2854       Returns the main Canvas DOM wrapper
2855       
2856       Example:
2857       
2858       (start code js)
2859        var wrapper = canvas.getElement();
2860        //Returns <div id="infovis-canvaswidget" ... >...</div> as element
2861       (end code)
2862     */
2863     getElement: function() {
2864       return this.element;
2865     },
2866     /*
2867       Method: getSize
2868       
2869       Returns canvas dimensions.
2870       
2871       Returns:
2872       
2873       An object with *width* and *height* properties.
2874       
2875       Example:
2876       (start code js)
2877       canvas.getSize(); //returns { width: 900, height: 500 }
2878       (end code)
2879     */
2880     getSize: function(i) {
2881       return this.canvases[i || 0].getSize();
2882     },
2883     /*
2884       Method: resize
2885       
2886       Resizes the canvas.
2887       
2888       Parameters:
2889       
2890       width - New canvas width.
2891       height - New canvas height.
2892       
2893       Example:
2894       
2895       (start code js)
2896        canvas.resize(width, height);
2897       (end code)
2898     
2899     */
2900     resize: function(width, height) {
2901       this.getPos(true);
2902       this.translateOffsetX = this.translateOffsetY = 0;
2903       this.scaleOffsetX = this.scaleOffsetY = 1;
2904       for(var i=0, l=this.canvases.length; i<l; i++) {
2905         this.canvases[i].resize(width, height);
2906       }
2907       var style = this.element.style;
2908       style.width = width + 'px';
2909       style.height = height + 'px';
2910       if(this.labelContainer)
2911         this.labelContainer.style.width = width + 'px';
2912     },
2913     /*
2914       Method: translate
2915       
2916       Applies a translation to the canvas.
2917       
2918       Parameters:
2919       
2920       x - (number) x offset.
2921       y - (number) y offset.
2922       disablePlot - (boolean) Default's *false*. Set this to *true* if you don't want to refresh the visualization.
2923       
2924       Example:
2925       
2926       (start code js)
2927        canvas.translate(30, 30);
2928       (end code)
2929     
2930     */
2931     translate: function(x, y, disablePlot) {
2932       this.translateOffsetX += x*this.scaleOffsetX;
2933       this.translateOffsetY += y*this.scaleOffsetY;
2934       for(var i=0, l=this.canvases.length; i<l; i++) {
2935         this.canvases[i].translate(x, y, disablePlot);
2936       }
2937     },
2938     /*
2939       Method: scale
2940       
2941       Scales the canvas.
2942       
2943       Parameters:
2944       
2945       x - (number) scale value.
2946       y - (number) scale value.
2947       disablePlot - (boolean) Default's *false*. Set this to *true* if you don't want to refresh the visualization.
2948       
2949       Example:
2950       
2951       (start code js)
2952        canvas.scale(0.5, 0.5);
2953       (end code)
2954     
2955     */
2956     scale: function(x, y, disablePlot) {
2957       var px = this.scaleOffsetX * x,
2958           py = this.scaleOffsetY * y;
2959       var dx = this.translateOffsetX * (x -1) / px,
2960           dy = this.translateOffsetY * (y -1) / py;
2961       this.scaleOffsetX = px;
2962       this.scaleOffsetY = py;
2963       for(var i=0, l=this.canvases.length; i<l; i++) {
2964         this.canvases[i].scale(x, y, true);
2965       }
2966       this.translate(dx, dy, false);
2967     },
2968     /*
2969       Method: getPos
2970       
2971       Returns the canvas position as an *x, y* object.
2972       
2973       Parameters:
2974       
2975       force - (boolean) Default's *false*. Set this to *true* if you want to recalculate the position without using any cache information.
2976       
2977       Returns:
2978       
2979       An object with *x* and *y* properties.
2980       
2981       Example:
2982       (start code js)
2983       canvas.getPos(true); //returns { x: 900, y: 500 }
2984       (end code)
2985     */
2986     getPos: function(force){
2987       if(force || !this.pos) {
2988         return this.pos = $.getPos(this.getElement());
2989       }
2990       return this.pos;
2991     },
2992     /*
2993        Method: clear
2994        
2995        Clears the canvas.
2996     */
2997     clear: function(i){
2998       this.canvases[i||0].clear();
2999     },
3000     
3001     path: function(type, action){
3002       var ctx = this.canvases[0].getCtx();
3003       ctx.beginPath();
3004       action(ctx);
3005       ctx[type]();
3006       ctx.closePath();
3007     },
3008     
3009     createLabelContainer: function(type, idLabel, dim) {
3010       var NS = 'http://www.w3.org/2000/svg';
3011       if(type == 'HTML' || type == 'Native') {
3012         return $E('div', {
3013           'id': idLabel,
3014           'style': {
3015             'overflow': 'visible',
3016             'position': 'absolute',
3017             'top': 0,
3018             'left': 0,
3019             'width': dim.width + 'px',
3020             'height': 0
3021           }
3022         });
3023       } else if(type == 'SVG') {
3024         var svgContainer = document.createElementNS(NS, 'svg:svg');
3025         svgContainer.setAttribute("width", dim.width);
3026         svgContainer.setAttribute('height', dim.height);
3027         var style = svgContainer.style;
3028         style.position = 'absolute';
3029         style.left = style.top = '0px';
3030         var labelContainer = document.createElementNS(NS, 'svg:g');
3031         labelContainer.setAttribute('width', dim.width);
3032         labelContainer.setAttribute('height', dim.height);
3033         labelContainer.setAttribute('x', 0);
3034         labelContainer.setAttribute('y', 0);
3035         labelContainer.setAttribute('id', idLabel);
3036         svgContainer.appendChild(labelContainer);
3037         return svgContainer;
3038       }
3039     }
3040   });
3041   //base canvas wrapper
3042   Canvas.Base = {};
3043   Canvas.Base['2D'] = new Class({
3044     translateOffsetX: 0,
3045     translateOffsetY: 0,
3046     scaleOffsetX: 1,
3047     scaleOffsetY: 1,
3048
3049     initialize: function(viz) {
3050       this.viz = viz;
3051       this.opt = viz.config;
3052       this.size = false;
3053       this.createCanvas();
3054       this.translateToCenter();
3055     },
3056     createCanvas: function() {
3057       var opt = this.opt,
3058           width = opt.width,
3059           height = opt.height;
3060       this.canvas = $E('canvas', {
3061         'id': opt.injectInto + opt.idSuffix,
3062         'width': width,
3063         'height': height,
3064         'style': {
3065           'position': 'absolute',
3066           'top': 0,
3067           'left': 0,
3068           'width': width + 'px',
3069           'height': height + 'px'
3070         }
3071       });
3072     },
3073     getCtx: function() {
3074       if(!this.ctx) 
3075         return this.ctx = this.canvas.getContext('2d');
3076       return this.ctx;
3077     },
3078     getSize: function() {
3079       if(this.size) return this.size;
3080       var canvas = this.canvas;
3081       return this.size = {
3082         width: canvas.width,
3083         height: canvas.height
3084       };
3085     },
3086     translateToCenter: function(ps) {
3087       var size = this.getSize(),
3088           width = ps? (size.width - ps.width - this.translateOffsetX*2) : size.width;
3089           height = ps? (size.height - ps.height - this.translateOffsetY*2) : size.height;
3090       var ctx = this.getCtx();
3091       ps && ctx.scale(1/this.scaleOffsetX, 1/this.scaleOffsetY);
3092       ctx.translate(width/2, height/2);
3093     },
3094     resize: function(width, height) {
3095       var size = this.getSize(),
3096           canvas = this.canvas,
3097           styles = canvas.style;
3098       this.size = false;
3099       canvas.width = width;
3100       canvas.height = height;
3101       styles.width = width + "px";
3102       styles.height = height + "px";
3103       //small ExCanvas fix
3104       if(!supportsCanvas) {
3105         this.translateToCenter(size);
3106       } else {
3107         this.translateToCenter();
3108       }
3109       this.translateOffsetX =
3110         this.translateOffsetY = 0;
3111       this.scaleOffsetX = 
3112         this.scaleOffsetY = 1;
3113       this.clear();
3114       this.viz.resize(width, height, this);
3115     },
3116     translate: function(x, y, disablePlot) {
3117       var sx = this.scaleOffsetX,
3118           sy = this.scaleOffsetY;
3119       this.translateOffsetX += x*sx;
3120       this.translateOffsetY += y*sy;
3121       this.getCtx().translate(x, y);
3122       !disablePlot && this.plot();
3123     },
3124     scale: function(x, y, disablePlot) {
3125       this.scaleOffsetX *= x;
3126       this.scaleOffsetY *= y;
3127       this.getCtx().scale(x, y);
3128       !disablePlot && this.plot();
3129     },
3130     clear: function(){
3131       var size = this.getSize(),
3132           ox = this.translateOffsetX,
3133           oy = this.translateOffsetY,
3134           sx = this.scaleOffsetX,
3135           sy = this.scaleOffsetY;
3136       this.getCtx().clearRect((-size.width / 2 - ox) * 1/sx, 
3137                               (-size.height / 2 - oy) * 1/sy, 
3138                               size.width * 1/sx, size.height * 1/sy);
3139     },
3140     plot: function() {
3141       this.clear();
3142       this.viz.plot(this);
3143     }
3144   });
3145   //background canvases
3146   //TODO(nico): document this!
3147   Canvas.Background = {};
3148   Canvas.Background.Circles = new Class({
3149     initialize: function(viz, options) {
3150       this.viz = viz;
3151       this.config = $.merge({
3152         idSuffix: '-bkcanvas',
3153         levelDistance: 100,
3154         numberOfCircles: 6,
3155         CanvasStyles: {},
3156         offset: 0
3157       }, options);
3158     },
3159     resize: function(width, height, base) {
3160       this.plot(base);
3161     },
3162     plot: function(base) {
3163       var canvas = base.canvas,
3164           ctx = base.getCtx(),
3165           conf = this.config,
3166           styles = conf.CanvasStyles;
3167       //set canvas styles
3168       for(var s in styles) ctx[s] = styles[s];
3169       var n = conf.numberOfCircles,
3170           rho = conf.levelDistance;
3171       for(var i=1; i<=n; i++) {
3172         ctx.beginPath();
3173         ctx.arc(0, 0, rho * i, 0, 2 * Math.PI, false);
3174         ctx.stroke();
3175         ctx.closePath();
3176       }
3177       //TODO(nico): print labels too!
3178     }
3179   });
3180 })();
3181
3182
3183 /*
3184  * File: Polar.js
3185  * 
3186  * Defines the <Polar> class.
3187  *
3188  * Description:
3189  *
3190  * The <Polar> class, just like the <Complex> class, is used by the <Hypertree>, <ST> and <RGraph> as a 2D point representation.
3191  *
3192  * See also:
3193  *
3194  * <http://en.wikipedia.org/wiki/Polar_coordinates>
3195  *
3196 */
3197
3198 /*
3199    Class: Polar
3200
3201    A multi purpose polar representation.
3202
3203    Description:
3204  
3205    The <Polar> class, just like the <Complex> class, is used by the <Hypertree>, <ST> and <RGraph> as a 2D point representation.
3206  
3207    See also:
3208  
3209    <http://en.wikipedia.org/wiki/Polar_coordinates>
3210  
3211    Parameters:
3212
3213       theta - An angle.
3214       rho - The norm.
3215 */
3216
3217 var Polar = function(theta, rho) {
3218   this.theta = theta || 0;
3219   this.rho = rho || 0;
3220 };
3221
3222 $jit.Polar = Polar;
3223
3224 Polar.prototype = {
3225     /*
3226        Method: getc
3227     
3228        Returns a complex number.
3229     
3230        Parameters:
3231
3232        simple - _optional_ If *true*, this method will return only an object holding x and y properties and not a <Complex> instance. Default's *false*.
3233
3234       Returns:
3235     
3236           A complex number.
3237     */
3238     getc: function(simple) {
3239         return this.toComplex(simple);
3240     },
3241
3242     /*
3243        Method: getp
3244     
3245        Returns a <Polar> representation.
3246     
3247        Returns:
3248     
3249           A variable in polar coordinates.
3250     */
3251     getp: function() {
3252         return this;
3253     },
3254
3255
3256     /*
3257        Method: set
3258     
3259        Sets a number.
3260
3261        Parameters:
3262
3263        v - A <Complex> or <Polar> instance.
3264     
3265     */
3266     set: function(v) {
3267         v = v.getp();
3268         this.theta = v.theta; this.rho = v.rho;
3269     },
3270
3271     /*
3272        Method: setc
3273     
3274        Sets a <Complex> number.
3275
3276        Parameters:
3277
3278        x - A <Complex> number real part.
3279        y - A <Complex> number imaginary part.
3280     
3281     */
3282     setc: function(x, y) {
3283         this.rho = Math.sqrt(x * x + y * y);
3284         this.theta = Math.atan2(y, x);
3285         if(this.theta < 0) this.theta += Math.PI * 2;
3286     },
3287
3288     /*
3289        Method: setp
3290     
3291        Sets a polar number.
3292
3293        Parameters:
3294
3295        theta - A <Polar> number angle property.
3296        rho - A <Polar> number rho property.
3297     
3298     */
3299     setp: function(theta, rho) {
3300         this.theta = theta; 
3301         this.rho = rho;
3302     },
3303
3304     /*
3305        Method: clone
3306     
3307        Returns a copy of the current object.
3308     
3309        Returns:
3310     
3311           A copy of the real object.
3312     */
3313     clone: function() {
3314         return new Polar(this.theta, this.rho);
3315     },
3316
3317     /*
3318        Method: toComplex
3319     
3320         Translates from polar to cartesian coordinates and returns a new <Complex> instance.
3321     
3322         Parameters:
3323
3324         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*.
3325  
3326         Returns:
3327     
3328           A new <Complex> instance.
3329     */
3330     toComplex: function(simple) {
3331         var x = Math.cos(this.theta) * this.rho;
3332         var y = Math.sin(this.theta) * this.rho;
3333         if(simple) return { 'x': x, 'y': y};
3334         return new Complex(x, y);
3335     },
3336
3337     /*
3338        Method: add
3339     
3340         Adds two <Polar> instances.
3341     
3342        Parameters:
3343
3344        polar - A <Polar> number.
3345
3346        Returns:
3347     
3348           A new Polar instance.
3349     */
3350     add: function(polar) {
3351         return new Polar(this.theta + polar.theta, this.rho + polar.rho);
3352     },
3353     
3354     /*
3355        Method: scale
3356     
3357         Scales a polar norm.
3358     
3359         Parameters:
3360
3361         number - A scale factor.
3362         
3363         Returns:
3364     
3365           A new Polar instance.
3366     */
3367     scale: function(number) {
3368         return new Polar(this.theta, this.rho * number);
3369     },
3370     
3371     /*
3372        Method: equals
3373     
3374        Comparison method.
3375
3376        Returns *true* if the theta and rho properties are equal.
3377
3378        Parameters:
3379
3380        c - A <Polar> number.
3381
3382        Returns:
3383
3384        *true* if the theta and rho parameters for these objects are equal. *false* otherwise.
3385     */
3386     equals: function(c) {
3387         return this.theta == c.theta && this.rho == c.rho;
3388     },
3389     
3390     /*
3391        Method: $add
3392     
3393         Adds two <Polar> instances affecting the current object.
3394     
3395        Paramters:
3396
3397        polar - A <Polar> instance.
3398
3399        Returns:
3400     
3401           The changed object.
3402     */
3403     $add: function(polar) {
3404         this.theta = this.theta + polar.theta; this.rho += polar.rho;
3405         return this;
3406     },
3407
3408     /*
3409        Method: $madd
3410     
3411         Adds two <Polar> instances affecting the current object. The resulting theta angle is modulo 2pi.
3412     
3413        Parameters:
3414
3415        polar - A <Polar> instance.
3416
3417        Returns:
3418     
3419           The changed object.
3420     */
3421     $madd: function(polar) {
3422         this.theta = (this.theta + polar.theta) % (Math.PI * 2); this.rho += polar.rho;
3423         return this;
3424     },
3425
3426     
3427     /*
3428        Method: $scale
3429     
3430         Scales a polar instance affecting the object.
3431     
3432       Parameters:
3433
3434       number - A scaling factor.
3435
3436       Returns:
3437     
3438           The changed object.
3439     */
3440     $scale: function(number) {
3441         this.rho *= number;
3442         return this;
3443     },
3444     
3445     /*
3446       Method: isZero
3447    
3448       Returns *true* if the number is zero.
3449    
3450    */
3451     isZero: function () {
3452       var almostZero = 0.0001, abs = Math.abs;
3453       return abs(this.theta) < almostZero && abs(this.rho) < almostZero;
3454     },
3455
3456     /*
3457        Method: interpolate
3458     
3459         Calculates a polar interpolation between two points at a given delta moment.
3460
3461         Parameters:
3462       
3463         elem - A <Polar> instance.
3464         delta - A delta factor ranging [0, 1].
3465     
3466        Returns:
3467     
3468           A new <Polar> instance representing an interpolation between _this_ and _elem_
3469     */
3470     interpolate: function(elem, delta) {
3471         var pi = Math.PI, pi2 = pi * 2;
3472         var ch = function(t) {
3473             var a =  (t < 0)? (t % pi2) + pi2 : t % pi2;
3474             return a;
3475         };
3476         var tt = this.theta, et = elem.theta;
3477         var sum, diff = Math.abs(tt - et);
3478         if(diff == pi) {
3479           if(tt > et) {
3480             sum = ch((et + ((tt - pi2) - et) * delta)) ;
3481           } else {
3482             sum = ch((et - pi2 + (tt - (et)) * delta));
3483           }
3484         } else if(diff >= pi) {
3485           if(tt > et) {
3486             sum = ch((et + ((tt - pi2) - et) * delta)) ;
3487           } else {
3488             sum = ch((et - pi2 + (tt - (et - pi2)) * delta));
3489           }
3490         } else {  
3491           sum = ch((et + (tt - et) * delta)) ;
3492         }
3493         var r = (this.rho - elem.rho) * delta + elem.rho;
3494         return {
3495           'theta': sum,
3496           'rho': r
3497         };
3498     }
3499 };
3500
3501
3502 var $P = function(a, b) { return new Polar(a, b); };
3503
3504 Polar.KER = $P(0, 0);
3505
3506
3507
3508 /*
3509  * File: Complex.js
3510  * 
3511  * Defines the <Complex> class.
3512  *
3513  * Description:
3514  *
3515  * The <Complex> class, just like the <Polar> class, is used by the <Hypertree>, <ST> and <RGraph> as a 2D point representation.
3516  *
3517  * See also:
3518  *
3519  * <http://en.wikipedia.org/wiki/Complex_number>
3520  *
3521 */
3522
3523 /*
3524    Class: Complex
3525     
3526    A multi-purpose Complex Class with common methods.
3527  
3528    Description:
3529  
3530    The <Complex> class, just like the <Polar> class, is used by the <Hypertree>, <ST> and <RGraph> as a 2D point representation.
3531  
3532    See also:
3533  
3534    <http://en.wikipedia.org/wiki/Complex_number>
3535
3536    Parameters:
3537
3538    x - _optional_ A Complex number real part.
3539    y - _optional_ A Complex number imaginary part.
3540  
3541 */
3542
3543 var Complex = function(x, y) {
3544   this.x = x || 0;
3545   this.y = y || 0;
3546 };
3547
3548 $jit.Complex = Complex;
3549
3550 Complex.prototype = {
3551     /*
3552        Method: getc
3553     
3554        Returns a complex number.
3555     
3556        Returns:
3557     
3558           A complex number.
3559     */
3560     getc: function() {
3561         return this;
3562     },
3563
3564     /*
3565        Method: getp
3566     
3567        Returns a <Polar> representation of this number.
3568     
3569        Parameters:
3570
3571        simple - _optional_ If *true*, this method will return only an object holding theta and rho properties and not a <Polar> instance. Default's *false*.
3572
3573        Returns:
3574     
3575           A variable in <Polar> coordinates.
3576     */
3577     getp: function(simple) {
3578         return this.toPolar(simple);
3579     },
3580
3581
3582     /*
3583        Method: set
3584     
3585        Sets a number.
3586
3587        Parameters:
3588
3589        c - A <Complex> or <Polar> instance.
3590     
3591     */
3592     set: function(c) {
3593       c = c.getc(true);
3594       this.x = c.x; 
3595       this.y = c.y;
3596     },
3597
3598     /*
3599        Method: setc
3600     
3601        Sets a complex number.
3602
3603        Parameters:
3604
3605        x - A <Complex> number Real part.
3606        y - A <Complex> number Imaginary part.
3607     
3608     */
3609     setc: function(x, y) {
3610         this.x = x; 
3611         this.y = y;
3612     },
3613
3614     /*
3615        Method: setp
3616     
3617        Sets a polar number.
3618
3619        Parameters:
3620
3621        theta - A <Polar> number theta property.
3622        rho - A <Polar> number rho property.
3623     
3624     */
3625     setp: function(theta, rho) {
3626         this.x = Math.cos(theta) * rho;
3627         this.y = Math.sin(theta) * rho;
3628     },
3629
3630     /*
3631        Method: clone
3632     
3633        Returns a copy of the current object.
3634     
3635        Returns:
3636     
3637           A copy of the real object.
3638     */
3639     clone: function() {
3640         return new Complex(this.x, this.y);
3641     },
3642
3643     /*
3644        Method: toPolar
3645     
3646        Transforms cartesian to polar coordinates.
3647     
3648        Parameters:
3649
3650        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*.
3651        
3652        Returns:
3653     
3654           A new <Polar> instance.
3655     */
3656     
3657     toPolar: function(simple) {
3658         var rho = this.norm();
3659         var atan = Math.atan2(this.y, this.x);
3660         if(atan < 0) atan += Math.PI * 2;
3661         if(simple) return { 'theta': atan, 'rho': rho };
3662         return new Polar(atan, rho);
3663     },
3664     /*
3665        Method: norm
3666     
3667        Calculates a <Complex> number norm.
3668     
3669        Returns:
3670     
3671           A real number representing the complex norm.
3672     */
3673     norm: function () {
3674         return Math.sqrt(this.squaredNorm());
3675     },
3676     
3677     /*
3678        Method: squaredNorm
3679     
3680        Calculates a <Complex> number squared norm.
3681     
3682        Returns:
3683     
3684           A real number representing the complex squared norm.
3685     */
3686     squaredNorm: function () {
3687         return this.x*this.x + this.y*this.y;
3688     },
3689
3690     /*
3691        Method: add
3692     
3693        Returns the result of adding two complex numbers.
3694        
3695        Does not alter the original object.
3696
3697        Parameters:
3698     
3699           pos - A <Complex> instance.
3700     
3701        Returns:
3702     
3703          The result of adding two complex numbers.
3704     */
3705     add: function(pos) {
3706         return new Complex(this.x + pos.x, this.y + pos.y);
3707     },
3708
3709     /*
3710        Method: prod
3711     
3712        Returns the result of multiplying two <Complex> numbers.
3713        
3714        Does not alter the original object.
3715
3716        Parameters:
3717     
3718           pos - A <Complex> instance.
3719     
3720        Returns:
3721     
3722          The result of multiplying two complex numbers.
3723     */
3724     prod: function(pos) {
3725         return new Complex(this.x*pos.x - this.y*pos.y, this.y*pos.x + this.x*pos.y);
3726     },
3727
3728     /*
3729        Method: conjugate
3730     
3731        Returns the conjugate of this <Complex> number.
3732
3733        Does not alter the original object.
3734
3735        Returns:
3736     
3737          The conjugate of this <Complex> number.
3738     */
3739     conjugate: function() {
3740         return new Complex(this.x, -this.y);
3741     },
3742
3743
3744     /*
3745        Method: scale
3746     
3747        Returns the result of scaling a <Complex> instance.
3748        
3749        Does not alter the original object.
3750
3751        Parameters:
3752     
3753           factor - A scale factor.
3754     
3755        Returns:
3756     
3757          The result of scaling this complex to a factor.
3758     */
3759     scale: function(factor) {
3760         return new Complex(this.x * factor, this.y * factor);
3761     },
3762
3763     /*
3764        Method: equals
3765     
3766        Comparison method.
3767
3768        Returns *true* if both real and imaginary parts are equal.
3769
3770        Parameters:
3771
3772        c - A <Complex> instance.
3773
3774        Returns:
3775
3776        A boolean instance indicating if both <Complex> numbers are equal.
3777     */
3778     equals: function(c) {
3779         return this.x == c.x && this.y == c.y;
3780     },
3781
3782     /*
3783        Method: $add
3784     
3785        Returns the result of adding two <Complex> numbers.
3786        
3787        Alters the original object.
3788
3789        Parameters:
3790     
3791           pos - A <Complex> instance.
3792     
3793        Returns:
3794     
3795          The result of adding two complex numbers.
3796     */
3797     $add: function(pos) {
3798         this.x += pos.x; this.y += pos.y;
3799         return this;    
3800     },
3801     
3802     /*
3803        Method: $prod
3804     
3805        Returns the result of multiplying two <Complex> numbers.
3806        
3807        Alters the original object.
3808
3809        Parameters:
3810     
3811           pos - A <Complex> instance.
3812     
3813        Returns:
3814     
3815          The result of multiplying two complex numbers.
3816     */
3817     $prod:function(pos) {
3818         var x = this.x, y = this.y;
3819         this.x = x*pos.x - y*pos.y;
3820         this.y = y*pos.x + x*pos.y;
3821         return this;
3822     },
3823     
3824     /*
3825        Method: $conjugate
3826     
3827        Returns the conjugate for this <Complex>.
3828        
3829        Alters the original object.
3830
3831        Returns:
3832     
3833          The conjugate for this complex.
3834     */
3835     $conjugate: function() {
3836         this.y = -this.y;
3837         return this;
3838     },
3839     
3840     /*
3841        Method: $scale
3842     
3843        Returns the result of scaling a <Complex> instance.
3844        
3845        Alters the original object.
3846
3847        Parameters:
3848     
3849           factor - A scale factor.
3850     
3851        Returns:
3852     
3853          The result of scaling this complex to a factor.
3854     */
3855     $scale: function(factor) {
3856         this.x *= factor; this.y *= factor;
3857         return this;
3858     },
3859     
3860     /*
3861        Method: $div
3862     
3863        Returns the division of two <Complex> numbers.
3864        
3865        Alters the original object.
3866
3867        Parameters:
3868     
3869           pos - A <Complex> number.
3870     
3871        Returns:
3872     
3873          The result of scaling this complex to a factor.
3874     */
3875     $div: function(pos) {
3876         var x = this.x, y = this.y;
3877         var sq = pos.squaredNorm();
3878         this.x = x * pos.x + y * pos.y; this.y = y * pos.x - x * pos.y;
3879         return this.$scale(1 / sq);
3880     },
3881
3882     /*
3883       Method: isZero
3884    
3885       Returns *true* if the number is zero.
3886    
3887    */
3888     isZero: function () {
3889       var almostZero = 0.0001, abs = Math.abs;
3890       return abs(this.x) < almostZero && abs(this.y) < almostZero;
3891     }
3892 };
3893
3894 var $C = function(a, b) { return new Complex(a, b); };
3895
3896 Complex.KER = $C(0, 0);
3897
3898
3899
3900 /*
3901  * File: Graph.js
3902  *
3903 */
3904
3905 /*
3906  Class: Graph
3907
3908  A Graph Class that provides useful manipulation functions. You can find more manipulation methods in the <Graph.Util> object.
3909
3910  An instance of this class can be accessed by using the *graph* parameter of any tree or graph visualization.
3911  
3912  Example:
3913
3914  (start code js)
3915    //create new visualization
3916    var viz = new $jit.Viz(options);
3917    //load JSON data
3918    viz.loadJSON(json);
3919    //access model
3920    viz.graph; //<Graph> instance
3921  (end code)
3922  
3923  Implements:
3924  
3925  The following <Graph.Util> methods are implemented in <Graph>
3926  
3927   - <Graph.Util.getNode>
3928   - <Graph.Util.eachNode>
3929   - <Graph.Util.computeLevels>
3930   - <Graph.Util.eachBFS>
3931   - <Graph.Util.clean>
3932   - <Graph.Util.getClosestNodeToPos>
3933   - <Graph.Util.getClosestNodeToOrigin>
3934  
3935 */  
3936
3937 $jit.Graph = new Class({
3938
3939   initialize: function(opt, Node, Edge, Label) {
3940     var innerOptions = {
3941     'klass': Complex,
3942     'Node': {}
3943     };
3944     this.Node = Node;
3945     this.Edge = Edge;
3946     this.Label = Label;
3947     this.opt = $.merge(innerOptions, opt || {});
3948     this.nodes = {};
3949     this.edges = {};
3950     
3951     //add nodeList methods
3952     var that = this;
3953     this.nodeList = {};
3954     for(var p in Accessors) {
3955       that.nodeList[p] = (function(p) {
3956         return function() {
3957           var args = Array.prototype.slice.call(arguments);
3958           that.eachNode(function(n) {
3959             n[p].apply(n, args);
3960           });
3961         };
3962       })(p);
3963     }
3964
3965  },
3966
3967 /*
3968      Method: getNode
3969     
3970      Returns a <Graph.Node> by *id*.
3971
3972      Parameters:
3973
3974      id - (string) A <Graph.Node> id.
3975
3976      Example:
3977
3978      (start code js)
3979        var node = graph.getNode('nodeId');
3980      (end code)
3981 */  
3982  getNode: function(id) {
3983     if(this.hasNode(id)) return this.nodes[id];
3984     return false;
3985  },
3986
3987  /*
3988      Method: get
3989     
3990      An alias for <Graph.Util.getNode>. Returns a node by *id*.
3991     
3992      Parameters:
3993     
3994      id - (string) A <Graph.Node> id.
3995     
3996      Example:
3997     
3998      (start code js)
3999        var node = graph.get('nodeId');
4000      (end code)
4001 */  
4002   get: function(id) {
4003     return this.getNode(id);
4004   },
4005
4006  /*
4007    Method: getByName
4008   
4009    Returns a <Graph.Node> by *name*.
4010   
4011    Parameters:
4012   
4013    name - (string) A <Graph.Node> name.
4014   
4015    Example:
4016   
4017    (start code js)
4018      var node = graph.getByName('someName');
4019    (end code)
4020   */  
4021   getByName: function(name) {
4022     for(var id in this.nodes) {
4023       var n = this.nodes[id];
4024       if(n.name == name) return n;
4025     }
4026     return false;
4027   },
4028
4029 /*
4030    Method: getAdjacence
4031   
4032    Returns a <Graph.Adjacence> object connecting nodes with ids *id* and *id2*.
4033
4034    Parameters:
4035
4036    id - (string) A <Graph.Node> id.
4037    id2 - (string) A <Graph.Node> id.
4038 */  
4039   getAdjacence: function (id, id2) {
4040     if(id in this.edges) {
4041       return this.edges[id][id2];
4042     }
4043     return false;
4044  },
4045
4046     /*
4047      Method: addNode
4048     
4049      Adds a node.
4050      
4051      Parameters:
4052     
4053       obj - An object with the properties described below
4054
4055       id - (string) A node id
4056       name - (string) A node's name
4057       data - (object) A node's data hash
4058
4059     See also:
4060     <Graph.Node>
4061
4062   */  
4063   addNode: function(obj) { 
4064    if(!this.nodes[obj.id]) {  
4065      var edges = this.edges[obj.id] = {};
4066      this.nodes[obj.id] = new Graph.Node($.extend({
4067         'id': obj.id,
4068         'name': obj.name,
4069         'data': $.merge(obj.data || {}, {}),
4070         'adjacencies': edges 
4071       }, this.opt.Node), 
4072       this.opt.klass, 
4073       this.Node, 
4074       this.Edge,
4075       this.Label);
4076     }
4077     return this.nodes[obj.id];
4078   },
4079   
4080     /*
4081      Method: addAdjacence
4082     
4083      Connects nodes specified by *obj* and *obj2*. If not found, nodes are created.
4084      
4085      Parameters:
4086     
4087       obj - (object) A <Graph.Node> object.
4088       obj2 - (object) Another <Graph.Node> object.
4089       data - (object) A data object. Used to store some extra information in the <Graph.Adjacence> object created.
4090
4091     See also:
4092
4093     <Graph.Node>, <Graph.Adjacence>
4094     */  
4095   addAdjacence: function (obj, obj2, data) {
4096     if(!this.hasNode(obj.id)) { this.addNode(obj); }
4097     if(!this.hasNode(obj2.id)) { this.addNode(obj2); }
4098     obj = this.nodes[obj.id]; obj2 = this.nodes[obj2.id];
4099     if(!obj.adjacentTo(obj2)) {
4100       var adjsObj = this.edges[obj.id] = this.edges[obj.id] || {};
4101       var adjsObj2 = this.edges[obj2.id] = this.edges[obj2.id] || {};
4102       adjsObj[obj2.id] = adjsObj2[obj.id] = new Graph.Adjacence(obj, obj2, data, this.Edge, this.Label);
4103       return adjsObj[obj2.id];
4104     }
4105     return this.edges[obj.id][obj2.id];
4106  },
4107
4108     /*
4109      Method: removeNode
4110     
4111      Removes a <Graph.Node> matching the specified *id*.
4112
4113      Parameters:
4114
4115      id - (string) A node's id.
4116
4117     */  
4118   removeNode: function(id) {
4119     if(this.hasNode(id)) {
4120       delete this.nodes[id];
4121       var adjs = this.edges[id];
4122       for(var to in adjs) {
4123         delete this.edges[to][id];
4124       }
4125       delete this.edges[id];
4126     }
4127   },
4128   
4129 /*
4130      Method: removeAdjacence
4131     
4132      Removes a <Graph.Adjacence> matching *id1* and *id2*.
4133
4134      Parameters:
4135
4136      id1 - (string) A <Graph.Node> id.
4137      id2 - (string) A <Graph.Node> id.
4138 */  
4139   removeAdjacence: function(id1, id2) {
4140     delete this.edges[id1][id2];
4141     delete this.edges[id2][id1];
4142   },
4143
4144    /*
4145      Method: hasNode
4146     
4147      Returns a boolean indicating if the node belongs to the <Graph> or not.
4148      
4149      Parameters:
4150     
4151         id - (string) Node id.
4152    */  
4153   hasNode: function(id) {
4154     return id in this.nodes;
4155   },
4156   
4157   /*
4158     Method: empty
4159
4160     Empties the Graph
4161
4162   */
4163   empty: function() { this.nodes = {}; this.edges = {};}
4164
4165 });
4166
4167 var Graph = $jit.Graph;
4168
4169 /*
4170  Object: Accessors
4171  
4172  Defines a set of methods for data, canvas and label styles manipulation implemented by <Graph.Node> and <Graph.Adjacence> instances.
4173  
4174  */
4175 var Accessors;
4176
4177 (function () {
4178   var getDataInternal = function(prefix, prop, type, force, prefixConfig) {
4179     var data;
4180     type = type || 'current';
4181     prefix = "$" + (prefix ? prefix + "-" : "");
4182
4183     if(type == 'current') {
4184       data = this.data;
4185     } else if(type == 'start') {
4186       data = this.startData;
4187     } else if(type == 'end') {
4188       data = this.endData;
4189     }
4190
4191     var dollar = prefix + prop;
4192
4193     if(force) {
4194       return data[dollar];
4195     }
4196
4197     if(!this.Config.overridable)
4198       return prefixConfig[prop] || 0;
4199
4200     return (dollar in data) ?
4201       data[dollar] : ((dollar in this.data) ? this.data[dollar] : (prefixConfig[prop] || 0));
4202   }
4203
4204   var setDataInternal = function(prefix, prop, value, type) {
4205     type = type || 'current';
4206     prefix = '$' + (prefix ? prefix + '-' : '');
4207
4208     var data;
4209
4210     if(type == 'current') {
4211       data = this.data;
4212     } else if(type == 'start') {
4213       data = this.startData;
4214     } else if(type == 'end') {
4215       data = this.endData;
4216     }
4217
4218     data[prefix + prop] = value;
4219   }
4220
4221   var removeDataInternal = function(prefix, properties) {
4222     prefix = '$' + (prefix ? prefix + '-' : '');
4223     var that = this;
4224     $.each(properties, function(prop) {
4225       var pref = prefix + prop;
4226       delete that.data[pref];
4227       delete that.endData[pref];
4228       delete that.startData[pref];
4229     });
4230   }
4231
4232   Accessors = {
4233     /*
4234     Method: getData
4235
4236     Returns the specified data value property.
4237     This is useful for querying special/reserved <Graph.Node> data properties
4238     (i.e dollar prefixed properties).
4239
4240     Parameters:
4241
4242       prop  - (string) The name of the property. The dollar sign is not needed. For
4243               example *getData(width)* will return *data.$width*.
4244       type  - (string) The type of the data property queried. Default's "current". You can access *start* and *end* 
4245               data properties also. These properties are used when making animations.
4246       force - (boolean) Whether to obtain the true value of the property (equivalent to
4247               *data.$prop*) or to check for *node.overridable = true* first.
4248
4249     Returns:
4250
4251       The value of the dollar prefixed property or the global Node/Edge property
4252       value if *overridable=false*
4253
4254     Example:
4255     (start code js)
4256      node.getData('width'); //will return node.data.$width if Node.overridable=true;
4257     (end code)
4258     */
4259     getData: function(prop, type, force) {
4260       return getDataInternal.call(this, "", prop, type, force, this.Config);
4261     },
4262
4263
4264     /*
4265     Method: setData
4266
4267     Sets the current data property with some specific value.
4268     This method is only useful for reserved (dollar prefixed) properties.
4269
4270     Parameters:
4271
4272       prop  - (string) The name of the property. The dollar sign is not necessary. For
4273               example *setData(width)* will set *data.$width*.
4274       value - (mixed) The value to store.
4275       type  - (string) The type of the data property to store. Default's "current" but
4276               can also be "start" or "end".
4277
4278     Example:
4279     
4280     (start code js)
4281      node.setData('width', 30);
4282     (end code)
4283     
4284     If we were to make an animation of a node/edge width then we could do
4285     
4286     (start code js)
4287       var node = viz.getNode('nodeId');
4288       //set start and end values
4289       node.setData('width', 10, 'start');
4290       node.setData('width', 30, 'end');
4291       //will animate nodes width property
4292       viz.fx.animate({
4293         modes: ['node-property:width'],
4294         duration: 1000
4295       });
4296     (end code)
4297     */
4298     setData: function(prop, value, type) {
4299       setDataInternal.call(this, "", prop, value, type);
4300     },
4301
4302     /*
4303     Method: setDataset
4304
4305     Convenience method to set multiple data values at once.
4306     
4307     Parameters:
4308     
4309     types - (array|string) A set of 'current', 'end' or 'start' values.
4310     obj - (object) A hash containing the names and values of the properties to be altered.
4311
4312     Example:
4313     (start code js)
4314       node.setDataset(['current', 'end'], {
4315         'width': [100, 5],
4316         'color': ['#fff', '#ccc']
4317       });
4318       //...or also
4319       node.setDataset('end', {
4320         'width': 5,
4321         'color': '#ccc'
4322       });
4323     (end code)
4324     
4325     See also: 
4326     
4327     <Accessors.setData>
4328     
4329     */
4330     setDataset: function(types, obj) {
4331       types = $.splat(types);
4332       for(var attr in obj) {
4333         for(var i=0, val = $.splat(obj[attr]), l=types.length; i<l; i++) {
4334           this.setData(attr, val[i], types[i]);
4335         }
4336       }
4337     },
4338     
4339     /*
4340     Method: removeData
4341
4342     Remove data properties.
4343
4344     Parameters:
4345
4346     One or more property names as arguments. The dollar sign is not needed.
4347
4348     Example:
4349     (start code js)
4350     node.removeData('width'); //now the default width value is returned
4351     (end code)
4352     */
4353     removeData: function() {
4354       removeDataInternal.call(this, "", Array.prototype.slice.call(arguments));
4355     },
4356
4357     /*
4358     Method: getCanvasStyle
4359
4360     Returns the specified canvas style data value property. This is useful for
4361     querying special/reserved <Graph.Node> canvas style data properties (i.e.
4362     dollar prefixed properties that match with $canvas-<name of canvas style>).
4363
4364     Parameters:
4365
4366       prop  - (string) The name of the property. The dollar sign is not needed. For
4367               example *getCanvasStyle(shadowBlur)* will return *data[$canvas-shadowBlur]*.
4368       type  - (string) The type of the data property queried. Default's *current*. You can access *start* and *end* 
4369               data properties also.
4370               
4371     Example:
4372     (start code js)
4373       node.getCanvasStyle('shadowBlur');
4374     (end code)
4375     
4376     See also:
4377     
4378     <Accessors.getData>
4379     */
4380     getCanvasStyle: function(prop, type, force) {
4381       return getDataInternal.call(
4382           this, 'canvas', prop, type, force, this.Config.CanvasStyles);
4383     },
4384
4385     /*
4386     Method: setCanvasStyle
4387
4388     Sets the canvas style data property with some specific value.
4389     This method is only useful for reserved (dollar prefixed) properties.
4390     
4391     Parameters:
4392     
4393     prop - (string) Name of the property. Can be any canvas property like 'shadowBlur', 'shadowColor', 'strokeStyle', etc.
4394     value - (mixed) The value to set to the property.
4395     type - (string) Default's *current*. Whether to set *start*, *current* or *end* type properties.
4396     
4397     Example:
4398     
4399     (start code js)
4400      node.setCanvasStyle('shadowBlur', 30);
4401     (end code)
4402     
4403     If we were to make an animation of a node/edge shadowBlur canvas style then we could do
4404     
4405     (start code js)
4406       var node = viz.getNode('nodeId');
4407       //set start and end values
4408       node.setCanvasStyle('shadowBlur', 10, 'start');
4409       node.setCanvasStyle('shadowBlur', 30, 'end');
4410       //will animate nodes canvas style property for nodes
4411       viz.fx.animate({
4412         modes: ['node-style:shadowBlur'],
4413         duration: 1000
4414       });
4415     (end code)
4416     
4417     See also:
4418     
4419     <Accessors.setData>.
4420     */
4421     setCanvasStyle: function(prop, value, type) {
4422       setDataInternal.call(this, 'canvas', prop, value, type);
4423     },
4424
4425     /*
4426     Method: setCanvasStyles
4427
4428     Convenience method to set multiple styles at once.
4429
4430     Parameters:
4431     
4432     types - (array|string) A set of 'current', 'end' or 'start' values.
4433     obj - (object) A hash containing the names and values of the properties to be altered.
4434
4435     See also:
4436     
4437     <Accessors.setDataset>.
4438     */
4439     setCanvasStyles: function(types, obj) {
4440       types = $.splat(types);
4441       for(var attr in obj) {
4442         for(var i=0, val = $.splat(obj[attr]), l=types.length; i<l; i++) {
4443           this.setCanvasStyle(attr, val[i], types[i]);
4444         }
4445       }
4446     },
4447
4448     /*
4449     Method: removeCanvasStyle
4450
4451     Remove canvas style properties from data.
4452
4453     Parameters:
4454     
4455     A variable number of canvas style strings.
4456
4457     See also:
4458     
4459     <Accessors.removeData>.
4460     */
4461     removeCanvasStyle: function() {
4462       removeDataInternal.call(this, 'canvas', Array.prototype.slice.call(arguments));
4463     },
4464
4465     /*
4466     Method: getLabelData
4467
4468     Returns the specified label data value property. This is useful for
4469     querying special/reserved <Graph.Node> label options (i.e.
4470     dollar prefixed properties that match with $label-<name of label style>).
4471
4472     Parameters:
4473
4474       prop  - (string) The name of the property. The dollar sign prefix is not needed. For
4475               example *getLabelData(size)* will return *data[$label-size]*.
4476       type  - (string) The type of the data property queried. Default's *current*. You can access *start* and *end* 
4477               data properties also.
4478               
4479     See also:
4480     
4481     <Accessors.getData>.
4482     */
4483     getLabelData: function(prop, type, force) {
4484       return getDataInternal.call(
4485           this, 'label', prop, type, force, this.Label);
4486     },
4487
4488     /*
4489     Method: setLabelData
4490
4491     Sets the current label data with some specific value.
4492     This method is only useful for reserved (dollar prefixed) properties.
4493
4494     Parameters:
4495     
4496     prop - (string) Name of the property. Can be any canvas property like 'shadowBlur', 'shadowColor', 'strokeStyle', etc.
4497     value - (mixed) The value to set to the property.
4498     type - (string) Default's *current*. Whether to set *start*, *current* or *end* type properties.
4499     
4500     Example:
4501     
4502     (start code js)
4503      node.setLabelData('size', 30);
4504     (end code)
4505     
4506     If we were to make an animation of a node label size then we could do
4507     
4508     (start code js)
4509       var node = viz.getNode('nodeId');
4510       //set start and end values
4511       node.setLabelData('size', 10, 'start');
4512       node.setLabelData('size', 30, 'end');
4513       //will animate nodes label size
4514       viz.fx.animate({
4515         modes: ['label-property:size'],
4516         duration: 1000
4517       });
4518     (end code)
4519     
4520     See also:
4521     
4522     <Accessors.setData>.
4523     */
4524     setLabelData: function(prop, value, type) {
4525       setDataInternal.call(this, 'label', prop, value, type);
4526     },
4527
4528     /*
4529     Method: setLabelDataset
4530
4531     Convenience function to set multiple label data at once.
4532
4533     Parameters:
4534     
4535     types - (array|string) A set of 'current', 'end' or 'start' values.
4536     obj - (object) A hash containing the names and values of the properties to be altered.
4537
4538     See also:
4539     
4540     <Accessors.setDataset>.
4541     */
4542     setLabelDataset: function(types, obj) {
4543       types = $.splat(types);
4544       for(var attr in obj) {
4545         for(var i=0, val = $.splat(obj[attr]), l=types.length; i<l; i++) {
4546           this.setLabelData(attr, val[i], types[i]);
4547         }
4548       }
4549     },
4550
4551     /*
4552     Method: removeLabelData
4553
4554     Remove label properties from data.
4555     
4556     Parameters:
4557     
4558     A variable number of label property strings.
4559
4560     See also:
4561     
4562     <Accessors.removeData>.
4563     */
4564     removeLabelData: function() {
4565       removeDataInternal.call(this, 'label', Array.prototype.slice.call(arguments));
4566     }
4567   };
4568 })();
4569
4570 /*
4571      Class: Graph.Node
4572
4573      A <Graph> node.
4574      
4575      Implements:
4576      
4577      <Accessors> methods.
4578      
4579      The following <Graph.Util> methods are implemented by <Graph.Node>
4580      
4581     - <Graph.Util.eachAdjacency>
4582     - <Graph.Util.eachLevel>
4583     - <Graph.Util.eachSubgraph>
4584     - <Graph.Util.eachSubnode>
4585     - <Graph.Util.anySubnode>
4586     - <Graph.Util.getSubnodes>
4587     - <Graph.Util.getParents>
4588     - <Graph.Util.isDescendantOf>     
4589 */
4590 Graph.Node = new Class({
4591     
4592   initialize: function(opt, klass, Node, Edge, Label) {
4593     var innerOptions = {
4594       'id': '',
4595       'name': '',
4596       'data': {},
4597       'startData': {},
4598       'endData': {},
4599       'adjacencies': {},
4600
4601       'selected': false,
4602       'drawn': false,
4603       'exist': false,
4604
4605       'angleSpan': {
4606         'begin': 0,
4607         'end' : 0
4608       },
4609
4610       'pos': new klass,
4611       'startPos': new klass,
4612       'endPos': new klass
4613     };
4614     
4615     $.extend(this, $.extend(innerOptions, opt));
4616     this.Config = this.Node = Node;
4617     this.Edge = Edge;
4618     this.Label = Label;
4619   },
4620
4621     /*
4622        Method: adjacentTo
4623     
4624        Indicates if the node is adjacent to the node specified by id
4625
4626        Parameters:
4627     
4628           id - (string) A node id.
4629     
4630        Example:
4631        (start code js)
4632         node.adjacentTo('nodeId') == true;
4633        (end code)
4634     */
4635     adjacentTo: function(node) {
4636         return node.id in this.adjacencies;
4637     },
4638
4639     /*
4640        Method: getAdjacency
4641     
4642        Returns a <Graph.Adjacence> object connecting the current <Graph.Node> and the node having *id* as id.
4643
4644        Parameters:
4645     
4646           id - (string) A node id.
4647     */  
4648     getAdjacency: function(id) {
4649         return this.adjacencies[id];
4650     },
4651
4652     /*
4653       Method: getPos
4654    
4655       Returns the position of the node.
4656   
4657       Parameters:
4658    
4659          type - (string) Default's *current*. Possible values are "start", "end" or "current".
4660    
4661       Returns:
4662    
4663         A <Complex> or <Polar> instance.
4664   
4665       Example:
4666       (start code js)
4667        var pos = node.getPos('end');
4668       (end code)
4669    */
4670    getPos: function(type) {
4671        type = type || "current";
4672        if(type == "current") {
4673          return this.pos;
4674        } else if(type == "end") {
4675          return this.endPos;
4676        } else if(type == "start") {
4677          return this.startPos;
4678        }
4679    },
4680    /*
4681      Method: setPos
4682   
4683      Sets the node's position.
4684   
4685      Parameters:
4686   
4687         value - (object) A <Complex> or <Polar> instance.
4688         type - (string) Default's *current*. Possible values are "start", "end" or "current".
4689   
4690      Example:
4691      (start code js)
4692       node.setPos(new $jit.Complex(0, 0), 'end');
4693      (end code)
4694   */
4695   setPos: function(value, type) {
4696       type = type || "current";
4697       var pos;
4698       if(type == "current") {
4699         pos = this.pos;
4700       } else if(type == "end") {
4701         pos = this.endPos;
4702       } else if(type == "start") {
4703         pos = this.startPos;
4704       }
4705       pos.set(value);
4706   }
4707 });
4708
4709 Graph.Node.implement(Accessors);
4710
4711 /*
4712      Class: Graph.Adjacence
4713
4714      A <Graph> adjacence (or edge) connecting two <Graph.Nodes>.
4715      
4716      Implements:
4717      
4718      <Accessors> methods.
4719
4720      See also:
4721
4722      <Graph>, <Graph.Node>
4723
4724      Properties:
4725      
4726       nodeFrom - A <Graph.Node> connected by this edge.
4727       nodeTo - Another  <Graph.Node> connected by this edge.
4728       data - Node data property containing a hash (i.e {}) with custom options.
4729 */
4730 Graph.Adjacence = new Class({
4731   
4732   initialize: function(nodeFrom, nodeTo, data, Edge, Label) {
4733     this.nodeFrom = nodeFrom;
4734     this.nodeTo = nodeTo;
4735     this.data = data || {};
4736     this.startData = {};
4737     this.endData = {};
4738     this.Config = this.Edge = Edge;
4739     this.Label = Label;
4740   }
4741 });
4742
4743 Graph.Adjacence.implement(Accessors);
4744
4745 /*
4746    Object: Graph.Util
4747
4748    <Graph> traversal and processing utility object.
4749    
4750    Note:
4751    
4752    For your convenience some of these methods have also been appended to <Graph> and <Graph.Node> classes.
4753 */
4754 Graph.Util = {
4755     /*
4756        filter
4757     
4758        For internal use only. Provides a filtering function based on flags.
4759     */
4760     filter: function(param) {
4761         if(!param || !($.type(param) == 'string')) return function() { return true; };
4762         var props = param.split(" ");
4763         return function(elem) {
4764             for(var i=0; i<props.length; i++) { 
4765               if(elem[props[i]]) { 
4766                 return false; 
4767               }
4768             }
4769             return true;
4770         };
4771     },
4772     /*
4773        Method: getNode
4774     
4775        Returns a <Graph.Node> by *id*.
4776        
4777        Also implemented by:
4778        
4779        <Graph>
4780
4781        Parameters:
4782
4783        graph - (object) A <Graph> instance.
4784        id - (string) A <Graph.Node> id.
4785
4786        Example:
4787
4788        (start code js)
4789          $jit.Graph.Util.getNode(graph, 'nodeid');
4790          //or...
4791          graph.getNode('nodeid');
4792        (end code)
4793     */
4794     getNode: function(graph, id) {
4795         return graph.nodes[id];
4796     },
4797     
4798     /*
4799        Method: eachNode
4800     
4801        Iterates over <Graph> nodes performing an *action*.
4802        
4803        Also implemented by:
4804        
4805        <Graph>.
4806
4807        Parameters:
4808
4809        graph - (object) A <Graph> instance.
4810        action - (function) A callback function having a <Graph.Node> as first formal parameter.
4811
4812        Example:
4813        (start code js)
4814          $jit.Graph.Util.eachNode(graph, function(node) {
4815           alert(node.name);
4816          });
4817          //or...
4818          graph.eachNode(function(node) {
4819            alert(node.name);
4820          });
4821        (end code)
4822     */
4823     eachNode: function(graph, action, flags) {
4824         var filter = this.filter(flags);
4825         for(var i in graph.nodes) {
4826           if(filter(graph.nodes[i])) action(graph.nodes[i]);
4827         } 
4828     },
4829     
4830     /*
4831       Method: each
4832    
4833       Iterates over <Graph> nodes performing an *action*. It's an alias for <Graph.Util.eachNode>.
4834       
4835       Also implemented by:
4836       
4837       <Graph>.
4838   
4839       Parameters:
4840   
4841       graph - (object) A <Graph> instance.
4842       action - (function) A callback function having a <Graph.Node> as first formal parameter.
4843   
4844       Example:
4845       (start code js)
4846         $jit.Graph.Util.each(graph, function(node) {
4847          alert(node.name);
4848         });
4849         //or...
4850         graph.each(function(node) {
4851           alert(node.name);
4852         });
4853       (end code)
4854    */
4855    each: function(graph, action, flags) {
4856       this.eachNode(graph, action, flags); 
4857    },
4858
4859  /*
4860        Method: eachAdjacency
4861     
4862        Iterates over <Graph.Node> adjacencies applying the *action* function.
4863        
4864        Also implemented by:
4865        
4866        <Graph.Node>.
4867
4868        Parameters:
4869
4870        node - (object) A <Graph.Node>.
4871        action - (function) A callback function having <Graph.Adjacence> as first formal parameter.
4872
4873        Example:
4874        (start code js)
4875          $jit.Graph.Util.eachAdjacency(node, function(adj) {
4876           alert(adj.nodeTo.name);
4877          });
4878          //or...
4879          node.eachAdjacency(function(adj) {
4880            alert(adj.nodeTo.name);
4881          });
4882        (end code)
4883     */
4884     eachAdjacency: function(node, action, flags) {
4885         var adj = node.adjacencies, filter = this.filter(flags);
4886         for(var id in adj) {
4887           var a = adj[id];
4888           if(filter(a)) {
4889             if(a.nodeFrom != node) {
4890               var tmp = a.nodeFrom;
4891               a.nodeFrom = a.nodeTo;
4892               a.nodeTo = tmp;
4893             }
4894             action(a, id);
4895           }
4896         }
4897     },
4898
4899      /*
4900        Method: computeLevels
4901     
4902        Performs a BFS traversal setting the correct depth for each node.
4903         
4904        Also implemented by:
4905        
4906        <Graph>.
4907        
4908        Note:
4909        
4910        The depth of each node can then be accessed by 
4911        >node._depth
4912
4913        Parameters:
4914
4915        graph - (object) A <Graph>.
4916        id - (string) A starting node id for the BFS traversal.
4917        startDepth - (optional|number) A minimum depth value. Default's 0.
4918
4919     */
4920     computeLevels: function(graph, id, startDepth, flags) {
4921         startDepth = startDepth || 0;
4922         var filter = this.filter(flags);
4923         this.eachNode(graph, function(elem) {
4924             elem._flag = false;
4925             elem._depth = -1;
4926         }, flags);
4927         var root = graph.getNode(id);
4928         root._depth = startDepth;
4929         var queue = [root];
4930         while(queue.length != 0) {
4931             var node = queue.pop();
4932             node._flag = true;
4933             this.eachAdjacency(node, function(adj) {
4934                 var n = adj.nodeTo;
4935                 if(n._flag == false && filter(n)) {
4936                     if(n._depth < 0) n._depth = node._depth + 1 + startDepth;
4937                     queue.unshift(n);
4938                 }
4939             }, flags);
4940         }
4941     },
4942
4943     /*
4944        Method: eachBFS
4945     
4946        Performs a BFS traversal applying *action* to each <Graph.Node>.
4947        
4948        Also implemented by:
4949        
4950        <Graph>.
4951
4952        Parameters:
4953
4954        graph - (object) A <Graph>.
4955        id - (string) A starting node id for the BFS traversal.
4956        action - (function) A callback function having a <Graph.Node> as first formal parameter.
4957
4958        Example:
4959        (start code js)
4960          $jit.Graph.Util.eachBFS(graph, 'mynodeid', function(node) {
4961           alert(node.name);
4962          });
4963          //or...
4964          graph.eachBFS('mynodeid', function(node) {
4965            alert(node.name);
4966          });
4967        (end code)
4968     */
4969     eachBFS: function(graph, id, action, flags) {
4970         var filter = this.filter(flags);
4971         this.clean(graph);
4972         var queue = [graph.getNode(id)];
4973         while(queue.length != 0) {
4974             var node = queue.pop();
4975             node._flag = true;
4976             action(node, node._depth);
4977             this.eachAdjacency(node, function(adj) {
4978                 var n = adj.nodeTo;
4979                 if(n._flag == false && filter(n)) {
4980                     n._flag = true;
4981                     queue.unshift(n);
4982                 }
4983             }, flags);
4984         }
4985     },
4986     
4987     /*
4988        Method: eachLevel
4989     
4990        Iterates over a node's subgraph applying *action* to the nodes of relative depth between *levelBegin* and *levelEnd*.
4991        
4992        Also implemented by:
4993        
4994        <Graph.Node>.
4995
4996        Parameters:
4997        
4998        node - (object) A <Graph.Node>.
4999        levelBegin - (number) A relative level value.
5000        levelEnd - (number) A relative level value.
5001        action - (function) A callback function having a <Graph.Node> as first formal parameter.
5002
5003     */
5004     eachLevel: function(node, levelBegin, levelEnd, action, flags) {
5005         var d = node._depth, filter = this.filter(flags), that = this;
5006         levelEnd = levelEnd === false? Number.MAX_VALUE -d : levelEnd;
5007         (function loopLevel(node, levelBegin, levelEnd) {
5008             var d = node._depth;
5009             if(d >= levelBegin && d <= levelEnd && filter(node)) action(node, d);
5010             if(d < levelEnd) {
5011                 that.eachAdjacency(node, function(adj) {
5012                     var n = adj.nodeTo;
5013                     if(n._depth > d) loopLevel(n, levelBegin, levelEnd);
5014                 });
5015             }
5016         })(node, levelBegin + d, levelEnd + d);      
5017     },
5018
5019     /*
5020        Method: eachSubgraph
5021     
5022        Iterates over a node's children recursively.
5023        
5024        Also implemented by:
5025        
5026        <Graph.Node>.
5027
5028        Parameters:
5029        node - (object) A <Graph.Node>.
5030        action - (function) A callback function having a <Graph.Node> as first formal parameter.
5031
5032        Example:
5033        (start code js)
5034          $jit.Graph.Util.eachSubgraph(node, function(node) {
5035            alert(node.name);
5036          });
5037          //or...
5038          node.eachSubgraph(function(node) {
5039            alert(node.name);
5040          });
5041        (end code)
5042     */
5043     eachSubgraph: function(node, action, flags) {
5044       this.eachLevel(node, 0, false, action, flags);
5045     },
5046
5047     /*
5048        Method: eachSubnode
5049     
5050        Iterates over a node's children (without deeper recursion).
5051        
5052        Also implemented by:
5053        
5054        <Graph.Node>.
5055        
5056        Parameters:
5057        node - (object) A <Graph.Node>.
5058        action - (function) A callback function having a <Graph.Node> as first formal parameter.
5059
5060        Example:
5061        (start code js)
5062          $jit.Graph.Util.eachSubnode(node, function(node) {
5063           alert(node.name);
5064          });
5065          //or...
5066          node.eachSubnode(function(node) {
5067            alert(node.name);
5068          });
5069        (end code)
5070     */
5071     eachSubnode: function(node, action, flags) {
5072         this.eachLevel(node, 1, 1, action, flags);
5073     },
5074
5075     /*
5076        Method: anySubnode
5077     
5078        Returns *true* if any subnode matches the given condition.
5079        
5080        Also implemented by:
5081        
5082        <Graph.Node>.
5083
5084        Parameters:
5085        node - (object) A <Graph.Node>.
5086        cond - (function) A callback function returning a Boolean instance. This function has as first formal parameter a <Graph.Node>.
5087
5088        Example:
5089        (start code js)
5090          $jit.Graph.Util.anySubnode(node, function(node) { return node.name == "mynodename"; });
5091          //or...
5092          node.anySubnode(function(node) { return node.name == 'mynodename'; });
5093        (end code)
5094     */
5095     anySubnode: function(node, cond, flags) {
5096       var flag = false;
5097       cond = cond || $.lambda(true);
5098       var c = $.type(cond) == 'string'? function(n) { return n[cond]; } : cond;
5099       this.eachSubnode(node, function(elem) {
5100         if(c(elem)) flag = true;
5101       }, flags);
5102       return flag;
5103     },
5104   
5105     /*
5106        Method: getSubnodes
5107     
5108        Collects all subnodes for a specified node. 
5109        The *level* parameter filters nodes having relative depth of *level* from the root node. 
5110        
5111        Also implemented by:
5112        
5113        <Graph.Node>.
5114
5115        Parameters:
5116        node - (object) A <Graph.Node>.
5117        level - (optional|number) Default's *0*. A starting relative depth for collecting nodes.
5118
5119        Returns:
5120        An array of nodes.
5121
5122     */
5123     getSubnodes: function(node, level, flags) {
5124         var ans = [], that = this;
5125         level = level || 0;
5126         var levelStart, levelEnd;
5127         if($.type(level) == 'array') {
5128             levelStart = level[0];
5129             levelEnd = level[1];
5130         } else {
5131             levelStart = level;
5132             levelEnd = Number.MAX_VALUE - node._depth;
5133         }
5134         this.eachLevel(node, levelStart, levelEnd, function(n) {
5135             ans.push(n);
5136         }, flags);
5137         return ans;
5138     },
5139   
5140   
5141     /*
5142        Method: getParents
5143     
5144        Returns an Array of <Graph.Nodes> which are parents of the given node.
5145        
5146        Also implemented by:
5147        
5148        <Graph.Node>.
5149
5150        Parameters:
5151        node - (object) A <Graph.Node>.
5152
5153        Returns:
5154        An Array of <Graph.Nodes>.
5155
5156        Example:
5157        (start code js)
5158          var pars = $jit.Graph.Util.getParents(node);
5159          //or...
5160          var pars = node.getParents();
5161          
5162          if(pars.length > 0) {
5163            //do stuff with parents
5164          }
5165        (end code)
5166     */
5167     getParents: function(node) {
5168         var ans = [];
5169         this.eachAdjacency(node, function(adj) {
5170             var n = adj.nodeTo;
5171             if(n._depth < node._depth) ans.push(n);
5172         });
5173         return ans;
5174     },
5175     
5176     /*
5177     Method: isDescendantOf
5178  
5179     Returns a boolean indicating if some node is descendant of the node with the given id. 
5180
5181     Also implemented by:
5182     
5183     <Graph.Node>.
5184     
5185     
5186     Parameters:
5187     node - (object) A <Graph.Node>.
5188     id - (string) A <Graph.Node> id.
5189
5190     Example:
5191     (start code js)
5192       $jit.Graph.Util.isDescendantOf(node, "nodeid"); //true|false
5193       //or...
5194       node.isDescendantOf('nodeid');//true|false
5195     (end code)
5196  */
5197  isDescendantOf: function(node, id) {
5198     if(node.id == id) return true;
5199     var pars = this.getParents(node), ans = false;
5200     for ( var i = 0; !ans && i < pars.length; i++) {
5201     ans = ans || this.isDescendantOf(pars[i], id);
5202   }
5203     return ans;
5204  },
5205
5206  /*
5207      Method: clean
5208   
5209      Cleans flags from nodes.
5210
5211      Also implemented by:
5212      
5213      <Graph>.
5214      
5215      Parameters:
5216      graph - A <Graph> instance.
5217   */
5218   clean: function(graph) { this.eachNode(graph, function(elem) { elem._flag = false; }); },
5219   
5220   /* 
5221     Method: getClosestNodeToOrigin 
5222   
5223     Returns the closest node to the center of canvas.
5224   
5225     Also implemented by:
5226     
5227     <Graph>.
5228     
5229     Parameters:
5230    
5231      graph - (object) A <Graph> instance.
5232      prop - (optional|string) Default's 'current'. A <Graph.Node> position property. Possible properties are 'start', 'current' or 'end'.
5233   
5234   */
5235   getClosestNodeToOrigin: function(graph, prop, flags) {
5236    return this.getClosestNodeToPos(graph, Polar.KER, prop, flags);
5237   },
5238   
5239   /* 
5240     Method: getClosestNodeToPos
5241   
5242     Returns the closest node to the given position.
5243   
5244     Also implemented by:
5245     
5246     <Graph>.
5247     
5248     Parameters:
5249    
5250      graph - (object) A <Graph> instance.
5251      pos - (object) A <Complex> or <Polar> instance.
5252      prop - (optional|string) Default's *current*. A <Graph.Node> position property. Possible properties are 'start', 'current' or 'end'.
5253   
5254   */
5255   getClosestNodeToPos: function(graph, pos, prop, flags) {
5256    var node = null;
5257    prop = prop || 'current';
5258    pos = pos && pos.getc(true) || Complex.KER;
5259    var distance = function(a, b) {
5260      var d1 = a.x - b.x, d2 = a.y - b.y;
5261      return d1 * d1 + d2 * d2;
5262    };
5263    this.eachNode(graph, function(elem) {
5264      node = (node == null || distance(elem.getPos(prop).getc(true), pos) < distance(
5265          node.getPos(prop).getc(true), pos)) ? elem : node;
5266    }, flags);
5267    return node;
5268   } 
5269 };
5270
5271 //Append graph methods to <Graph>
5272 $.each(['get', 'getNode', 'each', 'eachNode', 'computeLevels', 'eachBFS', 'clean', 'getClosestNodeToPos', 'getClosestNodeToOrigin'], function(m) {
5273   Graph.prototype[m] = function() {
5274     return Graph.Util[m].apply(Graph.Util, [this].concat(Array.prototype.slice.call(arguments)));
5275   };
5276 });
5277
5278 //Append node methods to <Graph.Node>
5279 $.each(['eachAdjacency', 'eachLevel', 'eachSubgraph', 'eachSubnode', 'anySubnode', 'getSubnodes', 'getParents', 'isDescendantOf'], function(m) {
5280   Graph.Node.prototype[m] = function() {
5281     return Graph.Util[m].apply(Graph.Util, [this].concat(Array.prototype.slice.call(arguments)));
5282   };
5283 });
5284
5285 /*
5286  * File: Graph.Op.js
5287  *
5288 */
5289
5290 /*
5291    Object: Graph.Op
5292
5293    Perform <Graph> operations like adding/removing <Graph.Nodes> or <Graph.Adjacences>, 
5294    morphing a <Graph> into another <Graph>, contracting or expanding subtrees, etc.
5295
5296 */
5297 Graph.Op = {
5298
5299     options: {
5300       type: 'nothing',
5301       duration: 2000,
5302       hideLabels: true,
5303       fps:30
5304     },
5305     
5306     initialize: function(viz) {
5307       this.viz = viz;
5308     },
5309
5310     /*
5311        Method: removeNode
5312     
5313        Removes one or more <Graph.Nodes> from the visualization. 
5314        It can also perform several animations like fading sequentially, fading concurrently, iterating or replotting.
5315
5316        Parameters:
5317     
5318         node - (string|array) The node's id. Can also be an array having many ids.
5319         opt - (object) Animation options. It's an object with optional properties described below
5320         type - (string) Default's *nothing*. Type of the animation. Can be "nothing", "replot", "fade:seq",  "fade:con" or "iter".
5321         duration - Described in <Options.Fx>.
5322         fps - Described in <Options.Fx>.
5323         transition - Described in <Options.Fx>.
5324         hideLabels - (boolean) Default's *true*. Hide labels during the animation.
5325    
5326       Example:
5327       (start code js)
5328         var viz = new $jit.Viz(options);
5329         viz.op.removeNode('nodeId', {
5330           type: 'fade:seq',
5331           duration: 1000,
5332           hideLabels: false,
5333           transition: $jit.Trans.Quart.easeOut
5334         });
5335         //or also
5336         viz.op.removeNode(['someId', 'otherId'], {
5337           type: 'fade:con',
5338           duration: 1500
5339         });
5340       (end code)
5341     */
5342   
5343     removeNode: function(node, opt) {
5344         var viz = this.viz;
5345         var options = $.merge(this.options, viz.controller, opt);
5346         var n = $.splat(node);
5347         var i, that, nodeObj;
5348         switch(options.type) {
5349             case 'nothing':
5350                 for(i=0; i<n.length; i++) viz.graph.removeNode(n[i]);
5351                 break;
5352             
5353             case 'replot':
5354                 this.removeNode(n, { type: 'nothing' });
5355                 viz.labels.clearLabels();
5356                 viz.refresh(true);
5357                 break;
5358             
5359             case 'fade:seq': case 'fade':
5360                 that = this;
5361                 //set alpha to 0 for nodes to remove.
5362                 for(i=0; i<n.length; i++) {
5363                     nodeObj = viz.graph.getNode(n[i]);
5364                     nodeObj.setData('alpha', 0, 'end');
5365                 }
5366                 viz.fx.animate($.merge(options, {
5367                     modes: ['node-property:alpha'],
5368                     onComplete: function() {
5369                         that.removeNode(n, { type: 'nothing' });
5370                         viz.labels.clearLabels();
5371                         viz.reposition();
5372                         viz.fx.animate($.merge(options, {
5373                             modes: ['linear']
5374                         }));
5375                     }
5376                 }));
5377                 break;
5378             
5379             case 'fade:con':
5380                 that = this;
5381                 //set alpha to 0 for nodes to remove. Tag them for being ignored on computing positions.
5382                 for(i=0; i<n.length; i++) {
5383                     nodeObj = viz.graph.getNode(n[i]);
5384                     nodeObj.setData('alpha', 0, 'end');
5385                     nodeObj.ignore = true;
5386                 }
5387                 viz.reposition();
5388                 viz.fx.animate($.merge(options, {
5389                     modes: ['node-property:alpha', 'linear'],
5390                     onComplete: function() {
5391                         that.removeNode(n, { type: 'nothing' });
5392                         options.onComplete && options.onComplete();
5393                     }
5394                 }));
5395                 break;
5396             
5397             case 'iter':
5398                 that = this;
5399                 viz.fx.sequence({
5400                     condition: function() { return n.length != 0; },
5401                     step: function() { that.removeNode(n.shift(), { type: 'nothing' });  viz.labels.clearLabels(); },
5402                     onComplete: function() { options.onComplete && options.onComplete(); },
5403                     duration: Math.ceil(options.duration / n.length)
5404                 });
5405                 break;
5406                 
5407             default: this.doError();
5408         }
5409     },
5410     
5411     /*
5412        Method: removeEdge
5413     
5414        Removes one or more <Graph.Adjacences> from the visualization. 
5415        It can also perform several animations like fading sequentially, fading concurrently, iterating or replotting.
5416
5417        Parameters:
5418     
5419        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'], ...]).
5420        opt - (object) Animation options. It's an object with optional properties described below
5421        type - (string) Default's *nothing*. Type of the animation. Can be "nothing", "replot", "fade:seq",  "fade:con" or "iter".
5422        duration - Described in <Options.Fx>.
5423        fps - Described in <Options.Fx>.
5424        transition - Described in <Options.Fx>.
5425        hideLabels - (boolean) Default's *true*. Hide labels during the animation.
5426    
5427       Example:
5428       (start code js)
5429         var viz = new $jit.Viz(options);
5430         viz.op.removeEdge(['nodeId', 'otherId'], {
5431           type: 'fade:seq',
5432           duration: 1000,
5433           hideLabels: false,
5434           transition: $jit.Trans.Quart.easeOut
5435         });
5436         //or also
5437         viz.op.removeEdge([['someId', 'otherId'], ['id3', 'id4']], {
5438           type: 'fade:con',
5439           duration: 1500
5440         });
5441       (end code)
5442     
5443     */
5444     removeEdge: function(vertex, opt) {
5445         var viz = this.viz;
5446         var options = $.merge(this.options, viz.controller, opt);
5447         var v = ($.type(vertex[0]) == 'string')? [vertex] : vertex;
5448         var i, that, adj;
5449         switch(options.type) {
5450             case 'nothing':
5451                 for(i=0; i<v.length; i++)   viz.graph.removeAdjacence(v[i][0], v[i][1]);
5452                 break;
5453             
5454             case 'replot':
5455                 this.removeEdge(v, { type: 'nothing' });
5456                 viz.refresh(true);
5457                 break;
5458             
5459             case 'fade:seq': case 'fade':
5460                 that = this;
5461                 //set alpha to 0 for edges to remove.
5462                 for(i=0; i<v.length; i++) {
5463                     adj = viz.graph.getAdjacence(v[i][0], v[i][1]);
5464                     if(adj) {
5465                         adj.setData('alpha', 0,'end');
5466                     }
5467                 }
5468                 viz.fx.animate($.merge(options, {
5469                     modes: ['edge-property:alpha'],
5470                     onComplete: function() {
5471                         that.removeEdge(v, { type: 'nothing' });
5472                         viz.reposition();
5473                         viz.fx.animate($.merge(options, {
5474                             modes: ['linear']
5475                         }));
5476                     }
5477                 }));
5478                 break;
5479             
5480             case 'fade:con':
5481                 that = this;
5482                 //set alpha to 0 for nodes to remove. Tag them for being ignored when computing positions.
5483                 for(i=0; i<v.length; i++) {
5484                     adj = viz.graph.getAdjacence(v[i][0], v[i][1]);
5485                     if(adj) {
5486                         adj.setData('alpha',0 ,'end');
5487                         adj.ignore = true;
5488                     }
5489                 }
5490                 viz.reposition();
5491                 viz.fx.animate($.merge(options, {
5492                     modes: ['edge-property:alpha', 'linear'],
5493                     onComplete: function() {
5494                         that.removeEdge(v, { type: 'nothing' });
5495                         options.onComplete && options.onComplete();
5496                     }
5497                 }));
5498                 break;
5499             
5500             case 'iter':
5501                 that = this;
5502                 viz.fx.sequence({
5503                     condition: function() { return v.length != 0; },
5504                     step: function() { that.removeEdge(v.shift(), { type: 'nothing' }); viz.labels.clearLabels(); },
5505                     onComplete: function() { options.onComplete(); },
5506                     duration: Math.ceil(options.duration / v.length)
5507                 });
5508                 break;
5509                 
5510             default: this.doError();
5511         }
5512     },
5513     
5514     /*
5515        Method: sum
5516     
5517        Adds a new graph to the visualization. 
5518        The JSON graph (or tree) must at least have a common node with the current graph plotted by the visualization. 
5519        The resulting graph can be defined as follows <http://mathworld.wolfram.com/GraphSum.html>
5520
5521        Parameters:
5522     
5523        json - (object) A json tree or graph structure. See also <Loader.loadJSON>.
5524        opt - (object) Animation options. It's an object with optional properties described below
5525        type - (string) Default's *nothing*. Type of the animation. Can be "nothing", "replot", "fade:seq",  "fade:con".
5526        duration - Described in <Options.Fx>.
5527        fps - Described in <Options.Fx>.
5528        transition - Described in <Options.Fx>.
5529        hideLabels - (boolean) Default's *true*. Hide labels during the animation.
5530    
5531       Example:
5532       (start code js)
5533         //...json contains a tree or graph structure...
5534
5535         var viz = new $jit.Viz(options);
5536         viz.op.sum(json, {
5537           type: 'fade:seq',
5538           duration: 1000,
5539           hideLabels: false,
5540           transition: $jit.Trans.Quart.easeOut
5541         });
5542         //or also
5543         viz.op.sum(json, {
5544           type: 'fade:con',
5545           duration: 1500
5546         });
5547       (end code)
5548     
5549     */
5550     sum: function(json, opt) {
5551         var viz = this.viz;
5552         var options = $.merge(this.options, viz.controller, opt), root = viz.root;
5553         var graph;
5554         viz.root = opt.id || viz.root;
5555         switch(options.type) {
5556             case 'nothing':
5557                 graph = viz.construct(json);
5558                 graph.eachNode(function(elem) {
5559                     elem.eachAdjacency(function(adj) {
5560                         viz.graph.addAdjacence(adj.nodeFrom, adj.nodeTo, adj.data);
5561                     });
5562                 });
5563                 break;
5564             
5565             case 'replot':
5566                 viz.refresh(true);
5567                 this.sum(json, { type: 'nothing' });
5568                 viz.refresh(true);
5569                 break;
5570             
5571             case 'fade:seq': case 'fade': case 'fade:con':
5572                 that = this;
5573                 graph = viz.construct(json);
5574
5575                 //set alpha to 0 for nodes to add.
5576                 var fadeEdges = this.preprocessSum(graph);
5577                 var modes = !fadeEdges? ['node-property:alpha'] : ['node-property:alpha', 'edge-property:alpha'];
5578                 viz.reposition();
5579                 if(options.type != 'fade:con') {
5580                     viz.fx.animate($.merge(options, {
5581                         modes: ['linear'],
5582                         onComplete: function() {
5583                             viz.fx.animate($.merge(options, {
5584                                 modes: modes,
5585                                 onComplete: function() {
5586                                     options.onComplete();
5587                                 }
5588                             }));
5589                         }
5590                     }));
5591                 } else {
5592                     viz.graph.eachNode(function(elem) {
5593                         if (elem.id != root && elem.pos.isZero()) {
5594                           elem.pos.set(elem.endPos); 
5595                           elem.startPos.set(elem.endPos);
5596                         }
5597                     });
5598                     viz.fx.animate($.merge(options, {
5599                         modes: ['linear'].concat(modes)
5600                     }));
5601                 }
5602                 break;
5603
5604             default: this.doError();
5605         }
5606     },
5607     
5608     /*
5609        Method: morph
5610     
5611        This method will transform the current visualized graph into the new JSON representation passed in the method. 
5612        The JSON object must at least have the root node in common with the current visualized graph.
5613
5614        Parameters:
5615     
5616        json - (object) A json tree or graph structure. See also <Loader.loadJSON>.
5617        opt - (object) Animation options. It's an object with optional properties described below
5618        type - (string) Default's *nothing*. Type of the animation. Can be "nothing", "replot", "fade:con".
5619        duration - Described in <Options.Fx>.
5620        fps - Described in <Options.Fx>.
5621        transition - Described in <Options.Fx>.
5622        hideLabels - (boolean) Default's *true*. Hide labels during the animation.
5623        id - (string) The shared <Graph.Node> id between both graphs.
5624        
5625        extraModes - (optional|object) When morphing with an animation, dollar prefixed data parameters are added to 
5626                     *endData* and not *data* itself. This way you can animate dollar prefixed parameters during your morphing operation. 
5627                     For animating these extra-parameters you have to specify an object that has animation groups as keys and animation 
5628                     properties as values, just like specified in <Graph.Plot.animate>.
5629    
5630       Example:
5631       (start code js)
5632         //...json contains a tree or graph structure...
5633
5634         var viz = new $jit.Viz(options);
5635         viz.op.morph(json, {
5636           type: 'fade',
5637           duration: 1000,
5638           hideLabels: false,
5639           transition: $jit.Trans.Quart.easeOut
5640         });
5641         //or also
5642         viz.op.morph(json, {
5643           type: 'fade',
5644           duration: 1500
5645         });
5646         //if the json data contains dollar prefixed params
5647         //like $width or $height these too can be animated
5648         viz.op.morph(json, {
5649           type: 'fade',
5650           duration: 1500
5651         }, {
5652           'node-property': ['width', 'height']
5653         });
5654       (end code)
5655     
5656     */
5657     morph: function(json, opt, extraModes) {
5658         extraModes = extraModes || {};
5659         var viz = this.viz;
5660         var options = $.merge(this.options, viz.controller, opt), root = viz.root;
5661         var graph;
5662         //TODO(nico) this hack makes morphing work with the Hypertree. 
5663         //Need to check if it has been solved and this can be removed.
5664         viz.root = opt.id || viz.root;
5665         switch(options.type) {
5666             case 'nothing':
5667                 graph = viz.construct(json);
5668                 graph.eachNode(function(elem) {
5669                   var nodeExists = viz.graph.hasNode(elem.id);  
5670                   elem.eachAdjacency(function(adj) {
5671                     var adjExists = !!viz.graph.getAdjacence(adj.nodeFrom.id, adj.nodeTo.id);
5672                     viz.graph.addAdjacence(adj.nodeFrom, adj.nodeTo, adj.data);
5673                     //Update data properties if the node existed
5674                     if(adjExists) {
5675                       var addedAdj = viz.graph.getAdjacence(adj.nodeFrom.id, adj.nodeTo.id);
5676                       for(var prop in (adj.data || {})) {
5677                         addedAdj.data[prop] = adj.data[prop];
5678                       }
5679                     }
5680                   });
5681                   //Update data properties if the node existed
5682                   if(nodeExists) {
5683                     var addedNode = viz.graph.getNode(elem.id);
5684                     for(var prop in (elem.data || {})) {
5685                       addedNode.data[prop] = elem.data[prop];
5686                     }
5687                   }
5688                 });
5689                 viz.graph.eachNode(function(elem) {
5690                     elem.eachAdjacency(function(adj) {
5691                         if(!graph.getAdjacence(adj.nodeFrom.id, adj.nodeTo.id)) {
5692                             viz.graph.removeAdjacence(adj.nodeFrom.id, adj.nodeTo.id);
5693                         }
5694                     });
5695                     if(!graph.hasNode(elem.id)) viz.graph.removeNode(elem.id);
5696                 });
5697                 
5698                 break;
5699             
5700             case 'replot':
5701                 viz.labels.clearLabels(true);
5702                 this.morph(json, { type: 'nothing' });
5703                 viz.refresh(true);
5704                 viz.refresh(true);
5705                 break;
5706                 
5707             case 'fade:seq': case 'fade': case 'fade:con':
5708                 that = this;
5709                 graph = viz.construct(json);
5710                 //preprocessing for nodes to delete.
5711                 //get node property modes to interpolate
5712                 var nodeModes = ('node-property' in extraModes) 
5713                   && $.map($.splat(extraModes['node-property']), 
5714                       function(n) { return '$' + n; });
5715                 viz.graph.eachNode(function(elem) {
5716                   var graphNode = graph.getNode(elem.id);   
5717                   if(!graphNode) {
5718                       elem.setData('alpha', 1);
5719                       elem.setData('alpha', 1, 'start');
5720                       elem.setData('alpha', 0, 'end');
5721                       elem.ignore = true;
5722                     } else {
5723                       //Update node data information
5724                       var graphNodeData = graphNode.data;
5725                       for(var prop in graphNodeData) {
5726                         if(nodeModes && ($.indexOf(nodeModes, prop) > -1)) {
5727                           elem.endData[prop] = graphNodeData[prop];
5728                         } else {
5729                           elem.data[prop] = graphNodeData[prop];
5730                         }
5731                       }
5732                     }
5733                 }); 
5734                 viz.graph.eachNode(function(elem) {
5735                     if(elem.ignore) return;
5736                     elem.eachAdjacency(function(adj) {
5737                         if(adj.nodeFrom.ignore || adj.nodeTo.ignore) return;
5738                         var nodeFrom = graph.getNode(adj.nodeFrom.id);
5739                         var nodeTo = graph.getNode(adj.nodeTo.id);
5740                         if(!nodeFrom.adjacentTo(nodeTo)) {
5741                             var adj = viz.graph.getAdjacence(nodeFrom.id, nodeTo.id);
5742                             fadeEdges = true;
5743                             adj.setData('alpha', 1);
5744                             adj.setData('alpha', 1, 'start');
5745                             adj.setData('alpha', 0, 'end');
5746                         }
5747                     });
5748                 }); 
5749                 //preprocessing for adding nodes.
5750                 var fadeEdges = this.preprocessSum(graph);
5751
5752                 var modes = !fadeEdges? ['node-property:alpha'] : 
5753                                         ['node-property:alpha', 
5754                                          'edge-property:alpha'];
5755                 //Append extra node-property animations (if any)
5756                 modes[0] = modes[0] + (('node-property' in extraModes)? 
5757                     (':' + $.splat(extraModes['node-property']).join(':')) : '');
5758                 //Append extra edge-property animations (if any)
5759                 modes[1] = (modes[1] || 'edge-property:alpha') + (('edge-property' in extraModes)? 
5760                     (':' + $.splat(extraModes['edge-property']).join(':')) : '');
5761                 //Add label-property animations (if any)
5762                 if('label-property' in extraModes) {
5763                   modes.push('label-property:' + $.splat(extraModes['label-property']).join(':'))
5764                 }
5765                 //only use reposition if its implemented.
5766                 if (viz.reposition) {
5767                   viz.reposition();
5768                 } else {
5769                   viz.compute('end');
5770                 }
5771                 viz.graph.eachNode(function(elem) {
5772                     if (elem.id != root && elem.pos.getp().equals(Polar.KER)) {
5773                       elem.pos.set(elem.endPos); elem.startPos.set(elem.endPos);
5774                     }
5775                 });
5776                 viz.fx.animate($.merge(options, {
5777                     modes: [extraModes.position || 'polar'].concat(modes),
5778                     onComplete: function() {
5779                         viz.graph.eachNode(function(elem) {
5780                             if(elem.ignore) viz.graph.removeNode(elem.id);
5781                         });
5782                         viz.graph.eachNode(function(elem) {
5783                             elem.eachAdjacency(function(adj) {
5784                                 if(adj.ignore) viz.graph.removeAdjacence(adj.nodeFrom.id, adj.nodeTo.id);
5785                             });
5786                         });
5787                         options.onComplete();
5788                     }
5789                 }));
5790                 break;
5791
5792             default:;
5793         }
5794     },
5795
5796     
5797   /*
5798     Method: contract
5799  
5800     Collapses the subtree of the given node. The node will have a _collapsed=true_ property.
5801     
5802     Parameters:
5803  
5804     node - (object) A <Graph.Node>.
5805     opt - (object) An object containing options described below
5806     type - (string) Whether to 'replot' or 'animate' the contraction.
5807    
5808     There are also a number of Animation options. For more information see <Options.Fx>.
5809
5810     Example:
5811     (start code js)
5812      var viz = new $jit.Viz(options);
5813      viz.op.contract(node, {
5814        type: 'animate',
5815        duration: 1000,
5816        hideLabels: true,
5817        transition: $jit.Trans.Quart.easeOut
5818      });
5819    (end code)
5820  
5821    */
5822     contract: function(node, opt) {
5823       var viz = this.viz;
5824       if(node.collapsed || !node.anySubnode($.lambda(true))) return;
5825       opt = $.merge(this.options, viz.config, opt || {}, {
5826         'modes': ['node-property:alpha:span', 'linear']
5827       });
5828       node.collapsed = true;
5829       (function subn(n) {
5830         n.eachSubnode(function(ch) {
5831           ch.ignore = true;
5832           ch.setData('alpha', 0, opt.type == 'animate'? 'end' : 'current');
5833           subn(ch);
5834         });
5835       })(node);
5836       if(opt.type == 'animate') {
5837         viz.compute('end');
5838         if(viz.rotated) {
5839           viz.rotate(viz.rotated, 'none', {
5840             'property':'end'
5841           });
5842         }
5843         (function subn(n) {
5844           n.eachSubnode(function(ch) {
5845             ch.setPos(node.getPos('end'), 'end');
5846             subn(ch);
5847           });
5848         })(node);
5849         viz.fx.animate(opt);
5850       } else if(opt.type == 'replot'){
5851         viz.refresh();
5852       }
5853     },
5854     
5855     /*
5856     Method: expand
5857  
5858     Expands the previously contracted subtree. The given node must have the _collapsed=true_ property.
5859     
5860     Parameters:
5861  
5862     node - (object) A <Graph.Node>.
5863     opt - (object) An object containing options described below
5864     type - (string) Whether to 'replot' or 'animate'.
5865      
5866     There are also a number of Animation options. For more information see <Options.Fx>.
5867
5868     Example:
5869     (start code js)
5870       var viz = new $jit.Viz(options);
5871       viz.op.expand(node, {
5872         type: 'animate',
5873         duration: 1000,
5874         hideLabels: true,
5875         transition: $jit.Trans.Quart.easeOut
5876       });
5877     (end code)
5878  
5879    */
5880     expand: function(node, opt) {
5881       if(!('collapsed' in node)) return;
5882       var viz = this.viz;
5883       opt = $.merge(this.options, viz.config, opt || {}, {
5884         'modes': ['node-property:alpha:span', 'linear']
5885       });
5886       delete node.collapsed;
5887       (function subn(n) {
5888         n.eachSubnode(function(ch) {
5889           delete ch.ignore;
5890           ch.setData('alpha', 1, opt.type == 'animate'? 'end' : 'current');
5891           subn(ch);
5892         });
5893       })(node);
5894       if(opt.type == 'animate') {
5895         viz.compute('end');
5896         if(viz.rotated) {
5897           viz.rotate(viz.rotated, 'none', {
5898             'property':'end'
5899           });
5900         }
5901         viz.fx.animate(opt);
5902       } else if(opt.type == 'replot'){
5903         viz.refresh();
5904       }
5905     },
5906
5907     preprocessSum: function(graph) {
5908         var viz = this.viz;
5909         graph.eachNode(function(elem) {
5910             if(!viz.graph.hasNode(elem.id)) {
5911                 viz.graph.addNode(elem);
5912                 var n = viz.graph.getNode(elem.id);
5913                 n.setData('alpha', 0);
5914                 n.setData('alpha', 0, 'start');
5915                 n.setData('alpha', 1, 'end');
5916             }
5917         }); 
5918         var fadeEdges = false;
5919         graph.eachNode(function(elem) {
5920             elem.eachAdjacency(function(adj) {
5921                 var nodeFrom = viz.graph.getNode(adj.nodeFrom.id);
5922                 var nodeTo = viz.graph.getNode(adj.nodeTo.id);
5923                 if(!nodeFrom.adjacentTo(nodeTo)) {
5924                     var adj = viz.graph.addAdjacence(nodeFrom, nodeTo, adj.data);
5925                     if(nodeFrom.startAlpha == nodeFrom.endAlpha 
5926                     && nodeTo.startAlpha == nodeTo.endAlpha) {
5927                         fadeEdges = true;
5928                         adj.setData('alpha', 0);
5929                         adj.setData('alpha', 0, 'start');
5930                         adj.setData('alpha', 1, 'end');
5931                     } 
5932                 }
5933             });
5934         }); 
5935         return fadeEdges;
5936     }
5937 };
5938
5939
5940
5941 /*
5942    File: Helpers.js
5943  
5944    Helpers are objects that contain rendering primitives (like rectangles, ellipses, etc), for plotting nodes and edges.
5945    Helpers also contain implementations of the *contains* method, a method returning a boolean indicating whether the mouse
5946    position is over the rendered shape.
5947    
5948    Helpers are very useful when implementing new NodeTypes, since you can access them through *this.nodeHelper* and 
5949    *this.edgeHelper* <Graph.Plot> properties, providing you with simple primitives and mouse-position check functions.
5950    
5951    Example:
5952    (start code js)
5953    //implement a new node type
5954    $jit.Viz.Plot.NodeTypes.implement({
5955      'customNodeType': {
5956        'render': function(node, canvas) {
5957          this.nodeHelper.circle.render ...
5958        },
5959        'contains': function(node, pos) {
5960          this.nodeHelper.circle.contains ...
5961        }
5962      }
5963    });
5964    //implement an edge type
5965    $jit.Viz.Plot.EdgeTypes.implement({
5966      'customNodeType': {
5967        'render': function(node, canvas) {
5968          this.edgeHelper.circle.render ...
5969        },
5970        //optional
5971        'contains': function(node, pos) {
5972          this.edgeHelper.circle.contains ...
5973        }
5974      }
5975    });
5976    (end code)
5977
5978 */
5979
5980 /*
5981    Object: NodeHelper
5982    
5983    Contains rendering and other type of primitives for simple shapes.
5984  */
5985 var NodeHelper = {
5986   'none': {
5987     'render': $.empty,
5988     'contains': $.lambda(false)
5989   },
5990   /*
5991    Object: NodeHelper.circle
5992    */
5993   'circle': {
5994     /*
5995      Method: render
5996      
5997      Renders a circle into the canvas.
5998      
5999      Parameters:
6000      
6001      type - (string) Possible options are 'fill' or 'stroke'.
6002      pos - (object) An *x*, *y* object with the position of the center of the circle.
6003      radius - (number) The radius of the circle to be rendered.
6004      canvas - (object) A <Canvas> instance.
6005      
6006      Example:
6007      (start code js)
6008      NodeHelper.circle.render('fill', { x: 10, y: 30 }, 30, viz.canvas);
6009      (end code)
6010      */
6011     'render': function(type, pos, radius, canvas){
6012       var ctx = canvas.getCtx();
6013       ctx.beginPath();
6014       ctx.arc(pos.x, pos.y, radius, 0, Math.PI * 2, true);
6015       ctx.closePath();
6016       ctx[type]();
6017     },
6018     /*
6019     Method: contains
6020     
6021     Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.
6022     
6023     Parameters:
6024     
6025     npos - (object) An *x*, *y* object with the <Graph.Node> position.
6026     pos - (object) An *x*, *y* object with the position to check.
6027     radius - (number) The radius of the rendered circle.
6028     
6029     Example:
6030     (start code js)
6031     NodeHelper.circle.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, 30); //true
6032     (end code)
6033     */
6034     'contains': function(npos, pos, radius){
6035       var diffx = npos.x - pos.x, 
6036           diffy = npos.y - pos.y, 
6037           diff = diffx * diffx + diffy * diffy;
6038       return diff <= radius * radius;
6039     }
6040   },
6041   /*
6042   Object: NodeHelper.ellipse
6043   */
6044   'ellipse': {
6045     /*
6046     Method: render
6047     
6048     Renders an ellipse into the canvas.
6049     
6050     Parameters:
6051     
6052     type - (string) Possible options are 'fill' or 'stroke'.
6053     pos - (object) An *x*, *y* object with the position of the center of the ellipse.
6054     width - (number) The width of the ellipse.
6055     height - (number) The height of the ellipse.
6056     canvas - (object) A <Canvas> instance.
6057     
6058     Example:
6059     (start code js)
6060     NodeHelper.ellipse.render('fill', { x: 10, y: 30 }, 30, 40, viz.canvas);
6061     (end code)
6062     */
6063     'render': function(type, pos, width, height, canvas){
6064       var ctx = canvas.getCtx(),
6065           scalex = 1,
6066           scaley = 1,
6067           scaleposx = 1,
6068           scaleposy = 1,
6069           radius = 0;
6070
6071       if (width > height) {
6072           radius = width / 2;
6073           scaley = height / width;
6074           scaleposy = width / height;
6075       } else {
6076           radius = height / 2;
6077           scalex = width / height;
6078           scaleposx = height / width;
6079       }
6080
6081       ctx.save();
6082       ctx.scale(scalex, scaley);
6083       ctx.beginPath();
6084       ctx.arc(pos.x * scaleposx, pos.y * scaleposy, radius, 0, Math.PI * 2, true);
6085       ctx.closePath();
6086       ctx[type]();
6087       ctx.restore();
6088     },
6089     /*
6090     Method: contains
6091     
6092     Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.
6093     
6094     Parameters:
6095     
6096     npos - (object) An *x*, *y* object with the <Graph.Node> position.
6097     pos - (object) An *x*, *y* object with the position to check.
6098     width - (number) The width of the rendered ellipse.
6099     height - (number) The height of the rendered ellipse.
6100     
6101     Example:
6102     (start code js)
6103     NodeHelper.ellipse.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, 30, 40);
6104     (end code)
6105     */
6106     'contains': function(npos, pos, width, height){
6107       var radius = 0,
6108           scalex = 1,
6109           scaley = 1,
6110           diffx = 0,
6111           diffy = 0,
6112           diff = 0;
6113
6114       if (width > height) {
6115               radius = width / 2;
6116               scaley = height / width;
6117       } else {
6118           radius = height / 2;
6119           scalex = width / height;
6120       }
6121
6122       diffx = (npos.x - pos.x) * (1 / scalex);
6123       diffy = (npos.y - pos.y) * (1 / scaley);
6124       diff = diffx * diffx + diffy * diffy;
6125       return diff <= radius * radius;
6126     }
6127   },
6128   /*
6129   Object: NodeHelper.square
6130   */
6131   'square': {
6132     /*
6133     Method: render
6134     
6135     Renders a square into the canvas.
6136     
6137     Parameters:
6138     
6139     type - (string) Possible options are 'fill' or 'stroke'.
6140     pos - (object) An *x*, *y* object with the position of the center of the square.
6141     dim - (number) The radius (or half-diameter) of the square.
6142     canvas - (object) A <Canvas> instance.
6143     
6144     Example:
6145     (start code js)
6146     NodeHelper.square.render('stroke', { x: 10, y: 30 }, 40, viz.canvas);
6147     (end code)
6148     */
6149     'render': function(type, pos, dim, canvas){
6150       canvas.getCtx()[type + "Rect"](pos.x - dim, pos.y - dim, 2*dim, 2*dim);
6151     },
6152     /*
6153     Method: contains
6154     
6155     Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.
6156     
6157     Parameters:
6158     
6159     npos - (object) An *x*, *y* object with the <Graph.Node> position.
6160     pos - (object) An *x*, *y* object with the position to check.
6161     dim - (number) The radius (or half-diameter) of the square.
6162     
6163     Example:
6164     (start code js)
6165     NodeHelper.square.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, 30);
6166     (end code)
6167     */
6168     'contains': function(npos, pos, dim){
6169       return Math.abs(pos.x - npos.x) <= dim && Math.abs(pos.y - npos.y) <= dim;
6170     }
6171   },
6172   /*
6173   Object: NodeHelper.rectangle
6174   */
6175   'rectangle': {
6176     /*
6177     Method: render
6178     
6179     Renders a rectangle into the canvas.
6180     
6181     Parameters:
6182     
6183     type - (string) Possible options are 'fill' or 'stroke'.
6184     pos - (object) An *x*, *y* object with the position of the center of the rectangle.
6185     width - (number) The width of the rectangle.
6186     height - (number) The height of the rectangle.
6187     canvas - (object) A <Canvas> instance.
6188     
6189     Example:
6190     (start code js)
6191     NodeHelper.rectangle.render('fill', { x: 10, y: 30 }, 30, 40, viz.canvas);
6192     (end code)
6193     */
6194     'render': function(type, pos, width, height, canvas){
6195       canvas.getCtx()[type + "Rect"](pos.x - width / 2, pos.y - height / 2, 
6196                                       width, height);
6197     },
6198     /*
6199     Method: contains
6200     
6201     Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.
6202     
6203     Parameters:
6204     
6205     npos - (object) An *x*, *y* object with the <Graph.Node> position.
6206     pos - (object) An *x*, *y* object with the position to check.
6207     width - (number) The width of the rendered rectangle.
6208     height - (number) The height of the rendered rectangle.
6209     
6210     Example:
6211     (start code js)
6212     NodeHelper.rectangle.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, 30, 40);
6213     (end code)
6214     */
6215     'contains': function(npos, pos, width, height){
6216       return Math.abs(pos.x - npos.x) <= width / 2
6217           && Math.abs(pos.y - npos.y) <= height / 2;
6218     }
6219   },
6220   /*
6221   Object: NodeHelper.triangle
6222   */
6223   'triangle': {
6224     /*
6225     Method: render
6226     
6227     Renders a triangle into the canvas.
6228     
6229     Parameters:
6230     
6231     type - (string) Possible options are 'fill' or 'stroke'.
6232     pos - (object) An *x*, *y* object with the position of the center of the triangle.
6233     dim - (number) Half the base and half the height of the triangle.
6234     canvas - (object) A <Canvas> instance.
6235     
6236     Example:
6237     (start code js)
6238     NodeHelper.triangle.render('stroke', { x: 10, y: 30 }, 40, viz.canvas);
6239     (end code)
6240     */
6241     'render': function(type, pos, dim, canvas){
6242       var ctx = canvas.getCtx(), 
6243           c1x = pos.x, 
6244           c1y = pos.y - dim, 
6245           c2x = c1x - dim, 
6246           c2y = pos.y + dim, 
6247           c3x = c1x + dim, 
6248           c3y = c2y;
6249       ctx.beginPath();
6250       ctx.moveTo(c1x, c1y);
6251       ctx.lineTo(c2x, c2y);
6252       ctx.lineTo(c3x, c3y);
6253       ctx.closePath();
6254       ctx[type]();
6255     },
6256     /*
6257     Method: contains
6258     
6259     Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.
6260     
6261     Parameters:
6262     
6263     npos - (object) An *x*, *y* object with the <Graph.Node> position.
6264     pos - (object) An *x*, *y* object with the position to check.
6265     dim - (number) Half the base and half the height of the triangle.
6266     
6267     Example:
6268     (start code js)
6269     NodeHelper.triangle.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, 30);
6270     (end code)
6271     */
6272     'contains': function(npos, pos, dim) {
6273       return NodeHelper.circle.contains(npos, pos, dim);
6274     }
6275   },
6276   /*
6277   Object: NodeHelper.star
6278   */
6279   'star': {
6280     /*
6281     Method: render
6282     
6283     Renders a star (concave decagon) into the canvas.
6284     
6285     Parameters:
6286     
6287     type - (string) Possible options are 'fill' or 'stroke'.
6288     pos - (object) An *x*, *y* object with the position of the center of the star.
6289     dim - (number) The length of a side of a concave decagon.
6290     canvas - (object) A <Canvas> instance.
6291     
6292     Example:
6293     (start code js)
6294     NodeHelper.star.render('stroke', { x: 10, y: 30 }, 40, viz.canvas);
6295     (end code)
6296     */
6297     'render': function(type, pos, dim, canvas){
6298       var ctx = canvas.getCtx(), 
6299           pi5 = Math.PI / 5;
6300       ctx.save();
6301       ctx.translate(pos.x, pos.y);
6302       ctx.beginPath();
6303       ctx.moveTo(dim, 0);
6304       for (var i = 0; i < 9; i++) {
6305         ctx.rotate(pi5);
6306         if (i % 2 == 0) {
6307           ctx.lineTo((dim / 0.525731) * 0.200811, 0);
6308         } else {
6309           ctx.lineTo(dim, 0);
6310         }
6311       }
6312       ctx.closePath();
6313       ctx[type]();
6314       ctx.restore();
6315     },
6316     /*
6317     Method: contains
6318     
6319     Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.
6320     
6321     Parameters:
6322     
6323     npos - (object) An *x*, *y* object with the <Graph.Node> position.
6324     pos - (object) An *x*, *y* object with the position to check.
6325     dim - (number) The length of a side of a concave decagon.
6326     
6327     Example:
6328     (start code js)
6329     NodeHelper.star.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, 30);
6330     (end code)
6331     */
6332     'contains': function(npos, pos, dim) {
6333       return NodeHelper.circle.contains(npos, pos, dim);
6334     }
6335   }
6336 };
6337
6338 /*
6339   Object: EdgeHelper
6340   
6341   Contains rendering primitives for simple edge shapes.
6342 */
6343 var EdgeHelper = {
6344   /*
6345     Object: EdgeHelper.line
6346   */
6347   'line': {
6348       /*
6349       Method: render
6350       
6351       Renders a line into the canvas.
6352       
6353       Parameters:
6354       
6355       from - (object) An *x*, *y* object with the starting position of the line.
6356       to - (object) An *x*, *y* object with the ending position of the line.
6357       canvas - (object) A <Canvas> instance.
6358       
6359       Example:
6360       (start code js)
6361       EdgeHelper.line.render({ x: 10, y: 30 }, { x: 10, y: 50 }, viz.canvas);
6362       (end code)
6363       */
6364       'render': function(from, to, canvas){
6365         var ctx = canvas.getCtx();
6366         ctx.beginPath();
6367         ctx.moveTo(from.x, from.y);
6368         ctx.lineTo(to.x, to.y);
6369         ctx.stroke();
6370       },
6371       /*
6372       Method: contains
6373       
6374       Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.
6375       
6376       Parameters:
6377       
6378       posFrom - (object) An *x*, *y* object with a <Graph.Node> position.
6379       posTo - (object) An *x*, *y* object with a <Graph.Node> position.
6380       pos - (object) An *x*, *y* object with the position to check.
6381       epsilon - (number) The dimension of the shape.
6382       
6383       Example:
6384       (start code js)
6385       EdgeHelper.line.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, { x: 15, y: 35 }, 30);
6386       (end code)
6387       */
6388       'contains': function(posFrom, posTo, pos, epsilon) {
6389         var min = Math.min, 
6390             max = Math.max,
6391             minPosX = min(posFrom.x, posTo.x),
6392             maxPosX = max(posFrom.x, posTo.x),
6393             minPosY = min(posFrom.y, posTo.y),
6394             maxPosY = max(posFrom.y, posTo.y);
6395         
6396         if(pos.x >= minPosX && pos.x <= maxPosX 
6397             && pos.y >= minPosY && pos.y <= maxPosY) {
6398           if(Math.abs(posTo.x - posFrom.x) <= epsilon) {
6399             return true;
6400           }
6401           var dist = (posTo.y - posFrom.y) / (posTo.x - posFrom.x) * (pos.x - posFrom.x) + posFrom.y;
6402           return Math.abs(dist - pos.y) <= epsilon;
6403         }
6404         return false;
6405       }
6406     },
6407   /*
6408     Object: EdgeHelper.arrow
6409   */
6410   'arrow': {
6411       /*
6412       Method: render
6413       
6414       Renders an arrow into the canvas.
6415       
6416       Parameters:
6417       
6418       from - (object) An *x*, *y* object with the starting position of the arrow.
6419       to - (object) An *x*, *y* object with the ending position of the arrow.
6420       dim - (number) The dimension of the arrow.
6421       swap - (boolean) Whether to set the arrow pointing to the starting position or the ending position.
6422       canvas - (object) A <Canvas> instance.
6423       
6424       Example:
6425       (start code js)
6426       EdgeHelper.arrow.render({ x: 10, y: 30 }, { x: 10, y: 50 }, 13, false, viz.canvas);
6427       (end code)
6428       */
6429     'render': function(from, to, dim, swap, canvas){
6430         var ctx = canvas.getCtx();
6431         // invert edge direction
6432         if (swap) {
6433           var tmp = from;
6434           from = to;
6435           to = tmp;
6436         }
6437         var vect = new Complex(to.x - from.x, to.y - from.y);
6438         vect.$scale(dim / vect.norm());
6439         var intermediatePoint = new Complex(to.x - vect.x, to.y - vect.y),
6440             normal = new Complex(-vect.y / 2, vect.x / 2),
6441             v1 = intermediatePoint.add(normal), 
6442             v2 = intermediatePoint.$add(normal.$scale(-1));
6443         
6444         ctx.beginPath();
6445         ctx.moveTo(from.x, from.y);
6446         ctx.lineTo(to.x, to.y);
6447         ctx.stroke();
6448         ctx.beginPath();
6449         ctx.moveTo(v1.x, v1.y);
6450         ctx.lineTo(v2.x, v2.y);
6451         ctx.lineTo(to.x, to.y);
6452         ctx.closePath();
6453         ctx.fill();
6454     },
6455     /*
6456     Method: contains
6457     
6458     Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.
6459     
6460     Parameters:
6461     
6462     posFrom - (object) An *x*, *y* object with a <Graph.Node> position.
6463     posTo - (object) An *x*, *y* object with a <Graph.Node> position.
6464     pos - (object) An *x*, *y* object with the position to check.
6465     epsilon - (number) The dimension of the shape.
6466     
6467     Example:
6468     (start code js)
6469     EdgeHelper.arrow.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, { x: 15, y: 35 }, 30);
6470     (end code)
6471     */
6472     'contains': function(posFrom, posTo, pos, epsilon) {
6473       return EdgeHelper.line.contains(posFrom, posTo, pos, epsilon);
6474     }
6475   },
6476   /*
6477     Object: EdgeHelper.hyperline
6478   */
6479   'hyperline': {
6480     /*
6481     Method: render
6482     
6483     Renders a hyperline into the canvas. A hyperline are the lines drawn for the <Hypertree> visualization.
6484     
6485     Parameters:
6486     
6487     from - (object) An *x*, *y* object with the starting position of the hyperline. *x* and *y* must belong to [0, 1).
6488     to - (object) An *x*, *y* object with the ending position of the hyperline. *x* and *y* must belong to [0, 1).
6489     r - (number) The scaling factor.
6490     canvas - (object) A <Canvas> instance.
6491     
6492     Example:
6493     (start code js)
6494     EdgeHelper.hyperline.render({ x: 10, y: 30 }, { x: 10, y: 50 }, 100, viz.canvas);
6495     (end code)
6496     */
6497     'render': function(from, to, r, canvas){
6498       var ctx = canvas.getCtx();  
6499       var centerOfCircle = computeArcThroughTwoPoints(from, to);
6500       if (centerOfCircle.a > 1000 || centerOfCircle.b > 1000
6501           || centerOfCircle.ratio < 0) {
6502         ctx.beginPath();
6503         ctx.moveTo(from.x * r, from.y * r);
6504         ctx.lineTo(to.x * r, to.y * r);
6505         ctx.stroke();
6506       } else {
6507         var angleBegin = Math.atan2(to.y - centerOfCircle.y, to.x
6508             - centerOfCircle.x);
6509         var angleEnd = Math.atan2(from.y - centerOfCircle.y, from.x
6510             - centerOfCircle.x);
6511         var sense = sense(angleBegin, angleEnd);
6512         ctx.beginPath();
6513         ctx.arc(centerOfCircle.x * r, centerOfCircle.y * r, centerOfCircle.ratio
6514             * r, angleBegin, angleEnd, sense);
6515         ctx.stroke();
6516       }
6517       /*      
6518         Calculates the arc parameters through two points.
6519         
6520         More information in <http://en.wikipedia.org/wiki/Poincar%C3%A9_disc_model#Analytic_geometry_constructions_in_the_hyperbolic_plane> 
6521       
6522         Parameters:
6523       
6524         p1 - A <Complex> instance.
6525         p2 - A <Complex> instance.
6526         scale - The Disk's diameter.
6527       
6528         Returns:
6529       
6530         An object containing some arc properties.
6531       */
6532       function computeArcThroughTwoPoints(p1, p2){
6533         var aDen = (p1.x * p2.y - p1.y * p2.x), bDen = aDen;
6534         var sq1 = p1.squaredNorm(), sq2 = p2.squaredNorm();
6535         // Fall back to a straight line
6536         if (aDen == 0)
6537           return {
6538             x: 0,
6539             y: 0,
6540             ratio: -1
6541           };
6542     
6543         var a = (p1.y * sq2 - p2.y * sq1 + p1.y - p2.y) / aDen;
6544         var b = (p2.x * sq1 - p1.x * sq2 + p2.x - p1.x) / bDen;
6545         var x = -a / 2;
6546         var y = -b / 2;
6547         var squaredRatio = (a * a + b * b) / 4 - 1;
6548         // Fall back to a straight line
6549         if (squaredRatio < 0)
6550           return {
6551             x: 0,
6552             y: 0,
6553             ratio: -1
6554           };
6555         var ratio = Math.sqrt(squaredRatio);
6556         var out = {
6557           x: x,
6558           y: y,
6559           ratio: ratio > 1000? -1 : ratio,
6560           a: a,
6561           b: b
6562         };
6563     
6564         return out;
6565       }
6566       /*      
6567         Sets angle direction to clockwise (true) or counterclockwise (false). 
6568          
6569         Parameters: 
6570       
6571            angleBegin - Starting angle for drawing the arc. 
6572            angleEnd - The HyperLine will be drawn from angleBegin to angleEnd. 
6573       
6574         Returns: 
6575       
6576            A Boolean instance describing the sense for drawing the HyperLine. 
6577       */
6578       function sense(angleBegin, angleEnd){
6579         return (angleBegin < angleEnd)? ((angleBegin + Math.PI > angleEnd)? false
6580             : true) : ((angleEnd + Math.PI > angleBegin)? true : false);
6581       }
6582     },
6583     /*
6584     Method: contains
6585     
6586     Not Implemented
6587     
6588     Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.
6589     
6590     Parameters:
6591     
6592     posFrom - (object) An *x*, *y* object with a <Graph.Node> position.
6593     posTo - (object) An *x*, *y* object with a <Graph.Node> position.
6594     pos - (object) An *x*, *y* object with the position to check.
6595     epsilon - (number) The dimension of the shape.
6596     
6597     Example:
6598     (start code js)
6599     EdgeHelper.hyperline.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, { x: 15, y: 35 }, 30);
6600     (end code)
6601     */
6602     'contains': $.lambda(false)
6603   }
6604 };
6605
6606
6607 /*
6608  * File: Graph.Plot.js
6609  */
6610
6611 /*
6612    Object: Graph.Plot
6613
6614    <Graph> rendering and animation methods.
6615    
6616    Properties:
6617    
6618    nodeHelper - <NodeHelper> object.
6619    edgeHelper - <EdgeHelper> object.
6620 */
6621 Graph.Plot = {
6622     //Default initializer
6623     initialize: function(viz, klass){
6624       this.viz = viz;
6625       this.config = viz.config;
6626       this.node = viz.config.Node;
6627       this.edge = viz.config.Edge;
6628       this.animation = new Animation;
6629       this.nodeTypes = new klass.Plot.NodeTypes;
6630       this.edgeTypes = new klass.Plot.EdgeTypes;
6631       this.labels = viz.labels;
6632    },
6633
6634     //Add helpers
6635     nodeHelper: NodeHelper,
6636     edgeHelper: EdgeHelper,
6637     
6638     Interpolator: {
6639         //node/edge property parsers
6640         'map': {
6641           'border': 'color',
6642           'color': 'color',
6643           'width': 'number',
6644           'height': 'number',
6645           'dim': 'number',
6646           'alpha': 'number',
6647           'lineWidth': 'number',
6648           'angularWidth':'number',
6649           'span':'number',
6650           'valueArray':'array-number',
6651           'dimArray':'array-number'
6652           //'colorArray':'array-color'
6653         },
6654         
6655         //canvas specific parsers
6656         'canvas': {
6657           'globalAlpha': 'number',
6658           'fillStyle': 'color',
6659           'strokeStyle': 'color',
6660           'lineWidth': 'number',
6661           'shadowBlur': 'number',
6662           'shadowColor': 'color',
6663           'shadowOffsetX': 'number',
6664           'shadowOffsetY': 'number',
6665           'miterLimit': 'number'
6666         },
6667   
6668         //label parsers
6669         'label': {
6670           'size': 'number',
6671           'color': 'color'
6672         },
6673   
6674         //Number interpolator
6675         'compute': function(from, to, delta) {
6676           return from + (to - from) * delta;
6677         },
6678         
6679         //Position interpolators
6680         'moebius': function(elem, props, delta, vector) {
6681           var v = vector.scale(-delta);  
6682           if(v.norm() < 1) {
6683               var x = v.x, y = v.y;
6684               var ans = elem.startPos
6685                 .getc().moebiusTransformation(v);
6686               elem.pos.setc(ans.x, ans.y);
6687               v.x = x; v.y = y;
6688             }           
6689         },
6690
6691         'linear': function(elem, props, delta) {
6692             var from = elem.startPos.getc(true);
6693             var to = elem.endPos.getc(true);
6694             elem.pos.setc(this.compute(from.x, to.x, delta), 
6695                           this.compute(from.y, to.y, delta));
6696         },
6697
6698         'polar': function(elem, props, delta) {
6699           var from = elem.startPos.getp(true);
6700           var to = elem.endPos.getp();
6701           var ans = to.interpolate(from, delta);
6702           elem.pos.setp(ans.theta, ans.rho);
6703         },
6704         
6705         //Graph's Node/Edge interpolators
6706         'number': function(elem, prop, delta, getter, setter) {
6707           var from = elem[getter](prop, 'start');
6708           var to = elem[getter](prop, 'end');
6709           elem[setter](prop, this.compute(from, to, delta));
6710         },
6711
6712         'color': function(elem, prop, delta, getter, setter) {
6713           var from = $.hexToRgb(elem[getter](prop, 'start'));
6714           var to = $.hexToRgb(elem[getter](prop, 'end'));
6715           var comp = this.compute;
6716           var val = $.rgbToHex([parseInt(comp(from[0], to[0], delta)),
6717                                 parseInt(comp(from[1], to[1], delta)),
6718                                 parseInt(comp(from[2], to[2], delta))]);
6719           
6720           elem[setter](prop, val);
6721         },
6722         
6723         'array-number': function(elem, prop, delta, getter, setter) {
6724           var from = elem[getter](prop, 'start'),
6725               to = elem[getter](prop, 'end'),
6726               cur = [];
6727           for(var i=0, l=from.length; i<l; i++) {
6728             var fromi = from[i], toi = to[i];
6729             if(fromi.length) {
6730               for(var j=0, len=fromi.length, curi=[]; j<len; j++) {
6731                 curi.push(this.compute(fromi[j], toi[j], delta));
6732               }
6733               cur.push(curi);
6734             } else {
6735               cur.push(this.compute(fromi, toi, delta));
6736             }
6737           }
6738           elem[setter](prop, cur);
6739         },
6740         
6741         'node': function(elem, props, delta, map, getter, setter) {
6742           map = this[map];
6743           if(props) {
6744             var len = props.length;
6745             for(var i=0; i<len; i++) {
6746               var pi = props[i];
6747               this[map[pi]](elem, pi, delta, getter, setter);
6748             }
6749           } else {
6750             for(var pi in map) {
6751               this[map[pi]](elem, pi, delta, getter, setter);
6752             }
6753           }
6754         },
6755         
6756         'edge': function(elem, props, delta, mapKey, getter, setter) {
6757             var adjs = elem.adjacencies;
6758             for(var id in adjs) this['node'](adjs[id], props, delta, mapKey, getter, setter);
6759         },
6760         
6761         'node-property': function(elem, props, delta) {
6762           this['node'](elem, props, delta, 'map', 'getData', 'setData');
6763         },
6764         
6765         'edge-property': function(elem, props, delta) {
6766           this['edge'](elem, props, delta, 'map', 'getData', 'setData');  
6767         },
6768
6769         'label-property': function(elem, props, delta) {
6770           this['node'](elem, props, delta, 'label', 'getLabelData', 'setLabelData');
6771         },
6772         
6773         'node-style': function(elem, props, delta) {
6774           this['node'](elem, props, delta, 'canvas', 'getCanvasStyle', 'setCanvasStyle');
6775         },
6776         
6777         'edge-style': function(elem, props, delta) {
6778           this['edge'](elem, props, delta, 'canvas', 'getCanvasStyle', 'setCanvasStyle');  
6779         }
6780     },
6781     
6782   
6783     /*
6784        sequence
6785     
6786        Iteratively performs an action while refreshing the state of the visualization.
6787
6788        Parameters:
6789
6790        options - (object) An object containing some sequence options described below
6791        condition - (function) A function returning a boolean instance in order to stop iterations.
6792        step - (function) A function to execute on each step of the iteration.
6793        onComplete - (function) A function to execute when the sequence finishes.
6794        duration - (number) Duration (in milliseconds) of each step.
6795
6796       Example:
6797        (start code js)
6798         var rg = new $jit.RGraph(options);
6799         var i = 0;
6800         rg.fx.sequence({
6801           condition: function() {
6802            return i == 10;
6803           },
6804           step: function() {
6805             alert(i++);
6806           },
6807           onComplete: function() {
6808            alert('done!');
6809           }
6810         });
6811        (end code)
6812
6813     */
6814     sequence: function(options) {
6815         var that = this;
6816         options = $.merge({
6817           condition: $.lambda(false),
6818           step: $.empty,
6819           onComplete: $.empty,
6820           duration: 200
6821         }, options || {});
6822
6823         var interval = setInterval(function() {
6824           if(options.condition()) {
6825             options.step();
6826           } else {
6827             clearInterval(interval);
6828             options.onComplete();
6829           }
6830           that.viz.refresh(true);
6831         }, options.duration);
6832     },
6833     
6834     /*
6835       prepare
6836  
6837       Prepare graph position and other attribute values before performing an Animation. 
6838       This method is used internally by the Toolkit.
6839       
6840       See also:
6841        
6842        <Animation>, <Graph.Plot.animate>
6843
6844     */
6845     prepare: function(modes) {
6846       var graph = this.viz.graph,
6847           accessors = {
6848             'node-property': {
6849               'getter': 'getData',
6850               'setter': 'setData'
6851             },
6852             'edge-property': {
6853               'getter': 'getData',
6854               'setter': 'setData'
6855             },
6856             'node-style': {
6857               'getter': 'getCanvasStyle',
6858               'setter': 'setCanvasStyle'
6859             },
6860             'edge-style': {
6861               'getter': 'getCanvasStyle',
6862               'setter': 'setCanvasStyle'
6863             }
6864           };
6865
6866       //parse modes
6867       var m = {};
6868       if($.type(modes) == 'array') {
6869         for(var i=0, len=modes.length; i < len; i++) {
6870           var elems = modes[i].split(':');
6871           m[elems.shift()] = elems;
6872         }
6873       } else {
6874         for(var p in modes) {
6875           if(p == 'position') {
6876             m[modes.position] = [];
6877           } else {
6878             m[p] = $.splat(modes[p]);
6879           }
6880         }
6881       }
6882       
6883       graph.eachNode(function(node) { 
6884         node.startPos.set(node.pos);
6885         $.each(['node-property', 'node-style'], function(p) {
6886           if(p in m) {
6887             var prop = m[p];
6888             for(var i=0, l=prop.length; i < l; i++) {
6889               node[accessors[p].setter](prop[i], node[accessors[p].getter](prop[i]), 'start');
6890             }
6891           }
6892         });
6893         $.each(['edge-property', 'edge-style'], function(p) {
6894           if(p in m) {
6895             var prop = m[p];
6896             node.eachAdjacency(function(adj) {
6897               for(var i=0, l=prop.length; i < l; i++) {
6898                 adj[accessors[p].setter](prop[i], adj[accessors[p].getter](prop[i]), 'start');
6899               }
6900             });
6901           }
6902         });
6903       });
6904       return m;
6905     },
6906     
6907     /*
6908        Method: animate
6909     
6910        Animates a <Graph> by interpolating some <Graph.Node>, <Graph.Adjacence> or <Graph.Label> properties.
6911
6912        Parameters:
6913
6914        opt - (object) Animation options. The object properties are described below
6915        duration - (optional) Described in <Options.Fx>.
6916        fps - (optional) Described in <Options.Fx>.
6917        hideLabels - (optional|boolean) Whether to hide labels during the animation.
6918        modes - (required|object) An object with animation modes (described below).
6919
6920        Animation modes:
6921        
6922        Animation modes are strings representing different node/edge and graph properties that you'd like to animate. 
6923        They are represented by an object that has as keys main categories of properties to animate and as values a list 
6924        of these specific properties. The properties are described below
6925        
6926        position - Describes the way nodes' positions must be interpolated. Possible values are 'linear', 'polar' or 'moebius'.
6927        node-property - Describes which Node properties will be interpolated. These properties can be any of the ones defined in <Options.Node>.
6928        edge-property - Describes which Edge properties will be interpolated. These properties can be any the ones defined in <Options.Edge>.
6929        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.
6930        node-style - Describes which Node Canvas Styles will be interpolated. These are specific canvas properties like fillStyle, strokeStyle, lineWidth, shadowBlur, shadowColor, shadowOffsetX, shadowOffsetY, etc.
6931        edge-style - Describes which Edge Canvas Styles will be interpolated. These are specific canvas properties like fillStyle, strokeStyle, lineWidth, shadowBlur, shadowColor, shadowOffsetX, shadowOffsetY, etc.
6932
6933        Example:
6934        (start code js)
6935        var viz = new $jit.Viz(options);
6936        //...tweak some Data, CanvasStyles or LabelData properties...
6937        viz.fx.animate({
6938          modes: {
6939            'position': 'linear',
6940            'node-property': ['width', 'height'],
6941            'node-style': 'shadowColor',
6942            'label-property': 'size'
6943          },
6944          hideLabels: false
6945        });
6946        //...can also be written like this...
6947        viz.fx.animate({
6948          modes: ['linear',
6949                  'node-property:width:height',
6950                  'node-style:shadowColor',
6951                  'label-property:size'],
6952          hideLabels: false
6953        });
6954        (end code)
6955     */
6956     animate: function(opt, versor) {
6957       opt = $.merge(this.viz.config, opt || {});
6958       var that = this,
6959           viz = this.viz,
6960           graph  = viz.graph,
6961           interp = this.Interpolator,
6962           animation =  opt.type === 'nodefx'? this.nodeFxAnimation : this.animation;
6963       //prepare graph values
6964       var m = this.prepare(opt.modes);
6965       
6966       //animate
6967       if(opt.hideLabels) this.labels.hideLabels(true);
6968       animation.setOptions($.extend(opt, {
6969         $animating: false,
6970         compute: function(delta) {
6971           graph.eachNode(function(node) { 
6972             for(var p in m) {
6973               interp[p](node, m[p], delta, versor);
6974             }
6975           });
6976           that.plot(opt, this.$animating, delta);
6977           this.$animating = true;
6978         },
6979         complete: function() {
6980           if(opt.hideLabels) that.labels.hideLabels(false);
6981           that.plot(opt);
6982           opt.onComplete();
6983           //TODO(nico): This shouldn't be here!
6984           //opt.onAfterCompute();
6985         }       
6986       })).start();
6987     },
6988     
6989     /*
6990       nodeFx
6991    
6992       Apply animation to node properties like color, width, height, dim, etc.
6993   
6994       Parameters:
6995   
6996       options - Animation options. This object properties is described below
6997       elements - The Elements to be transformed. This is an object that has a properties
6998       
6999       (start code js)
7000       'elements': {
7001         //can also be an array of ids
7002         'id': 'id-of-node-to-transform',
7003         //properties to be modified. All properties are optional.
7004         'properties': {
7005           'color': '#ccc', //some color
7006           'width': 10, //some width
7007           'height': 10, //some height
7008           'dim': 20, //some dim
7009           'lineWidth': 10 //some line width
7010         } 
7011       }
7012       (end code)
7013       
7014       - _reposition_ Whether to recalculate positions and add a motion animation. 
7015       This might be used when changing _width_ or _height_ properties in a <Layouts.Tree> like layout. Default's *false*.
7016       
7017       - _onComplete_ A method that is called when the animation completes.
7018       
7019       ...and all other <Graph.Plot.animate> options like _duration_, _fps_, _transition_, etc.
7020   
7021       Example:
7022       (start code js)
7023        var rg = new RGraph(canvas, config); //can be also Hypertree or ST
7024        rg.fx.nodeFx({
7025          'elements': {
7026            'id':'mynodeid',
7027            'properties': {
7028              'color':'#ccf'
7029            },
7030            'transition': Trans.Quart.easeOut
7031          }
7032        });
7033       (end code)    
7034    */
7035    nodeFx: function(opt) {
7036      var viz = this.viz,
7037          graph  = viz.graph,
7038          animation = this.nodeFxAnimation,
7039          options = $.merge(this.viz.config, {
7040            'elements': {
7041              'id': false,
7042              'properties': {}
7043            },
7044            'reposition': false
7045          });
7046      opt = $.merge(options, opt || {}, {
7047        onBeforeCompute: $.empty,
7048        onAfterCompute: $.empty
7049      });
7050      //check if an animation is running
7051      animation.stopTimer();
7052      var props = opt.elements.properties;
7053      //set end values for nodes
7054      if(!opt.elements.id) {
7055        graph.eachNode(function(n) {
7056          for(var prop in props) {
7057            n.setData(prop, props[prop], 'end');
7058          }
7059        });
7060      } else {
7061        var ids = $.splat(opt.elements.id);
7062        $.each(ids, function(id) {
7063          var n = graph.getNode(id);
7064          if(n) {
7065            for(var prop in props) {
7066              n.setData(prop, props[prop], 'end');
7067            }
7068          }
7069        });
7070      }
7071      //get keys
7072      var propnames = [];
7073      for(var prop in props) propnames.push(prop);
7074      //add node properties modes
7075      var modes = ['node-property:' + propnames.join(':')];
7076      //set new node positions
7077      if(opt.reposition) {
7078        modes.push('linear');
7079        viz.compute('end');
7080      }
7081      //animate
7082      this.animate($.merge(opt, {
7083        modes: modes,
7084        type: 'nodefx'
7085      }));
7086    },
7087
7088     
7089     /*
7090        Method: plot
7091     
7092        Plots a <Graph>.
7093
7094        Parameters:
7095
7096        opt - (optional) Plotting options. Most of them are described in <Options.Fx>.
7097
7098        Example:
7099
7100        (start code js)
7101        var viz = new $jit.Viz(options);
7102        viz.fx.plot(); 
7103        (end code)
7104
7105     */
7106    plot: function(opt, animating) {
7107      var viz = this.viz, 
7108          aGraph = viz.graph, 
7109          canvas = viz.canvas, 
7110          id = viz.root, 
7111          that = this, 
7112          ctx = canvas.getCtx(), 
7113          min = Math.min,
7114          opt = opt || this.viz.controller;
7115      
7116      opt.clearCanvas && canvas.clear();
7117        
7118      var root = aGraph.getNode(id);
7119      if(!root) return;
7120      
7121      var T = !!root.visited;
7122      aGraph.eachNode(function(node) {
7123        var nodeAlpha = node.getData('alpha');
7124        node.eachAdjacency(function(adj) {
7125          var nodeTo = adj.nodeTo;
7126          if(!!nodeTo.visited === T && node.drawn && nodeTo.drawn) {
7127            !animating && opt.onBeforePlotLine(adj);
7128            that.plotLine(adj, canvas, animating);
7129            !animating && opt.onAfterPlotLine(adj);
7130          }
7131        });
7132        if(node.drawn) {
7133          !animating && opt.onBeforePlotNode(node);
7134          that.plotNode(node, canvas, animating);
7135          !animating && opt.onAfterPlotNode(node);
7136        }
7137        if(!that.labelsHidden && opt.withLabels) {
7138          if(node.drawn && nodeAlpha >= 0.95) {
7139            that.labels.plotLabel(canvas, node, opt);
7140          } else {
7141            that.labels.hideLabel(node, false);
7142          }
7143        }
7144        node.visited = !T;
7145      });
7146     },
7147
7148   /*
7149       Plots a Subtree.
7150    */
7151    plotTree: function(node, opt, animating) {
7152        var that = this, 
7153        viz = this.viz, 
7154        canvas = viz.canvas,
7155        config = this.config,
7156        ctx = canvas.getCtx();
7157        var nodeAlpha = node.getData('alpha');
7158        node.eachSubnode(function(elem) {
7159          if(opt.plotSubtree(node, elem) && elem.exist && elem.drawn) {
7160              var adj = node.getAdjacency(elem.id);
7161              !animating && opt.onBeforePlotLine(adj);
7162              that.plotLine(adj, canvas, animating);
7163              !animating && opt.onAfterPlotLine(adj);
7164              that.plotTree(elem, opt, animating);
7165          }
7166        });
7167        if(node.drawn) {
7168            !animating && opt.onBeforePlotNode(node);
7169            this.plotNode(node, canvas, animating);
7170            !animating && opt.onAfterPlotNode(node);
7171            if(!opt.hideLabels && opt.withLabels && nodeAlpha >= 0.95) 
7172                this.labels.plotLabel(canvas, node, opt);
7173            else 
7174                this.labels.hideLabel(node, false);
7175        } else {
7176            this.labels.hideLabel(node, true);
7177        }
7178    },
7179
7180   /*
7181        Method: plotNode
7182     
7183        Plots a <Graph.Node>.
7184
7185        Parameters:
7186        
7187        node - (object) A <Graph.Node>.
7188        canvas - (object) A <Canvas> element.
7189
7190     */
7191     plotNode: function(node, canvas, animating) {
7192         var f = node.getData('type'), 
7193             ctxObj = this.node.CanvasStyles;
7194         if(f != 'none') {
7195           var width = node.getData('lineWidth'),
7196               color = node.getData('color'),
7197               alpha = node.getData('alpha'),
7198               ctx = canvas.getCtx();
7199           ctx.save();
7200           ctx.lineWidth = width;
7201           ctx.fillStyle = ctx.strokeStyle = color;
7202           ctx.globalAlpha = alpha;
7203           
7204           for(var s in ctxObj) {
7205             ctx[s] = node.getCanvasStyle(s);
7206           }
7207
7208           this.nodeTypes[f].render.call(this, node, canvas, animating);
7209           ctx.restore();
7210         }
7211     },
7212     
7213     /*
7214        Method: plotLine
7215     
7216        Plots a <Graph.Adjacence>.
7217
7218        Parameters:
7219
7220        adj - (object) A <Graph.Adjacence>.
7221        canvas - (object) A <Canvas> instance.
7222
7223     */
7224     plotLine: function(adj, canvas, animating) {
7225       var f = adj.getData('type'),
7226           ctxObj = this.edge.CanvasStyles;
7227       if(f != 'none') {
7228         var width = adj.getData('lineWidth'),
7229             color = adj.getData('color'),
7230             ctx = canvas.getCtx(),
7231             nodeFrom = adj.nodeFrom,
7232             nodeTo = adj.nodeTo;
7233         
7234         ctx.save();
7235         ctx.lineWidth = width;
7236         ctx.fillStyle = ctx.strokeStyle = color;
7237         ctx.globalAlpha = Math.min(nodeFrom.getData('alpha'), 
7238             nodeTo.getData('alpha'), 
7239             adj.getData('alpha'));
7240         
7241         for(var s in ctxObj) {
7242           ctx[s] = adj.getCanvasStyle(s);
7243         }
7244
7245         this.edgeTypes[f].render.call(this, adj, canvas, animating);
7246         ctx.restore();
7247       }
7248     }    
7249   
7250 };
7251
7252 /*
7253   Object: Graph.Plot3D
7254   
7255   <Graph> 3D rendering and animation methods.
7256   
7257   Properties:
7258   
7259   nodeHelper - <NodeHelper> object.
7260   edgeHelper - <EdgeHelper> object.
7261
7262 */
7263 Graph.Plot3D = $.merge(Graph.Plot, {
7264   Interpolator: {
7265     'linear': function(elem, props, delta) {
7266       var from = elem.startPos.getc(true);
7267       var to = elem.endPos.getc(true);
7268       elem.pos.setc(this.compute(from.x, to.x, delta), 
7269                     this.compute(from.y, to.y, delta),
7270                     this.compute(from.z, to.z, delta));
7271     }
7272   },
7273   
7274   plotNode: function(node, canvas) {
7275     if(node.getData('type') == 'none') return;
7276     this.plotElement(node, canvas, {
7277       getAlpha: function() {
7278         return node.getData('alpha');
7279       }
7280     });
7281   },
7282   
7283   plotLine: function(adj, canvas) {
7284     if(adj.getData('type') == 'none') return;
7285     this.plotElement(adj, canvas, {
7286       getAlpha: function() {
7287         return Math.min(adj.nodeFrom.getData('alpha'),
7288                         adj.nodeTo.getData('alpha'),
7289                         adj.getData('alpha'));
7290       }
7291     });
7292   },
7293   
7294   plotElement: function(elem, canvas, opt) {
7295     var gl = canvas.getCtx(),
7296         viewMatrix = new Matrix4,
7297         lighting = canvas.config.Scene.Lighting,
7298         wcanvas = canvas.canvases[0],
7299         program = wcanvas.program,
7300         camera = wcanvas.camera;
7301     
7302     if(!elem.geometry) {
7303       elem.geometry = new O3D[elem.getData('type')];
7304     }
7305     elem.geometry.update(elem);
7306     if(!elem.webGLVertexBuffer) {
7307       var vertices = [],
7308           faces = [],
7309           normals = [],
7310           vertexIndex = 0,
7311           geom = elem.geometry;
7312       
7313       for(var i=0, vs=geom.vertices, fs=geom.faces, fsl=fs.length; i<fsl; i++) {
7314         var face = fs[i],
7315             v1 = vs[face.a],
7316             v2 = vs[face.b],
7317             v3 = vs[face.c],
7318             v4 = face.d? vs[face.d] : false,
7319             n = face.normal;
7320         
7321         vertices.push(v1.x, v1.y, v1.z);
7322         vertices.push(v2.x, v2.y, v2.z);
7323         vertices.push(v3.x, v3.y, v3.z);
7324         if(v4) vertices.push(v4.x, v4.y, v4.z);
7325             
7326         normals.push(n.x, n.y, n.z);
7327         normals.push(n.x, n.y, n.z);
7328         normals.push(n.x, n.y, n.z);
7329         if(v4) normals.push(n.x, n.y, n.z);
7330             
7331         faces.push(vertexIndex, vertexIndex +1, vertexIndex +2);
7332         if(v4) {
7333           faces.push(vertexIndex, vertexIndex +2, vertexIndex +3);
7334           vertexIndex += 4;
7335         } else {
7336           vertexIndex += 3;
7337         }
7338       }
7339       //create and store vertex data
7340       elem.webGLVertexBuffer = gl.createBuffer();
7341       gl.bindBuffer(gl.ARRAY_BUFFER, elem.webGLVertexBuffer);
7342       gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
7343       //create and store faces index data
7344       elem.webGLFaceBuffer = gl.createBuffer();
7345       gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elem.webGLFaceBuffer);
7346       gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(faces), gl.STATIC_DRAW);
7347       elem.webGLFaceCount = faces.length;
7348       //calculate vertex normals and store them
7349       elem.webGLNormalBuffer = gl.createBuffer();
7350       gl.bindBuffer(gl.ARRAY_BUFFER, elem.webGLNormalBuffer);
7351       gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(normals), gl.STATIC_DRAW);
7352     }
7353     viewMatrix.multiply(camera.matrix, elem.geometry.matrix);
7354     //send matrix data
7355     gl.uniformMatrix4fv(program.viewMatrix, false, viewMatrix.flatten());
7356     gl.uniformMatrix4fv(program.projectionMatrix, false, camera.projectionMatrix.flatten());
7357     //send normal matrix for lighting
7358     var normalMatrix = Matrix4.makeInvert(viewMatrix);
7359     normalMatrix.$transpose();
7360     gl.uniformMatrix4fv(program.normalMatrix, false, normalMatrix.flatten());
7361     //send color data
7362     var color = $.hexToRgb(elem.getData('color'));
7363     color.push(opt.getAlpha());
7364     gl.uniform4f(program.color, color[0] / 255, color[1] / 255, color[2] / 255, color[3]);
7365     //send lighting data
7366     gl.uniform1i(program.enableLighting, lighting.enable);
7367     if(lighting.enable) {
7368       //set ambient light color
7369       if(lighting.ambient) {
7370         var acolor = lighting.ambient;
7371         gl.uniform3f(program.ambientColor, acolor[0], acolor[1], acolor[2]);
7372       }
7373       //set directional light
7374       if(lighting.directional) {
7375         var dir = lighting.directional,
7376             color = dir.color,
7377             pos = dir.direction,
7378             vd = new Vector3(pos.x, pos.y, pos.z).normalize().$scale(-1);
7379         gl.uniform3f(program.lightingDirection, vd.x, vd.y, vd.z);
7380         gl.uniform3f(program.directionalColor, color[0], color[1], color[2]);
7381       }
7382     }
7383     //send vertices data
7384     gl.bindBuffer(gl.ARRAY_BUFFER, elem.webGLVertexBuffer);
7385     gl.vertexAttribPointer(program.position, 3, gl.FLOAT, false, 0, 0);
7386     //send normals data
7387     gl.bindBuffer(gl.ARRAY_BUFFER, elem.webGLNormalBuffer);
7388     gl.vertexAttribPointer(program.normal, 3, gl.FLOAT, false, 0, 0);
7389     //draw!
7390     gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elem.webGLFaceBuffer );
7391     gl.drawElements(gl.TRIANGLES, elem.webGLFaceCount, gl.UNSIGNED_SHORT, 0);
7392   }
7393 });
7394
7395
7396 /*
7397  * File: Graph.Label.js
7398  *
7399 */
7400
7401 /*
7402    Object: Graph.Label
7403
7404    An interface for plotting/hiding/showing labels.
7405
7406    Description:
7407
7408    This is a generic interface for plotting/hiding/showing labels.
7409    The <Graph.Label> interface is implemented in multiple ways to provide
7410    different label types.
7411
7412    For example, the Graph.Label interface is implemented as <Graph.Label.HTML> to provide
7413    HTML label elements. Also we provide the <Graph.Label.SVG> interface for SVG type labels. 
7414    The <Graph.Label.Native> interface implements these methods with the native Canvas text rendering functions.
7415    
7416    All subclasses (<Graph.Label.HTML>, <Graph.Label.SVG> and <Graph.Label.Native>) implement the method plotLabel.
7417 */
7418
7419 Graph.Label = {};
7420
7421 /*
7422    Class: Graph.Label.Native
7423
7424    Implements labels natively, using the Canvas text API.
7425 */
7426 Graph.Label.Native = new Class({
7427     initialize: function(viz) {
7428       this.viz = viz;
7429     },
7430
7431     /*
7432        Method: plotLabel
7433
7434        Plots a label for a given node.
7435
7436        Parameters:
7437
7438        canvas - (object) A <Canvas> instance.
7439        node - (object) A <Graph.Node>.
7440        controller - (object) A configuration object.
7441        
7442        Example:
7443        
7444        (start code js)
7445        var viz = new $jit.Viz(options);
7446        var node = viz.graph.getNode('nodeId');
7447        viz.labels.plotLabel(viz.canvas, node, viz.config);
7448        (end code)
7449     */
7450     plotLabel: function(canvas, node, controller) {
7451       var ctx = canvas.getCtx();
7452       var pos = node.pos.getc(true);
7453
7454       ctx.font = node.getLabelData('style') + ' ' + node.getLabelData('size') + 'px ' + node.getLabelData('family');
7455       ctx.textAlign = node.getLabelData('textAlign');
7456       ctx.fillStyle = ctx.strokeStyle = node.getLabelData('color');
7457       ctx.textBaseline = node.getLabelData('textBaseline');
7458
7459       this.renderLabel(canvas, node, controller);
7460     },
7461
7462     /*
7463        renderLabel
7464
7465        Does the actual rendering of the label in the canvas. The default
7466        implementation renders the label close to the position of the node, this
7467        method should be overriden to position the labels differently.
7468
7469        Parameters:
7470
7471        canvas - A <Canvas> instance.
7472        node - A <Graph.Node>.
7473        controller - A configuration object. See also <Hypertree>, <RGraph>, <ST>.
7474     */
7475     renderLabel: function(canvas, node, controller) {
7476       var ctx = canvas.getCtx();
7477       var pos = node.pos.getc(true);
7478       ctx.fillText(node.name, pos.x, pos.y + node.getData("height") / 2);
7479     },
7480
7481     hideLabel: $.empty,
7482     hideLabels: $.empty
7483 });
7484
7485 /*
7486    Class: Graph.Label.DOM
7487
7488    Abstract Class implementing some DOM label methods.
7489
7490    Implemented by:
7491
7492    <Graph.Label.HTML> and <Graph.Label.SVG>.
7493
7494 */
7495 Graph.Label.DOM = new Class({
7496     //A flag value indicating if node labels are being displayed or not.
7497     labelsHidden: false,
7498     //Label container
7499     labelContainer: false,
7500     //Label elements hash.
7501     labels: {},
7502
7503     /*
7504        Method: getLabelContainer
7505
7506        Lazy fetcher for the label container.
7507
7508        Returns:
7509
7510        The label container DOM element.
7511
7512        Example:
7513
7514       (start code js)
7515         var viz = new $jit.Viz(options);
7516         var labelContainer = viz.labels.getLabelContainer();
7517         alert(labelContainer.innerHTML);
7518       (end code)
7519     */
7520     getLabelContainer: function() {
7521       return this.labelContainer ?
7522         this.labelContainer :
7523         this.labelContainer = document.getElementById(this.viz.config.labelContainer);
7524     },
7525
7526     /*
7527        Method: getLabel
7528
7529        Lazy fetcher for the label element.
7530
7531        Parameters:
7532
7533        id - (string) The label id (which is also a <Graph.Node> id).
7534
7535        Returns:
7536
7537        The label element.
7538
7539        Example:
7540
7541       (start code js)
7542         var viz = new $jit.Viz(options);
7543         var label = viz.labels.getLabel('someid');
7544         alert(label.innerHTML);
7545       (end code)
7546
7547     */
7548     getLabel: function(id) {
7549       return (id in this.labels && this.labels[id] != null) ?
7550         this.labels[id] :
7551         this.labels[id] = document.getElementById(id);
7552     },
7553
7554     /*
7555        Method: hideLabels
7556
7557        Hides all labels (by hiding the label container).
7558
7559        Parameters:
7560
7561        hide - (boolean) A boolean value indicating if the label container must be hidden or not.
7562
7563        Example:
7564        (start code js)
7565         var viz = new $jit.Viz(options);
7566         rg.labels.hideLabels(true);
7567        (end code)
7568
7569     */
7570     hideLabels: function (hide) {
7571       var container = this.getLabelContainer();
7572       if(hide)
7573         container.style.display = 'none';
7574       else
7575         container.style.display = '';
7576       this.labelsHidden = hide;
7577     },
7578
7579     /*
7580        Method: clearLabels
7581
7582        Clears the label container.
7583
7584        Useful when using a new visualization with the same canvas element/widget.
7585
7586        Parameters:
7587
7588        force - (boolean) Forces deletion of all labels.
7589
7590        Example:
7591        (start code js)
7592         var viz = new $jit.Viz(options);
7593         viz.labels.clearLabels();
7594         (end code)
7595     */
7596     clearLabels: function(force) {
7597       for(var id in this.labels) {
7598         if (force || !this.viz.graph.hasNode(id)) {
7599           this.disposeLabel(id);
7600           delete this.labels[id];
7601         }
7602       }
7603     },
7604
7605     /*
7606        Method: disposeLabel
7607
7608        Removes a label.
7609
7610        Parameters:
7611
7612        id - (string) A label id (which generally is also a <Graph.Node> id).
7613
7614        Example:
7615        (start code js)
7616         var viz = new $jit.Viz(options);
7617         viz.labels.disposeLabel('labelid');
7618        (end code)
7619     */
7620     disposeLabel: function(id) {
7621       var elem = this.getLabel(id);
7622       if(elem && elem.parentNode) {
7623         elem.parentNode.removeChild(elem);
7624       }
7625     },
7626
7627     /*
7628        Method: hideLabel
7629
7630        Hides the corresponding <Graph.Node> label.
7631
7632        Parameters:
7633
7634        node - (object) A <Graph.Node>. Can also be an array of <Graph.Nodes>.
7635        show - (boolean) If *true*, nodes will be shown. Otherwise nodes will be hidden.
7636
7637        Example:
7638        (start code js)
7639         var rg = new $jit.Viz(options);
7640         viz.labels.hideLabel(viz.graph.getNode('someid'), false);
7641        (end code)
7642     */
7643     hideLabel: function(node, show) {
7644       node = $.splat(node);
7645       var st = show ? "" : "none", lab, that = this;
7646       $.each(node, function(n) {
7647         var lab = that.getLabel(n.id);
7648         if (lab) {
7649           lab.style.display = st;
7650         }
7651       });
7652     },
7653
7654     /*
7655        fitsInCanvas
7656
7657        Returns _true_ or _false_ if the label for the node is contained in the canvas dom element or not.
7658
7659        Parameters:
7660
7661        pos - A <Complex> instance (I'm doing duck typing here so any object with _x_ and _y_ parameters will do).
7662        canvas - A <Canvas> instance.
7663
7664        Returns:
7665
7666        A boolean value specifying if the label is contained in the <Canvas> DOM element or not.
7667
7668     */
7669     fitsInCanvas: function(pos, canvas) {
7670       var size = canvas.getSize();
7671       if(pos.x >= size.width || pos.x < 0
7672          || pos.y >= size.height || pos.y < 0) return false;
7673        return true;
7674     }
7675 });
7676
7677 /*
7678    Class: Graph.Label.HTML
7679
7680    Implements HTML labels.
7681
7682    Extends:
7683
7684    All <Graph.Label.DOM> methods.
7685
7686 */
7687 Graph.Label.HTML = new Class({
7688     Implements: Graph.Label.DOM,
7689
7690     /*
7691        Method: plotLabel
7692
7693        Plots a label for a given node.
7694
7695        Parameters:
7696
7697        canvas - (object) A <Canvas> instance.
7698        node - (object) A <Graph.Node>.
7699        controller - (object) A configuration object.
7700        
7701       Example:
7702        
7703        (start code js)
7704        var viz = new $jit.Viz(options);
7705        var node = viz.graph.getNode('nodeId');
7706        viz.labels.plotLabel(viz.canvas, node, viz.config);
7707        (end code)
7708
7709
7710     */
7711     plotLabel: function(canvas, node, controller) {
7712       var id = node.id, tag = this.getLabel(id);
7713
7714       if(!tag && !(tag = document.getElementById(id))) {
7715         tag = document.createElement('div');
7716         var container = this.getLabelContainer();
7717         tag.id = id;
7718         tag.className = 'node';
7719         tag.style.position = 'absolute';
7720         controller.onCreateLabel(tag, node);
7721         container.appendChild(tag);
7722         this.labels[node.id] = tag;
7723       }
7724
7725       this.placeLabel(tag, node, controller);
7726     }
7727 });
7728
7729 /*
7730    Class: Graph.Label.SVG
7731
7732    Implements SVG labels.
7733
7734    Extends:
7735
7736    All <Graph.Label.DOM> methods.
7737 */
7738 Graph.Label.SVG = new Class({
7739     Implements: Graph.Label.DOM,
7740
7741     /*
7742        Method: plotLabel
7743
7744        Plots a label for a given node.
7745
7746        Parameters:
7747
7748        canvas - (object) A <Canvas> instance.
7749        node - (object) A <Graph.Node>.
7750        controller - (object) A configuration object.
7751        
7752        Example:
7753        
7754        (start code js)
7755        var viz = new $jit.Viz(options);
7756        var node = viz.graph.getNode('nodeId');
7757        viz.labels.plotLabel(viz.canvas, node, viz.config);
7758        (end code)
7759
7760
7761     */
7762     plotLabel: function(canvas, node, controller) {
7763       var id = node.id, tag = this.getLabel(id);
7764       if(!tag && !(tag = document.getElementById(id))) {
7765         var ns = 'http://www.w3.org/2000/svg';
7766           tag = document.createElementNS(ns, 'svg:text');
7767         var tspan = document.createElementNS(ns, 'svg:tspan');
7768         tag.appendChild(tspan);
7769         var container = this.getLabelContainer();
7770         tag.setAttribute('id', id);
7771         tag.setAttribute('class', 'node');
7772         container.appendChild(tag);
7773         controller.onCreateLabel(tag, node);
7774         this.labels[node.id] = tag;
7775       }
7776       this.placeLabel(tag, node, controller);
7777     }
7778 });
7779
7780
7781
7782 Graph.Geom = new Class({
7783
7784   initialize: function(viz) {
7785     this.viz = viz;
7786     this.config = viz.config;
7787     this.node = viz.config.Node;
7788     this.edge = viz.config.Edge;
7789   },
7790   /*
7791     Applies a translation to the tree.
7792   
7793     Parameters:
7794   
7795     pos - A <Complex> number specifying translation vector.
7796     prop - A <Graph.Node> position property ('pos', 'start' or 'end').
7797   
7798     Example:
7799   
7800     (start code js)
7801       st.geom.translate(new Complex(300, 100), 'end');
7802     (end code)
7803   */  
7804   translate: function(pos, prop) {
7805      prop = $.splat(prop);
7806      this.viz.graph.eachNode(function(elem) {
7807          $.each(prop, function(p) { elem.getPos(p).$add(pos); });
7808      });
7809   },
7810   /*
7811     Hides levels of the tree until it properly fits in canvas.
7812   */  
7813   setRightLevelToShow: function(node, canvas, callback) {
7814      var level = this.getRightLevelToShow(node, canvas), 
7815          fx = this.viz.labels,
7816          opt = $.merge({
7817            execShow:true,
7818            execHide:true,
7819            onHide: $.empty,
7820            onShow: $.empty
7821          }, callback || {});
7822      node.eachLevel(0, this.config.levelsToShow, function(n) {
7823          var d = n._depth - node._depth;
7824          if(d > level) {
7825              opt.onHide(n);
7826              if(opt.execHide) {
7827                n.drawn = false; 
7828                n.exist = false;
7829                fx.hideLabel(n, false);
7830              }
7831          } else {
7832              opt.onShow(n);
7833              if(opt.execShow) {
7834                n.exist = true;
7835              }
7836          }
7837      });
7838      node.drawn= true;
7839   },
7840   /*
7841     Returns the right level to show for the current tree in order to fit in canvas.
7842   */  
7843   getRightLevelToShow: function(node, canvas) {
7844      var config = this.config;
7845      var level = config.levelsToShow;
7846      var constrained = config.constrained;
7847      if(!constrained) return level;
7848      while(!this.treeFitsInCanvas(node, canvas, level) && level > 1) { level-- ; }
7849      return level;
7850   }
7851 });
7852
7853 /*
7854  * File: Loader.js
7855  * 
7856  */
7857
7858 /*
7859    Object: Loader
7860
7861    Provides methods for loading and serving JSON data.
7862 */
7863 var Loader = {
7864      construct: function(json) {
7865         var isGraph = ($.type(json) == 'array');
7866         var ans = new Graph(this.graphOptions, this.config.Node, this.config.Edge, this.config.Label);
7867         if(!isGraph) 
7868             //make tree
7869             (function (ans, json) {
7870                 ans.addNode(json);
7871                 if(json.children) {
7872                   for(var i=0, ch = json.children; i<ch.length; i++) {
7873                     ans.addAdjacence(json, ch[i]);
7874                     arguments.callee(ans, ch[i]);
7875                   }
7876                 }
7877             })(ans, json);
7878         else
7879             //make graph
7880             (function (ans, json) {
7881                 var getNode = function(id) {
7882                   for(var i=0, l=json.length; i<l; i++) {
7883                     if(json[i].id == id) {
7884                       return json[i];
7885                     }
7886                   }
7887                   // The node was not defined in the JSON
7888                   // Let's create it
7889                   var newNode = {
7890                                 "id" : id,
7891                                 "name" : id
7892                         };
7893                   return ans.addNode(newNode);
7894                 };
7895
7896                 for(var i=0, l=json.length; i<l; i++) {
7897                   ans.addNode(json[i]);
7898                   var adj = json[i].adjacencies;
7899                   if (adj) {
7900                     for(var j=0, lj=adj.length; j<lj; j++) {
7901                       var node = adj[j], data = {};
7902                       if(typeof adj[j] != 'string') {
7903                         data = $.merge(node.data, {});
7904                         node = node.nodeTo;
7905                       }
7906                       ans.addAdjacence(json[i], getNode(node), data);
7907                     }
7908                   }
7909                 }
7910             })(ans, json);
7911
7912         return ans;
7913     },
7914
7915     /*
7916      Method: loadJSON
7917     
7918      Loads a JSON structure to the visualization. The JSON structure can be a JSON *tree* or *graph* structure.
7919      
7920       A JSON tree or graph structure consists of nodes, each having as properties
7921        
7922        id - (string) A unique identifier for the node
7923        name - (string) A node's name
7924        data - (object) The data optional property contains a hash (i.e {}) 
7925        where you can store all the information you want about this node.
7926         
7927       For JSON *Tree* structures, there's an extra optional property *children* of type Array which contains the node's children.
7928       
7929       Example:
7930
7931       (start code js)
7932         var json = {  
7933           "id": "aUniqueIdentifier",  
7934           "name": "usually a nodes name",  
7935           "data": {
7936             "some key": "some value",
7937             "some other key": "some other value"
7938            },  
7939           "children": [ *other nodes or empty* ]  
7940         };  
7941       (end code)
7942         
7943         JSON *Graph* structures consist of an array of nodes, each specifying the nodes to which the current node is connected. 
7944         For JSON *Graph* structures, the *children* property is replaced by the *adjacencies* property.
7945         
7946         There are two types of *Graph* structures, *simple* and *extended* graph structures.
7947         
7948         For *simple* Graph structures, the adjacencies property contains an array of strings, each specifying the 
7949         id of the node connected to the main node.
7950         
7951         Example:
7952         
7953         (start code js)
7954         var json = [  
7955           {  
7956             "id": "aUniqueIdentifier",  
7957             "name": "usually a nodes name",  
7958             "data": {
7959               "some key": "some value",
7960               "some other key": "some other value"
7961              },  
7962             "adjacencies": ["anotherUniqueIdentifier", "yetAnotherUniqueIdentifier", 'etc']  
7963           },
7964
7965           'other nodes go here...' 
7966         ];          
7967         (end code)
7968         
7969         For *extended Graph structures*, the adjacencies property contains an array of Adjacency objects that have as properties
7970         
7971         nodeTo - (string) The other node connected by this adjacency.
7972         data - (object) A data property, where we can store custom key/value information.
7973         
7974         Example:
7975         
7976         (start code js)
7977         var json = [  
7978           {  
7979             "id": "aUniqueIdentifier",  
7980             "name": "usually a nodes name",  
7981             "data": {
7982               "some key": "some value",
7983               "some other key": "some other value"
7984              },  
7985             "adjacencies": [  
7986             {  
7987               nodeTo:"aNodeId",  
7988               data: {} //put whatever you want here  
7989             },
7990             'other adjacencies go here...'  
7991           },
7992
7993           'other nodes go here...' 
7994         ];          
7995         (end code)
7996        
7997        About the data property:
7998        
7999        As described before, you can store custom data in the *data* property of JSON *nodes* and *adjacencies*. 
8000        You can use almost any string as key for the data object. Some keys though are reserved by the toolkit, and 
8001        have special meanings. This is the case for keys starting with a dollar sign, for example, *$width*.
8002        
8003        For JSON *node* objects, adding dollar prefixed properties that match the names of the options defined in 
8004        <Options.Node> will override the general value for that option with that particular value. For this to work 
8005        however, you do have to set *overridable = true* in <Options.Node>.
8006        
8007        The same thing is true for JSON adjacencies. Dollar prefixed data properties will alter values set in <Options.Edge> 
8008        if <Options.Edge> has *overridable = true*.
8009        
8010        When loading JSON data into TreeMaps, the *data* property must contain a value for the *$area* key, 
8011        since this is the value which will be taken into account when creating the layout. 
8012        The same thing goes for the *$color* parameter.
8013        
8014        In JSON Nodes you can use also *$label-* prefixed properties to refer to <Options.Label> properties. For example, 
8015        *$label-size* will refer to <Options.Label> size property. Also, in JSON nodes and adjacencies you can set 
8016        canvas specific properties individually by using the *$canvas-* prefix. For example, *$canvas-shadowBlur* will refer 
8017        to the *shadowBlur* property.
8018        
8019        These properties can also be accessed after loading the JSON data from <Graph.Nodes> and <Graph.Adjacences> 
8020        by using <Accessors>. For more information take a look at the <Graph> and <Accessors> documentation.
8021        
8022        Finally, these properties can also be used to create advanced animations like with <Options.NodeStyles>. For more 
8023        information about creating animations please take a look at the <Graph.Plot> and <Graph.Plot.animate> documentation.
8024        
8025        loadJSON Parameters:
8026     
8027         json - A JSON Tree or Graph structure.
8028         i - For Graph structures only. Sets the indexed node as root for the visualization.
8029
8030     */
8031     loadJSON: function(json, i) {
8032       this.json = json;
8033       //if they're canvas labels erase them.
8034       if(this.labels && this.labels.clearLabels) {
8035         this.labels.clearLabels(true);
8036       }
8037       this.graph = this.construct(json);
8038       if($.type(json) != 'array'){
8039         this.root = json.id;
8040       } else {
8041         this.root = json[i? i : 0].id;
8042       }
8043     },
8044     
8045     /*
8046       Method: toJSON
8047    
8048       Returns a JSON tree/graph structure from the visualization's <Graph>. 
8049       See <Loader.loadJSON> for the graph formats available.
8050       
8051       See also:
8052       
8053       <Loader.loadJSON>
8054       
8055       Parameters:
8056       
8057       type - (string) Default's "tree". The type of the JSON structure to be returned. 
8058       Possible options are "tree" or "graph".
8059     */    
8060     toJSON: function(type) {
8061       type = type || "tree";
8062       if(type == 'tree') {
8063         var ans = {};
8064         var rootNode = this.graph.getNode(this.root);
8065         var ans = (function recTree(node) {
8066           var ans = {};
8067           ans.id = node.id;
8068           ans.name = node.name;
8069           ans.data = node.data;
8070           var ch =[];
8071           node.eachSubnode(function(n) {
8072             ch.push(recTree(n));
8073           });
8074           ans.children = ch;
8075           return ans;
8076         })(rootNode);
8077         return ans;
8078       } else {
8079         var ans = [];
8080         var T = !!this.graph.getNode(this.root).visited;
8081         this.graph.eachNode(function(node) {
8082           var ansNode = {};
8083           ansNode.id = node.id;
8084           ansNode.name = node.name;
8085           ansNode.data = node.data;
8086           var adjs = [];
8087           node.eachAdjacency(function(adj) {
8088             var nodeTo = adj.nodeTo;
8089             if(!!nodeTo.visited === T) {
8090               var ansAdj = {};
8091               ansAdj.nodeTo = nodeTo.id;
8092               ansAdj.data = adj.data;
8093               adjs.push(ansAdj);
8094             }
8095           });
8096           ansNode.adjacencies = adjs;
8097           ans.push(ansNode);
8098           node.visited = !T;
8099         });
8100         return ans;
8101       }
8102     }
8103 };
8104
8105
8106
8107 /*
8108  * File: Layouts.js
8109  * 
8110  * Implements base Tree and Graph layouts.
8111  *
8112  * Description:
8113  *
8114  * Implements base Tree and Graph layouts like Radial, Tree, etc.
8115  * 
8116  */
8117
8118 /*
8119  * Object: Layouts
8120  * 
8121  * Parent object for common layouts.
8122  *
8123  */
8124 var Layouts = $jit.Layouts = {};
8125
8126
8127 //Some util shared layout functions are defined here.
8128 var NodeDim = {
8129   label: null,
8130   
8131   compute: function(graph, prop, opt) {
8132     this.initializeLabel(opt);
8133     var label = this.label, style = label.style;
8134     graph.eachNode(function(n) {
8135       var autoWidth  = n.getData('autoWidth'),
8136           autoHeight = n.getData('autoHeight');
8137       if(autoWidth || autoHeight) {
8138         //delete dimensions since these are
8139         //going to be overridden now.
8140         delete n.data.$width;
8141         delete n.data.$height;
8142         delete n.data.$dim;
8143         
8144         var width  = n.getData('width'),
8145             height = n.getData('height');
8146         //reset label dimensions
8147         style.width  = autoWidth? 'auto' : width + 'px';
8148         style.height = autoHeight? 'auto' : height + 'px';
8149         
8150         //TODO(nico) should let the user choose what to insert here.
8151         label.innerHTML = n.name;
8152         
8153         var offsetWidth  = label.offsetWidth,
8154             offsetHeight = label.offsetHeight;
8155         var type = n.getData('type');
8156         if($.indexOf(['circle', 'square', 'triangle', 'star'], type) === -1) {
8157           n.setData('width', offsetWidth);
8158           n.setData('height', offsetHeight);
8159         } else {
8160           var dim = offsetWidth > offsetHeight? offsetWidth : offsetHeight;
8161           n.setData('width', dim);
8162           n.setData('height', dim);
8163           n.setData('dim', dim); 
8164         }
8165       }
8166     });
8167   },
8168   
8169   initializeLabel: function(opt) {
8170     if(!this.label) {
8171       this.label = document.createElement('div');
8172       document.body.appendChild(this.label);
8173     }
8174     this.setLabelStyles(opt);
8175   },
8176   
8177   setLabelStyles: function(opt) {
8178     $.extend(this.label.style, {
8179       'visibility': 'hidden',
8180       'position': 'absolute',
8181       'width': 'auto',
8182       'height': 'auto'
8183     });
8184     this.label.className = 'jit-autoadjust-label';
8185   }
8186 };
8187
8188
8189 /*
8190  * Class: Layouts.Tree
8191  * 
8192  * Implements a Tree Layout.
8193  * 
8194  * Implemented By:
8195  * 
8196  * <ST>
8197  * 
8198  * Inspired by:
8199  * 
8200  * Drawing Trees (Andrew J. Kennedy) <http://research.microsoft.com/en-us/um/people/akenn/fun/drawingtrees.pdf>
8201  * 
8202  */
8203 Layouts.Tree = (function() {
8204   //Layout functions
8205   var slice = Array.prototype.slice;
8206
8207   /*
8208      Calculates the max width and height nodes for a tree level
8209   */  
8210   function getBoundaries(graph, config, level, orn, prop) {
8211     var dim = config.Node;
8212     var multitree = config.multitree;
8213     if (dim.overridable) {
8214       var w = -1, h = -1;
8215       graph.eachNode(function(n) {
8216         if (n._depth == level
8217             && (!multitree || ('$orn' in n.data) && n.data.$orn == orn)) {
8218           var dw = n.getData('width', prop);
8219           var dh = n.getData('height', prop);
8220           w = (w < dw) ? dw : w;
8221           h = (h < dh) ? dh : h;
8222         }
8223       });
8224       return {
8225         'width' : w < 0 ? dim.width : w,
8226         'height' : h < 0 ? dim.height : h
8227       };
8228     } else {
8229       return dim;
8230     }
8231   }
8232
8233
8234   function movetree(node, prop, val, orn) {
8235     var p = (orn == "left" || orn == "right") ? "y" : "x";
8236     node.getPos(prop)[p] += val;
8237   }
8238
8239
8240   function moveextent(extent, val) {
8241     var ans = [];
8242     $.each(extent, function(elem) {
8243       elem = slice.call(elem);
8244       elem[0] += val;
8245       elem[1] += val;
8246       ans.push(elem);
8247     });
8248     return ans;
8249   }
8250
8251
8252   function merge(ps, qs) {
8253     if (ps.length == 0)
8254       return qs;
8255     if (qs.length == 0)
8256       return ps;
8257     var p = ps.shift(), q = qs.shift();
8258     return [ [ p[0], q[1] ] ].concat(merge(ps, qs));
8259   }
8260
8261
8262   function mergelist(ls, def) {
8263     def = def || [];
8264     if (ls.length == 0)
8265       return def;
8266     var ps = ls.pop();
8267     return mergelist(ls, merge(ps, def));
8268   }
8269
8270
8271   function fit(ext1, ext2, subtreeOffset, siblingOffset, i) {
8272     if (ext1.length <= i || ext2.length <= i)
8273       return 0;
8274
8275     var p = ext1[i][1], q = ext2[i][0];
8276     return Math.max(fit(ext1, ext2, subtreeOffset, siblingOffset, ++i)
8277         + subtreeOffset, p - q + siblingOffset);
8278   }
8279
8280
8281   function fitlistl(es, subtreeOffset, siblingOffset) {
8282     function $fitlistl(acc, es, i) {
8283       if (es.length <= i)
8284         return [];
8285       var e = es[i], ans = fit(acc, e, subtreeOffset, siblingOffset, 0);
8286       return [ ans ].concat($fitlistl(merge(acc, moveextent(e, ans)), es, ++i));
8287     }
8288     ;
8289     return $fitlistl( [], es, 0);
8290   }
8291
8292
8293   function fitlistr(es, subtreeOffset, siblingOffset) {
8294     function $fitlistr(acc, es, i) {
8295       if (es.length <= i)
8296         return [];
8297       var e = es[i], ans = -fit(e, acc, subtreeOffset, siblingOffset, 0);
8298       return [ ans ].concat($fitlistr(merge(moveextent(e, ans), acc), es, ++i));
8299     }
8300     ;
8301     es = slice.call(es);
8302     var ans = $fitlistr( [], es.reverse(), 0);
8303     return ans.reverse();
8304   }
8305
8306
8307   function fitlist(es, subtreeOffset, siblingOffset, align) {
8308     var esl = fitlistl(es, subtreeOffset, siblingOffset), esr = fitlistr(es,
8309         subtreeOffset, siblingOffset);
8310
8311     if (align == "left")
8312       esr = esl;
8313     else if (align == "right")
8314       esl = esr;
8315
8316     for ( var i = 0, ans = []; i < esl.length; i++) {
8317       ans[i] = (esl[i] + esr[i]) / 2;
8318     }
8319     return ans;
8320   }
8321
8322
8323   function design(graph, node, prop, config, orn) {
8324     var multitree = config.multitree;
8325     var auxp = [ 'x', 'y' ], auxs = [ 'width', 'height' ];
8326     var ind = +(orn == "left" || orn == "right");
8327     var p = auxp[ind], notp = auxp[1 - ind];
8328
8329     var cnode = config.Node;
8330     var s = auxs[ind], nots = auxs[1 - ind];
8331
8332     var siblingOffset = config.siblingOffset;
8333     var subtreeOffset = config.subtreeOffset;
8334     var align = config.align;
8335
8336     function $design(node, maxsize, acum) {
8337       var sval = node.getData(s, prop);
8338       var notsval = maxsize
8339           || (node.getData(nots, prop));
8340
8341       var trees = [], extents = [], chmaxsize = false;
8342       var chacum = notsval + config.levelDistance;
8343       node.eachSubnode(function(n) {
8344             if (n.exist
8345                 && (!multitree || ('$orn' in n.data) && n.data.$orn == orn)) {
8346
8347               if (!chmaxsize)
8348                 chmaxsize = getBoundaries(graph, config, n._depth, orn, prop);
8349
8350               var s = $design(n, chmaxsize[nots], acum + chacum);
8351               trees.push(s.tree);
8352               extents.push(s.extent);
8353             }
8354           });
8355       var positions = fitlist(extents, subtreeOffset, siblingOffset, align);
8356       for ( var i = 0, ptrees = [], pextents = []; i < trees.length; i++) {
8357         movetree(trees[i], prop, positions[i], orn);
8358         pextents.push(moveextent(extents[i], positions[i]));
8359       }
8360       var resultextent = [ [ -sval / 2, sval / 2 ] ]
8361           .concat(mergelist(pextents));
8362       node.getPos(prop)[p] = 0;
8363
8364       if (orn == "top" || orn == "left") {
8365         node.getPos(prop)[notp] = acum;
8366       } else {
8367         node.getPos(prop)[notp] = -acum;
8368       }
8369
8370       return {
8371         tree : node,
8372         extent : resultextent
8373       };
8374     }
8375
8376     $design(node, false, 0);
8377   }
8378
8379
8380   return new Class({
8381     /*
8382     Method: compute
8383     
8384     Computes nodes' positions.
8385
8386      */
8387     compute : function(property, computeLevels) {
8388       var prop = property || 'start';
8389       var node = this.graph.getNode(this.root);
8390       $.extend(node, {
8391         'drawn' : true,
8392         'exist' : true,
8393         'selected' : true
8394       });
8395       NodeDim.compute(this.graph, prop, this.config);
8396       if (!!computeLevels || !("_depth" in node)) {
8397         this.graph.computeLevels(this.root, 0, "ignore");
8398       }
8399       
8400       this.computePositions(node, prop);
8401     },
8402
8403     computePositions : function(node, prop) {
8404       var config = this.config;
8405       var multitree = config.multitree;
8406       var align = config.align;
8407       var indent = align !== 'center' && config.indent;
8408       var orn = config.orientation;
8409       var orns = multitree ? [ 'top', 'right', 'bottom', 'left' ] : [ orn ];
8410       var that = this;
8411       $.each(orns, function(orn) {
8412         //calculate layout
8413           design(that.graph, node, prop, that.config, orn, prop);
8414           var i = [ 'x', 'y' ][+(orn == "left" || orn == "right")];
8415           //absolutize
8416           (function red(node) {
8417             node.eachSubnode(function(n) {
8418               if (n.exist
8419                   && (!multitree || ('$orn' in n.data) && n.data.$orn == orn)) {
8420
8421                 n.getPos(prop)[i] += node.getPos(prop)[i];
8422                 if (indent) {
8423                   n.getPos(prop)[i] += align == 'left' ? indent : -indent;
8424                 }
8425                 red(n);
8426               }
8427             });
8428           })(node);
8429         });
8430     }
8431   });
8432   
8433 })();
8434
8435 /*
8436  * File: Spacetree.js
8437  */
8438
8439 /*
8440    Class: ST
8441    
8442   A Tree layout with advanced contraction and expansion animations.
8443      
8444   Inspired by:
8445  
8446   SpaceTree: Supporting Exploration in Large Node Link Tree, Design Evolution and Empirical Evaluation (Catherine Plaisant, Jesse Grosjean, Benjamin B. Bederson) 
8447   <http://hcil.cs.umd.edu/trs/2002-05/2002-05.pdf>
8448   
8449   Drawing Trees (Andrew J. Kennedy) <http://research.microsoft.com/en-us/um/people/akenn/fun/drawingtrees.pdf>
8450   
8451   Note:
8452  
8453   This visualization was built and engineered from scratch, taking only the papers as inspiration, and only shares some features with the visualization described in those papers.
8454  
8455   Implements:
8456   
8457   All <Loader> methods
8458   
8459   Constructor Options:
8460   
8461   Inherits options from
8462   
8463   - <Options.Canvas>
8464   - <Options.Controller>
8465   - <Options.Tree>
8466   - <Options.Node>
8467   - <Options.Edge>
8468   - <Options.Label>
8469   - <Options.Events>
8470   - <Options.Tips>
8471   - <Options.NodeStyles>
8472   - <Options.Navigation>
8473   
8474   Additionally, there are other parameters and some default values changed
8475   
8476   constrained - (boolean) Default's *true*. Whether to show the entire tree when loaded or just the number of levels specified by _levelsToShow_.
8477   levelsToShow - (number) Default's *2*. The number of levels to show for a subtree. This number is relative to the selected node.
8478   levelDistance - (number) Default's *30*. The distance between two consecutive levels of the tree.
8479   Node.type - Described in <Options.Node>. Default's set to *rectangle*.
8480   offsetX - (number) Default's *0*. The x-offset distance from the selected node to the center of the canvas.
8481   offsetY - (number) Default's *0*. The y-offset distance from the selected node to the center of the canvas.
8482   duration - Described in <Options.Fx>. It's default value has been changed to *700*.
8483   
8484   Instance Properties:
8485   
8486   canvas - Access a <Canvas> instance.
8487   graph - Access a <Graph> instance.
8488   op - Access a <ST.Op> instance.
8489   fx - Access a <ST.Plot> instance.
8490   labels - Access a <ST.Label> interface implementation.
8491
8492  */
8493
8494 $jit.ST= (function() {
8495     // Define some private methods first...
8496     // Nodes in path
8497     var nodesInPath = [];
8498     // Nodes to contract
8499     function getNodesToHide(node) {
8500       node = node || this.clickedNode;
8501       if(!this.config.constrained) {
8502         return [];
8503       }
8504       var Geom = this.geom;
8505       var graph = this.graph;
8506       var canvas = this.canvas;
8507       var level = node._depth, nodeArray = [];
8508           graph.eachNode(function(n) {
8509           if(n.exist && !n.selected) {
8510               if(n.isDescendantOf(node.id)) {
8511                 if(n._depth <= level) nodeArray.push(n);
8512               } else {
8513                 nodeArray.push(n);
8514               }
8515           }
8516           });
8517           var leafLevel = Geom.getRightLevelToShow(node, canvas);
8518           node.eachLevel(leafLevel, leafLevel, function(n) {
8519           if(n.exist && !n.selected) nodeArray.push(n);
8520           });
8521             
8522           for (var i = 0; i < nodesInPath.length; i++) {
8523             var n = this.graph.getNode(nodesInPath[i]);
8524             if(!n.isDescendantOf(node.id)) {
8525               nodeArray.push(n);
8526             }
8527           } 
8528           return nodeArray;       
8529     };
8530     // Nodes to expand
8531      function getNodesToShow(node) {
8532         var nodeArray = [], config = this.config;
8533         node = node || this.clickedNode;
8534         this.clickedNode.eachLevel(0, config.levelsToShow, function(n) {
8535             if(config.multitree && !('$orn' in n.data) 
8536                         && n.anySubnode(function(ch){ return ch.exist && !ch.drawn; })) {
8537                 nodeArray.push(n);
8538             } else if(n.drawn && !n.anySubnode("drawn")) {
8539               nodeArray.push(n);
8540             }
8541         });
8542         return nodeArray;
8543      };
8544     // Now define the actual class.
8545     return new Class({
8546     
8547         Implements: [Loader, Extras, Layouts.Tree],
8548         
8549         initialize: function(controller) {            
8550           var $ST = $jit.ST;
8551           
8552           var config= {
8553                 levelsToShow: 2,
8554                 levelDistance: 30,
8555                 constrained: true,                
8556                 Node: {
8557                   type: 'rectangle'
8558                 },
8559                 duration: 700,
8560                 offsetX: 0,
8561                 offsetY: 0
8562             };
8563             
8564             this.controller = this.config = $.merge(
8565                 Options("Canvas", "Fx", "Tree", "Node", "Edge", "Controller", 
8566                     "Tips", "NodeStyles", "Events", "Navigation", "Label"), config, controller);
8567
8568             var canvasConfig = this.config;
8569             if(canvasConfig.useCanvas) {
8570               this.canvas = canvasConfig.useCanvas;
8571               this.config.labelContainer = this.canvas.id + '-label';
8572             } else {
8573               if(canvasConfig.background) {
8574                 canvasConfig.background = $.merge({
8575                   type: 'Circles'
8576                 }, canvasConfig.background);
8577               }
8578               this.canvas = new Canvas(this, canvasConfig);
8579               this.config.labelContainer = (typeof canvasConfig.injectInto == 'string'? canvasConfig.injectInto : canvasConfig.injectInto.id) + '-label';
8580             }
8581
8582             this.graphOptions = {
8583                 'klass': Complex
8584             };
8585             this.graph = new Graph(this.graphOptions, this.config.Node, this.config.Edge);
8586             this.labels = new $ST.Label[canvasConfig.Label.type](this);
8587             this.fx = new $ST.Plot(this, $ST);
8588             this.op = new $ST.Op(this);
8589             this.group = new $ST.Group(this);
8590             this.geom = new $ST.Geom(this);
8591             this.clickedNode=  null;
8592             // initialize extras
8593             this.initializeExtras();
8594         },
8595     
8596         /*
8597          Method: plot
8598         
8599          Plots the <ST>. This is a shortcut to *fx.plot*.
8600
8601         */  
8602         plot: function() { this.fx.plot(this.controller); },
8603     
8604       
8605         /*
8606          Method: switchPosition
8607         
8608          Switches the tree orientation.
8609
8610          Parameters:
8611
8612         pos - (string) The new tree orientation. Possible values are "top", "left", "right" and "bottom".
8613         method - (string) Set this to "animate" if you want to animate the tree when switching its position. You can also set this parameter to "replot" to just replot the subtree.
8614         onComplete - (optional|object) This callback is called once the "switching" animation is complete.
8615
8616          Example:
8617
8618          (start code js)
8619            st.switchPosition("right", "animate", {
8620             onComplete: function() {
8621               alert('completed!');
8622             } 
8623            });
8624          (end code)
8625         */  
8626         switchPosition: function(pos, method, onComplete) {
8627           var Geom = this.geom, Plot = this.fx, that = this;
8628           if(!Plot.busy) {
8629               Plot.busy = true;
8630               this.contract({
8631                   onComplete: function() {
8632                       Geom.switchOrientation(pos);
8633                       that.compute('end', false);
8634                       Plot.busy = false;
8635                       if(method == 'animate') {
8636                           that.onClick(that.clickedNode.id, onComplete);  
8637                       } else if(method == 'replot') {
8638                           that.select(that.clickedNode.id, onComplete);
8639                       }
8640                   }
8641               }, pos);
8642           }
8643         },
8644
8645         /*
8646         Method: switchAlignment
8647        
8648         Switches the tree alignment.
8649
8650         Parameters:
8651
8652        align - (string) The new tree alignment. Possible values are "left", "center" and "right".
8653        method - (string) Set this to "animate" if you want to animate the tree after aligning its position. You can also set this parameter to "replot" to just replot the subtree.
8654        onComplete - (optional|object) This callback is called once the "switching" animation is complete.
8655
8656         Example:
8657
8658         (start code js)
8659           st.switchAlignment("right", "animate", {
8660            onComplete: function() {
8661              alert('completed!');
8662            } 
8663           });
8664         (end code)
8665        */  
8666        switchAlignment: function(align, method, onComplete) {
8667         this.config.align = align;
8668         if(method == 'animate') {
8669                 this.select(this.clickedNode.id, onComplete);
8670         } else if(method == 'replot') {
8671                 this.onClick(this.clickedNode.id, onComplete);  
8672         }
8673        },
8674
8675        /*
8676         Method: addNodeInPath
8677        
8678         Adds a node to the current path as selected node. The selected node will be visible (as in non-collapsed) at all times.
8679         
8680
8681         Parameters:
8682
8683        id - (string) A <Graph.Node> id.
8684
8685         Example:
8686
8687         (start code js)
8688           st.addNodeInPath("nodeId");
8689         (end code)
8690        */  
8691        addNodeInPath: function(id) {
8692            nodesInPath.push(id);
8693            this.select((this.clickedNode && this.clickedNode.id) || this.root);
8694        },       
8695
8696        /*
8697        Method: clearNodesInPath
8698       
8699        Removes all nodes tagged as selected by the <ST.addNodeInPath> method.
8700        
8701        See also:
8702        
8703        <ST.addNodeInPath>
8704      
8705        Example:
8706
8707        (start code js)
8708          st.clearNodesInPath();
8709        (end code)
8710       */  
8711        clearNodesInPath: function(id) {
8712            nodesInPath.length = 0;
8713            this.select((this.clickedNode && this.clickedNode.id) || this.root);
8714        },
8715         
8716        /*
8717          Method: refresh
8718         
8719          Computes positions and plots the tree.
8720          
8721        */
8722        refresh: function() {
8723            this.reposition();
8724            this.select((this.clickedNode && this.clickedNode.id) || this.root);
8725        },    
8726
8727        reposition: function() {
8728             this.graph.computeLevels(this.root, 0, "ignore");
8729              this.geom.setRightLevelToShow(this.clickedNode, this.canvas);
8730             this.graph.eachNode(function(n) {
8731                 if(n.exist) n.drawn = true;
8732             });
8733             this.compute('end');
8734         },
8735         
8736         requestNodes: function(node, onComplete) {
8737           var handler = $.merge(this.controller, onComplete), 
8738           lev = this.config.levelsToShow;
8739           if(handler.request) {
8740               var leaves = [], d = node._depth;
8741               node.eachLevel(0, lev, function(n) {
8742                   if(n.drawn && 
8743                    !n.anySubnode()) {
8744                    leaves.push(n);
8745                    n._level = lev - (n._depth - d);
8746                   }
8747               });
8748               this.group.requestNodes(leaves, handler);
8749           }
8750             else
8751               handler.onComplete();
8752         },
8753      
8754         contract: function(onComplete, switched) {
8755           var orn  = this.config.orientation;
8756           var Geom = this.geom, Group = this.group;
8757           if(switched) Geom.switchOrientation(switched);
8758           var nodes = getNodesToHide.call(this);
8759           if(switched) Geom.switchOrientation(orn);
8760           Group.contract(nodes, $.merge(this.controller, onComplete));
8761         },
8762       
8763          move: function(node, onComplete) {
8764             this.compute('end', false);
8765             var move = onComplete.Move, offset = {
8766                 'x': move.offsetX,
8767                 'y': move.offsetY 
8768             };
8769             if(move.enable) {
8770                 this.geom.translate(node.endPos.add(offset).$scale(-1), "end");
8771             }
8772             this.fx.animate($.merge(this.controller, { modes: ['linear'] }, onComplete));
8773          },
8774       
8775         expand: function (node, onComplete) {
8776             var nodeArray = getNodesToShow.call(this, node);
8777             this.group.expand(nodeArray, $.merge(this.controller, onComplete));
8778         },
8779     
8780         selectPath: function(node) {
8781           var that = this;
8782           this.graph.eachNode(function(n) { n.selected = false; }); 
8783           function path(node) {
8784               if(node == null || node.selected) return;
8785               node.selected = true;
8786               $.each(that.group.getSiblings([node])[node.id], 
8787               function(n) { 
8788                    n.exist = true; 
8789                    n.drawn = true; 
8790               });    
8791               var parents = node.getParents();
8792               parents = (parents.length > 0)? parents[0] : null;
8793               path(parents);
8794           };
8795           for(var i=0, ns = [node.id].concat(nodesInPath); i < ns.length; i++) {
8796               path(this.graph.getNode(ns[i]));
8797           }
8798         },
8799       
8800         /*
8801         Method: setRoot
8802      
8803          Switches the current root node. Changes the topology of the Tree.
8804      
8805         Parameters:
8806            id - (string) The id of the node to be set as root.
8807            method - (string) Set this to "animate" if you want to animate the tree after adding the subtree. You can also set this parameter to "replot" to just replot the subtree.
8808            onComplete - (optional|object) An action to perform after the animation (if any).
8809  
8810         Example:
8811
8812         (start code js)
8813           st.setRoot('nodeId', 'animate', {
8814              onComplete: function() {
8815                alert('complete!');
8816              }
8817           });
8818         (end code)
8819      */
8820      setRoot: function(id, method, onComplete) {
8821                 if(this.busy) return;
8822                 this.busy = true;
8823           var that = this, canvas = this.canvas;
8824                 var rootNode = this.graph.getNode(this.root);
8825                 var clickedNode = this.graph.getNode(id);
8826                 function $setRoot() {
8827                 if(this.config.multitree && clickedNode.data.$orn) {
8828                         var orn = clickedNode.data.$orn;
8829                         var opp = {
8830                                         'left': 'right',
8831                                         'right': 'left',
8832                                         'top': 'bottom',
8833                                         'bottom': 'top'
8834                         }[orn];
8835                         rootNode.data.$orn = opp;
8836                         (function tag(rootNode) {
8837                                 rootNode.eachSubnode(function(n) {
8838                                         if(n.id != id) {
8839                                                 n.data.$orn = opp;
8840                                                 tag(n);
8841                                         }
8842                                 });
8843                         })(rootNode);
8844                         delete clickedNode.data.$orn;
8845                 }
8846                 this.root = id;
8847                 this.clickedNode = clickedNode;
8848                 this.graph.computeLevels(this.root, 0, "ignore");
8849                 this.geom.setRightLevelToShow(clickedNode, canvas, {
8850                   execHide: false,
8851                   onShow: function(node) {
8852                     if(!node.drawn) {
8853                     node.drawn = true;
8854                     node.setData('alpha', 1, 'end');
8855                     node.setData('alpha', 0);
8856                     node.pos.setc(clickedNode.pos.x, clickedNode.pos.y);
8857                     }
8858                   }
8859                 });
8860               this.compute('end');
8861               this.busy = true;
8862               this.fx.animate({
8863                 modes: ['linear', 'node-property:alpha'],
8864                 onComplete: function() {
8865                   that.busy = false;
8866                   that.onClick(id, {
8867                     onComplete: function() {
8868                       onComplete && onComplete.onComplete();
8869                     }
8870                   });
8871                 }
8872               });
8873                 }
8874
8875                 // delete previous orientations (if any)
8876                 delete rootNode.data.$orns;
8877
8878                 if(method == 'animate') {
8879                   $setRoot.call(this);
8880                   that.selectPath(clickedNode);
8881                 } else if(method == 'replot') {
8882                         $setRoot.call(this);
8883                         this.select(this.root);
8884                 }
8885      },
8886
8887      /*
8888            Method: addSubtree
8889         
8890             Adds a subtree.
8891         
8892            Parameters:
8893               subtree - (object) A JSON Tree object. See also <Loader.loadJSON>.
8894               method - (string) Set this to "animate" if you want to animate the tree after adding the subtree. You can also set this parameter to "replot" to just replot the subtree.
8895               onComplete - (optional|object) An action to perform after the animation (if any).
8896     
8897            Example:
8898
8899            (start code js)
8900              st.addSubtree(json, 'animate', {
8901                 onComplete: function() {
8902                   alert('complete!');
8903                 }
8904              });
8905            (end code)
8906         */
8907         addSubtree: function(subtree, method, onComplete) {
8908             if(method == 'replot') {
8909                 this.op.sum(subtree, $.extend({ type: 'replot' }, onComplete || {}));
8910             } else if (method == 'animate') {
8911                 this.op.sum(subtree, $.extend({ type: 'fade:seq' }, onComplete || {}));
8912             }
8913         },
8914     
8915         /*
8916            Method: removeSubtree
8917         
8918             Removes a subtree.
8919         
8920            Parameters:
8921               id - (string) The _id_ of the subtree to be removed.
8922               removeRoot - (boolean) Default's *false*. Remove the root of the subtree or only its subnodes.
8923               method - (string) Set this to "animate" if you want to animate the tree after removing the subtree. You can also set this parameter to "replot" to just replot the subtree.
8924               onComplete - (optional|object) An action to perform after the animation (if any).
8925
8926           Example:
8927
8928           (start code js)
8929             st.removeSubtree('idOfSubtreeToBeRemoved', false, 'animate', {
8930               onComplete: function() {
8931                 alert('complete!');
8932               }
8933             });
8934           (end code)
8935     
8936         */
8937         removeSubtree: function(id, removeRoot, method, onComplete) {
8938             var node = this.graph.getNode(id), subids = [];
8939             node.eachLevel(+!removeRoot, false, function(n) {
8940                 subids.push(n.id);
8941             });
8942             if(method == 'replot') {
8943                 this.op.removeNode(subids, $.extend({ type: 'replot' }, onComplete || {}));
8944             } else if (method == 'animate') {
8945                 this.op.removeNode(subids, $.extend({ type: 'fade:seq'}, onComplete || {}));
8946             }
8947         },
8948     
8949         /*
8950            Method: select
8951         
8952             Selects a node in the <ST> without performing an animation. Useful when selecting 
8953             nodes which are currently hidden or deep inside the tree.
8954
8955           Parameters:
8956             id - (string) The id of the node to select.
8957             onComplete - (optional|object) an onComplete callback.
8958
8959           Example:
8960           (start code js)
8961             st.select('mynodeid', {
8962               onComplete: function() {
8963                 alert('complete!');
8964               }
8965             });
8966           (end code)
8967         */
8968         select: function(id, onComplete) {
8969             var group = this.group, geom = this.geom;
8970             var node=  this.graph.getNode(id), canvas = this.canvas;
8971             var root  = this.graph.getNode(this.root);
8972             var complete = $.merge(this.controller, onComplete);
8973             var that = this;
8974     
8975             complete.onBeforeCompute(node);
8976             this.selectPath(node);
8977             this.clickedNode= node;
8978             this.requestNodes(node, {
8979                 onComplete: function(){
8980                     group.hide(group.prepare(getNodesToHide.call(that)), complete);
8981                     geom.setRightLevelToShow(node, canvas);
8982                     that.compute("current");
8983                     that.graph.eachNode(function(n) { 
8984                         var pos = n.pos.getc(true);
8985                         n.startPos.setc(pos.x, pos.y);
8986                         n.endPos.setc(pos.x, pos.y);
8987                         n.visited = false; 
8988                     });
8989                     var offset = { x: complete.offsetX, y: complete.offsetY };
8990                     that.geom.translate(node.endPos.add(offset).$scale(-1), ["start", "current", "end"]);
8991                     group.show(getNodesToShow.call(that));              
8992                     that.plot();
8993                     complete.onAfterCompute(that.clickedNode);
8994                     complete.onComplete();
8995                 }
8996             });     
8997         },
8998     
8999       /*
9000          Method: onClick
9001     
9002         Animates the <ST> to center the node specified by *id*.
9003             
9004         Parameters:
9005         
9006         id - (string) A node id.
9007         options - (optional|object) A group of options and callbacks described below.
9008         onComplete - (object) An object callback called when the animation finishes.
9009         Move - (object) An object that has as properties _offsetX_ or _offsetY_ for adding some offset position to the centered node.
9010
9011         Example:
9012
9013         (start code js)
9014           st.onClick('mynodeid', {
9015                   Move: {
9016                         enable: true,
9017                     offsetX: 30,
9018                     offsetY: 5
9019                   },
9020                   onComplete: function() {
9021                       alert('yay!');
9022                   }
9023           });
9024         (end code)
9025     
9026         */    
9027       onClick: function (id, options) {
9028         var canvas = this.canvas, that = this, Geom = this.geom, config = this.config;
9029         var innerController = {
9030             Move: {
9031                     enable: true,
9032               offsetX: config.offsetX || 0,
9033               offsetY: config.offsetY || 0  
9034             },
9035             setRightLevelToShowConfig: false,
9036             onBeforeRequest: $.empty,
9037             onBeforeContract: $.empty,
9038             onBeforeMove: $.empty,
9039             onBeforeExpand: $.empty
9040         };
9041         var complete = $.merge(this.controller, innerController, options);
9042         
9043         if(!this.busy) {
9044             this.busy = true;
9045             var node = this.graph.getNode(id);
9046             this.selectPath(node, this.clickedNode);
9047                 this.clickedNode = node;
9048             complete.onBeforeCompute(node);
9049             complete.onBeforeRequest(node);
9050             this.requestNodes(node, {
9051                 onComplete: function() {
9052                     complete.onBeforeContract(node);
9053                     that.contract({
9054                         onComplete: function() {
9055                             Geom.setRightLevelToShow(node, canvas, complete.setRightLevelToShowConfig);
9056                             complete.onBeforeMove(node);
9057                             that.move(node, {
9058                                 Move: complete.Move,
9059                                 onComplete: function() {
9060                                     complete.onBeforeExpand(node);
9061                                     that.expand(node, {
9062                                         onComplete: function() {
9063                                             that.busy = false;
9064                                             complete.onAfterCompute(id);
9065                                             complete.onComplete();
9066                                         }
9067                                     }); // expand
9068                                 }
9069                             }); // move
9070                         }
9071                     });// contract
9072                 }
9073             });// request
9074         }
9075       }
9076     });
9077
9078 })();
9079
9080 $jit.ST.$extend = true;
9081
9082 /*
9083    Class: ST.Op
9084     
9085    Custom extension of <Graph.Op>.
9086
9087    Extends:
9088
9089    All <Graph.Op> methods
9090    
9091    See also:
9092    
9093    <Graph.Op>
9094
9095 */
9096 $jit.ST.Op = new Class({
9097
9098   Implements: Graph.Op
9099     
9100 });
9101
9102 /*
9103     
9104      Performs operations on group of nodes.
9105
9106 */
9107 $jit.ST.Group = new Class({
9108     
9109     initialize: function(viz) {
9110         this.viz = viz;
9111         this.canvas = viz.canvas;
9112         this.config = viz.config;
9113         this.animation = new Animation;
9114         this.nodes = null;
9115     },
9116     
9117     /*
9118     
9119        Calls the request method on the controller to request a subtree for each node. 
9120     */
9121     requestNodes: function(nodes, controller) {
9122         var counter = 0, len = nodes.length, nodeSelected = {};
9123         var complete = function() { controller.onComplete(); };
9124         var viz = this.viz;
9125         if(len == 0) complete();
9126         for(var i=0; i<len; i++) {
9127             nodeSelected[nodes[i].id] = nodes[i];
9128             controller.request(nodes[i].id, nodes[i]._level, {
9129                 onComplete: function(nodeId, data) {
9130                     if(data && data.children) {
9131                         data.id = nodeId;
9132                         viz.op.sum(data, { type: 'nothing' });
9133                     }
9134                     if(++counter == len) {
9135                         viz.graph.computeLevels(viz.root, 0);
9136                         complete();
9137                     }
9138                 }
9139             });
9140         }
9141     },
9142     
9143     /*
9144     
9145        Collapses group of nodes. 
9146     */
9147     contract: function(nodes, controller) {
9148         var viz = this.viz;
9149         var that = this;
9150
9151         nodes = this.prepare(nodes);
9152         this.animation.setOptions($.merge(controller, {
9153             $animating: false,
9154             compute: function(delta) {
9155               if(delta == 1) delta = 0.99;
9156               that.plotStep(1 - delta, controller, this.$animating);
9157               this.$animating = 'contract';
9158             },
9159             
9160             complete: function() {
9161                 that.hide(nodes, controller);
9162             }       
9163         })).start();
9164     },
9165     
9166     hide: function(nodes, controller) {
9167         var viz = this.viz;
9168         for(var i=0; i<nodes.length; i++) {
9169             // TODO nodes are requested on demand, but not
9170             // deleted when hidden. Would that be a good feature?
9171             // Currently that feature is buggy, so I'll turn it off
9172             // Actually this feature is buggy because trimming should take
9173             // place onAfterCompute and not right after collapsing nodes.
9174             if (true || !controller || !controller.request) {
9175                 nodes[i].eachLevel(1, false, function(elem){
9176                     if (elem.exist) {
9177                         $.extend(elem, {
9178                             'drawn': false,
9179                             'exist': false
9180                         });
9181                     }
9182                 });
9183             } else {
9184                 var ids = [];
9185                 nodes[i].eachLevel(1, false, function(n) {
9186                     ids.push(n.id);
9187                 });
9188                 viz.op.removeNode(ids, { 'type': 'nothing' });
9189                 viz.labels.clearLabels();
9190             }
9191         }
9192         controller.onComplete();
9193     },    
9194     
9195
9196     /*
9197        Expands group of nodes. 
9198     */
9199     expand: function(nodes, controller) {
9200         var that = this;
9201         this.show(nodes);
9202         this.animation.setOptions($.merge(controller, {
9203             $animating: false,
9204             compute: function(delta) {
9205                 that.plotStep(delta, controller, this.$animating);
9206                 this.$animating = 'expand';
9207             },
9208             
9209             complete: function() {
9210                 that.plotStep(undefined, controller, false);
9211                 controller.onComplete();
9212             }       
9213         })).start();
9214         
9215     },
9216     
9217     show: function(nodes) {
9218         var config = this.config;
9219         this.prepare(nodes);
9220         $.each(nodes, function(n) {
9221                 // check for root nodes if multitree
9222                 if(config.multitree && !('$orn' in n.data)) {
9223                         delete n.data.$orns;
9224                         var orns = ' ';
9225                         n.eachSubnode(function(ch) {
9226                                 if(('$orn' in ch.data) 
9227                                                 && orns.indexOf(ch.data.$orn) < 0 
9228                                                 && ch.exist && !ch.drawn) {
9229                                         orns += ch.data.$orn + ' ';
9230                                 }
9231                         });
9232                         n.data.$orns = orns;
9233                 }
9234             n.eachLevel(0, config.levelsToShow, function(n) {
9235                 if(n.exist) n.drawn = true;
9236             });     
9237         });
9238     },
9239     
9240     prepare: function(nodes) {
9241         this.nodes = this.getNodesWithChildren(nodes);
9242         return this.nodes;
9243     },
9244     
9245     /*
9246        Filters an array of nodes leaving only nodes with children.
9247     */
9248     getNodesWithChildren: function(nodes) {
9249         var ans = [], config = this.config, root = this.viz.root;
9250         nodes.sort(function(a, b) { return (a._depth <= b._depth) - (a._depth >= b._depth); });
9251         for(var i=0; i<nodes.length; i++) {
9252             if(nodes[i].anySubnode("exist")) {
9253                 for (var j = i+1, desc = false; !desc && j < nodes.length; j++) {
9254                     if(!config.multitree || '$orn' in nodes[j].data) {
9255                                 desc = desc || nodes[i].isDescendantOf(nodes[j].id);                            
9256                     }
9257                 }
9258                 if(!desc) ans.push(nodes[i]);
9259             }
9260         }
9261         return ans;
9262     },
9263     
9264     plotStep: function(delta, controller, animating) {
9265         var viz = this.viz,
9266         config = this.config,
9267         canvas = viz.canvas, 
9268         ctx = canvas.getCtx(),
9269         nodes = this.nodes;
9270         var i, node;
9271         // hide nodes that are meant to be collapsed/expanded
9272         var nds = {};
9273         for(i=0; i<nodes.length; i++) {
9274           node = nodes[i];
9275           nds[node.id] = [];
9276           var root = config.multitree && !('$orn' in node.data);
9277           var orns = root && node.data.$orns;
9278           node.eachSubgraph(function(n) { 
9279             // TODO(nico): Cleanup
9280                   // special check for root node subnodes when
9281                   // multitree is checked.
9282                   if(root && orns && orns.indexOf(n.data.$orn) > 0 
9283                                   && n.drawn) {
9284                           n.drawn = false;
9285                   nds[node.id].push(n);
9286               } else if((!root || !orns) && n.drawn) {
9287                 n.drawn = false;
9288                 nds[node.id].push(n);
9289               }
9290             }); 
9291             node.drawn = true;
9292         }
9293         // plot the whole (non-scaled) tree
9294         if(nodes.length > 0) viz.fx.plot();
9295         // show nodes that were previously hidden
9296         for(i in nds) {
9297           $.each(nds[i], function(n) { n.drawn = true; });
9298         }
9299         // plot each scaled subtree
9300         for(i=0; i<nodes.length; i++) {
9301           node = nodes[i];
9302           ctx.save();
9303           viz.fx.plotSubtree(node, controller, delta, animating);                
9304           ctx.restore();
9305         }
9306       },
9307
9308       getSiblings: function(nodes) {
9309         var siblings = {};
9310         $.each(nodes, function(n) {
9311             var par = n.getParents();
9312             if (par.length == 0) {
9313                 siblings[n.id] = [n];
9314             } else {
9315                 var ans = [];
9316                 par[0].eachSubnode(function(sn) {
9317                     ans.push(sn);
9318                 });
9319                 siblings[n.id] = ans;
9320             }
9321         });
9322         return siblings;
9323     }
9324 });
9325
9326 /*
9327    ST.Geom
9328
9329    Performs low level geometrical computations.
9330
9331    Access:
9332
9333    This instance can be accessed with the _geom_ parameter of the st instance created.
9334
9335    Example:
9336
9337    (start code js)
9338     var st = new ST(canvas, config);
9339     st.geom.translate //or can also call any other <ST.Geom> method
9340    (end code)
9341
9342 */
9343
9344 $jit.ST.Geom = new Class({
9345     Implements: Graph.Geom,
9346     /*
9347        Changes the tree current orientation to the one specified.
9348
9349        You should usually use <ST.switchPosition> instead.
9350     */  
9351     switchOrientation: function(orn) {
9352         this.config.orientation = orn;
9353     },
9354
9355     /*
9356        Makes a value dispatch according to the current layout
9357        Works like a CSS property, either _top-right-bottom-left_ or _top|bottom - left|right_.
9358      */
9359     dispatch: function() {
9360           // TODO(nico) should store Array.prototype.slice.call somewhere.
9361         var args = Array.prototype.slice.call(arguments);
9362         var s = args.shift(), len = args.length;
9363         var val = function(a) { return typeof a == 'function'? a() : a; };
9364         if(len == 2) {
9365             return (s == "top" || s == "bottom")? val(args[0]) : val(args[1]);
9366         } else if(len == 4) {
9367             switch(s) {
9368                 case "top": return val(args[0]);
9369                 case "right": return val(args[1]);
9370                 case "bottom": return val(args[2]);
9371                 case "left": return val(args[3]);
9372             }
9373         }
9374         return undefined;
9375     },
9376
9377     /*
9378        Returns label height or with, depending on the tree current orientation.
9379     */  
9380     getSize: function(n, invert) {
9381         var data = n.data, config = this.config;
9382         var siblingOffset = config.siblingOffset;
9383         var s = (config.multitree 
9384                         && ('$orn' in data) 
9385                         && data.$orn) || config.orientation;
9386         var w = n.getData('width') + siblingOffset;
9387         var h = n.getData('height') + siblingOffset;
9388         if(!invert)
9389             return this.dispatch(s, h, w);
9390         else
9391             return this.dispatch(s, w, h);
9392     },
9393     
9394     /*
9395        Calculates a subtree base size. This is an utility function used by _getBaseSize_
9396     */  
9397     getTreeBaseSize: function(node, level, leaf) {
9398         var size = this.getSize(node, true), baseHeight = 0, that = this;
9399         if(leaf(level, node)) return size;
9400         if(level === 0) return 0;
9401         node.eachSubnode(function(elem) {
9402             baseHeight += that.getTreeBaseSize(elem, level -1, leaf);
9403         });
9404         return (size > baseHeight? size : baseHeight) + this.config.subtreeOffset;
9405     },
9406
9407
9408     /*
9409        getEdge
9410        
9411        Returns a Complex instance with the begin or end position of the edge to be plotted.
9412
9413        Parameters:
9414
9415        node - A <Graph.Node> that is connected to this edge.
9416        type - Returns the begin or end edge position. Possible values are 'begin' or 'end'.
9417
9418        Returns:
9419
9420        A <Complex> number specifying the begin or end position.
9421     */  
9422     getEdge: function(node, type, s) {
9423         var $C = function(a, b) { 
9424           return function(){
9425             return node.pos.add(new Complex(a, b));
9426           }; 
9427         };
9428         var dim = this.node;
9429         var w = node.getData('width');
9430         var h = node.getData('height');
9431
9432         if(type == 'begin') {
9433             if(dim.align == "center") {
9434                 return this.dispatch(s, $C(0, h/2), $C(-w/2, 0),
9435                                      $C(0, -h/2),$C(w/2, 0));
9436             } else if(dim.align == "left") {
9437                 return this.dispatch(s, $C(0, h), $C(0, 0),
9438                                      $C(0, 0), $C(w, 0));
9439             } else if(dim.align == "right") {
9440                 return this.dispatch(s, $C(0, 0), $C(-w, 0),
9441                                      $C(0, -h),$C(0, 0));
9442             } else throw "align: not implemented";
9443             
9444             
9445         } else if(type == 'end') {
9446             if(dim.align == "center") {
9447                 return this.dispatch(s, $C(0, -h/2), $C(w/2, 0),
9448                                      $C(0, h/2),  $C(-w/2, 0));
9449             } else if(dim.align == "left") {
9450                 return this.dispatch(s, $C(0, 0), $C(w, 0),
9451                                      $C(0, h), $C(0, 0));
9452             } else if(dim.align == "right") {
9453                 return this.dispatch(s, $C(0, -h),$C(0, 0),
9454                                      $C(0, 0), $C(-w, 0));
9455             } else throw "align: not implemented";
9456         }
9457     },
9458
9459     /*
9460        Adjusts the tree position due to canvas scaling or translation.
9461     */  
9462     getScaledTreePosition: function(node, scale) {
9463         var dim = this.node;
9464         var w = node.getData('width');
9465         var h = node.getData('height');
9466         var s = (this.config.multitree 
9467                         && ('$orn' in node.data) 
9468                         && node.data.$orn) || this.config.orientation;
9469
9470         var $C = function(a, b) { 
9471           return function(){
9472             return node.pos.add(new Complex(a, b)).$scale(1 - scale);
9473           }; 
9474         };
9475         if(dim.align == "left") {
9476             return this.dispatch(s, $C(0, h), $C(0, 0),
9477                                  $C(0, 0), $C(w, 0));
9478         } else if(dim.align == "center") {
9479             return this.dispatch(s, $C(0, h / 2), $C(-w / 2, 0),
9480                                  $C(0, -h / 2),$C(w / 2, 0));
9481         } else if(dim.align == "right") {
9482             return this.dispatch(s, $C(0, 0), $C(-w, 0),
9483                                  $C(0, -h),$C(0, 0));
9484         } else throw "align: not implemented";
9485     },
9486
9487     /*
9488        treeFitsInCanvas
9489        
9490        Returns a Boolean if the current subtree fits in canvas.
9491
9492        Parameters:
9493
9494        node - A <Graph.Node> which is the current root of the subtree.
9495        canvas - The <Canvas> object.
9496        level - The depth of the subtree to be considered.
9497     */  
9498     treeFitsInCanvas: function(node, canvas, level) {
9499         var csize = canvas.getSize();
9500         var s = (this.config.multitree 
9501                         && ('$orn' in node.data) 
9502                         && node.data.$orn) || this.config.orientation;
9503
9504         var size = this.dispatch(s, csize.width, csize.height);
9505         var baseSize = this.getTreeBaseSize(node, level, function(level, node) { 
9506           return level === 0 || !node.anySubnode();
9507         });
9508         return (baseSize < size);
9509     }
9510 });
9511
9512 /*
9513   Class: ST.Plot
9514   
9515   Custom extension of <Graph.Plot>.
9516
9517   Extends:
9518
9519   All <Graph.Plot> methods
9520   
9521   See also:
9522   
9523   <Graph.Plot>
9524
9525 */
9526 $jit.ST.Plot = new Class({
9527     
9528     Implements: Graph.Plot,
9529     
9530     /*
9531        Plots a subtree from the spacetree.
9532     */
9533     plotSubtree: function(node, opt, scale, animating) {
9534         var viz = this.viz, canvas = viz.canvas, config = viz.config;
9535         scale = Math.min(Math.max(0.001, scale), 1);
9536         if(scale >= 0) {
9537             node.drawn = false;     
9538             var ctx = canvas.getCtx();
9539             var diff = viz.geom.getScaledTreePosition(node, scale);
9540             ctx.translate(diff.x, diff.y);
9541             ctx.scale(scale, scale);
9542         }
9543         this.plotTree(node, $.merge(opt, {
9544           'withLabels': true,
9545           'hideLabels': !!scale,
9546           'plotSubtree': function(n, ch) {
9547             var root = config.multitree && !('$orn' in node.data);
9548             var orns = root && node.getData('orns');
9549             return !root || orns.indexOf(node.getData('orn')) > -1;
9550           }
9551         }), animating);
9552         if(scale >= 0) node.drawn = true;
9553     },   
9554    
9555     /*
9556         Method: getAlignedPos
9557         
9558         Returns a *x, y* object with the position of the top/left corner of a <ST> node.
9559         
9560         Parameters:
9561         
9562         pos - (object) A <Graph.Node> position.
9563         width - (number) The width of the node.
9564         height - (number) The height of the node.
9565         
9566      */
9567     getAlignedPos: function(pos, width, height) {
9568         var nconfig = this.node;
9569         var square, orn;
9570         if(nconfig.align == "center") {
9571             square = {
9572                 x: pos.x - width / 2,
9573                 y: pos.y - height / 2
9574             };
9575         } else if (nconfig.align == "left") {
9576             orn = this.config.orientation;
9577             if(orn == "bottom" || orn == "top") {
9578                 square = {
9579                     x: pos.x - width / 2,
9580                     y: pos.y
9581                 };
9582             } else {
9583                 square = {
9584                     x: pos.x,
9585                     y: pos.y - height / 2
9586                 };
9587             }
9588         } else if(nconfig.align == "right") {
9589             orn = this.config.orientation;
9590             if(orn == "bottom" || orn == "top") {
9591                 square = {
9592                     x: pos.x - width / 2,
9593                     y: pos.y - height
9594                 };
9595             } else {
9596                 square = {
9597                     x: pos.x - width,
9598                     y: pos.y - height / 2
9599                 };
9600             }
9601         } else throw "align: not implemented";
9602         
9603         return square;
9604     },
9605     
9606     getOrientation: function(adj) {
9607         var config = this.config;
9608         var orn = config.orientation;
9609
9610         if(config.multitree) {
9611                 var nodeFrom = adj.nodeFrom;
9612                 var nodeTo = adj.nodeTo;
9613                 orn = (('$orn' in nodeFrom.data) 
9614                         && nodeFrom.data.$orn) 
9615                         || (('$orn' in nodeTo.data) 
9616                         && nodeTo.data.$orn);
9617         }
9618
9619         return orn; 
9620     }
9621 });
9622
9623 /*
9624   Class: ST.Label
9625
9626   Custom extension of <Graph.Label>. 
9627   Contains custom <Graph.Label.SVG>, <Graph.Label.HTML> and <Graph.Label.Native> extensions.
9628
9629   Extends:
9630
9631   All <Graph.Label> methods and subclasses.
9632
9633   See also:
9634
9635   <Graph.Label>, <Graph.Label.Native>, <Graph.Label.HTML>, <Graph.Label.SVG>.
9636  */ 
9637 $jit.ST.Label = {};
9638
9639 /*
9640    ST.Label.Native
9641
9642    Custom extension of <Graph.Label.Native>.
9643
9644    Extends:
9645
9646    All <Graph.Label.Native> methods
9647
9648    See also:
9649
9650    <Graph.Label.Native>
9651 */
9652 $jit.ST.Label.Native = new Class({
9653   Implements: Graph.Label.Native,
9654
9655   renderLabel: function(canvas, node, controller) {
9656     var ctx = canvas.getCtx(),
9657         coord = node.pos.getc(true),
9658         width = node.getData('width'),
9659         height = node.getData('height'),
9660         pos = this.viz.fx.getAlignedPos(coord, width, height);
9661     ctx.fillText(node.name, pos.x + width / 2, pos.y + height / 2);
9662   }
9663 });
9664
9665 $jit.ST.Label.DOM = new Class({
9666   Implements: Graph.Label.DOM,
9667
9668   /* 
9669       placeLabel
9670
9671       Overrides abstract method placeLabel in <Graph.Plot>.
9672
9673       Parameters:
9674
9675       tag - A DOM label element.
9676       node - A <Graph.Node>.
9677       controller - A configuration/controller object passed to the visualization.
9678      
9679     */
9680     placeLabel: function(tag, node, controller) {
9681         var pos = node.pos.getc(true), 
9682             config = this.viz.config, 
9683             dim = config.Node, 
9684             canvas = this.viz.canvas,
9685             w = node.getData('width'),
9686             h = node.getData('height'),
9687             radius = canvas.getSize(),
9688             labelPos, orn;
9689         
9690         var ox = canvas.translateOffsetX,
9691             oy = canvas.translateOffsetY,
9692             sx = canvas.scaleOffsetX,
9693             sy = canvas.scaleOffsetY,
9694             posx = pos.x * sx + ox,
9695             posy = pos.y * sy + oy;
9696
9697         if(dim.align == "center") {
9698             labelPos= {
9699                 x: Math.round(posx - w / 2 + radius.width/2),
9700                 y: Math.round(posy - h / 2 + radius.height/2)
9701             };
9702         } else if (dim.align == "left") {
9703             orn = config.orientation;
9704             if(orn == "bottom" || orn == "top") {
9705                 labelPos= {
9706                     x: Math.round(posx - w / 2 + radius.width/2),
9707                     y: Math.round(posy + radius.height/2)
9708                 };
9709             } else {
9710                 labelPos= {
9711                     x: Math.round(posx + radius.width/2),
9712                     y: Math.round(posy - h / 2 + radius.height/2)
9713                 };
9714             }
9715         } else if(dim.align == "right") {
9716             orn = config.orientation;
9717             if(orn == "bottom" || orn == "top") {
9718                 labelPos= {
9719                     x: Math.round(posx - w / 2 + radius.width/2),
9720                     y: Math.round(posy - h + radius.height/2)
9721                 };
9722             } else {
9723                 labelPos= {
9724                     x: Math.round(posx - w + radius.width/2),
9725                     y: Math.round(posy - h / 2 + radius.height/2)
9726                 };
9727             }
9728         } else throw "align: not implemented";
9729
9730         var style = tag.style;
9731         style.left = labelPos.x + 'px';
9732         style.top  = labelPos.y + 'px';
9733         style.display = this.fitsInCanvas(labelPos, canvas)? '' : 'none';
9734         controller.onPlaceLabel(tag, node);
9735     }
9736 });
9737
9738 /*
9739   ST.Label.SVG
9740
9741   Custom extension of <Graph.Label.SVG>.
9742
9743   Extends:
9744
9745   All <Graph.Label.SVG> methods
9746
9747   See also:
9748
9749   <Graph.Label.SVG>
9750 */
9751 $jit.ST.Label.SVG = new Class({
9752   Implements: [$jit.ST.Label.DOM, Graph.Label.SVG],
9753
9754   initialize: function(viz) {
9755     this.viz = viz;
9756   }
9757 });
9758
9759 /*
9760    ST.Label.HTML
9761
9762    Custom extension of <Graph.Label.HTML>.
9763
9764    Extends:
9765
9766    All <Graph.Label.HTML> methods.
9767
9768    See also:
9769
9770    <Graph.Label.HTML>
9771
9772 */
9773 $jit.ST.Label.HTML = new Class({
9774   Implements: [$jit.ST.Label.DOM, Graph.Label.HTML],
9775
9776   initialize: function(viz) {
9777     this.viz = viz;
9778   }
9779 });
9780
9781
9782 /*
9783   Class: ST.Plot.NodeTypes
9784
9785   This class contains a list of <Graph.Node> built-in types. 
9786   Node types implemented are 'none', 'circle', 'rectangle', 'ellipse' and 'square'.
9787
9788   You can add your custom node types, customizing your visualization to the extreme.
9789
9790   Example:
9791
9792   (start code js)
9793     ST.Plot.NodeTypes.implement({
9794       'mySpecialType': {
9795         'render': function(node, canvas) {
9796           //print your custom node to canvas
9797         },
9798         //optional
9799         'contains': function(node, pos) {
9800           //return true if pos is inside the node or false otherwise
9801         }
9802       }
9803     });
9804   (end code)
9805
9806 */
9807 $jit.ST.Plot.NodeTypes = new Class({
9808   'none': {
9809     'render': $.empty,
9810     'contains': $.lambda(false)
9811   },
9812   'circle': {
9813     'render': function(node, canvas) {
9814       var dim  = node.getData('dim'),
9815           pos = this.getAlignedPos(node.pos.getc(true), dim, dim),
9816           dim2 = dim/2;
9817       this.nodeHelper.circle.render('fill', {x:pos.x+dim2, y:pos.y+dim2}, dim2, canvas);
9818     },
9819     'contains': function(node, pos) {
9820       var dim  = node.getData('dim'),
9821           npos = this.getAlignedPos(node.pos.getc(true), dim, dim),
9822           dim2 = dim/2;
9823       this.nodeHelper.circle.contains({x:npos.x+dim2, y:npos.y+dim2}, pos, dim2);
9824     }
9825   },
9826   'square': {
9827     'render': function(node, canvas) {
9828       var dim  = node.getData('dim'),
9829           dim2 = dim/2,
9830           pos = this.getAlignedPos(node.pos.getc(true), dim, dim);
9831       this.nodeHelper.square.render('fill', {x:pos.x+dim2, y:pos.y+dim2}, dim2, canvas);
9832     },
9833     'contains': function(node, pos) {
9834       var dim  = node.getData('dim'),
9835           npos = this.getAlignedPos(node.pos.getc(true), dim, dim),
9836           dim2 = dim/2;
9837       this.nodeHelper.square.contains({x:npos.x+dim2, y:npos.y+dim2}, pos, dim2);
9838     }
9839   },
9840   'ellipse': {
9841     'render': function(node, canvas) {
9842       var width = node.getData('width'),
9843           height = node.getData('height'),
9844           pos = this.getAlignedPos(node.pos.getc(true), width, height);
9845       this.nodeHelper.ellipse.render('fill', {x:pos.x+width/2, y:pos.y+height/2}, width, height, canvas);
9846     },
9847     'contains': function(node, pos) {
9848       var width = node.getData('width'),
9849           height = node.getData('height'),
9850           npos = this.getAlignedPos(node.pos.getc(true), width, height);
9851       this.nodeHelper.ellipse.contains({x:npos.x+width/2, y:npos.y+height/2}, pos, width, height);
9852     }
9853   },
9854   'rectangle': {
9855     'render': function(node, canvas) {
9856       var width = node.getData('width'),
9857           height = node.getData('height'),
9858           pos = this.getAlignedPos(node.pos.getc(true), width, height);
9859       this.nodeHelper.rectangle.render('fill', {x:pos.x+width/2, y:pos.y+height/2}, width, height, canvas);
9860     },
9861     'contains': function(node, pos) {
9862       var width = node.getData('width'),
9863           height = node.getData('height'),
9864           npos = this.getAlignedPos(node.pos.getc(true), width, height);
9865       this.nodeHelper.rectangle.contains({x:npos.x+width/2, y:npos.y+height/2}, pos, width, height);
9866     }
9867   }
9868 });
9869
9870 /*
9871   Class: ST.Plot.EdgeTypes
9872
9873   This class contains a list of <Graph.Adjacence> built-in types. 
9874   Edge types implemented are 'none', 'line', 'arrow', 'quadratic:begin', 'quadratic:end', 'bezier'.
9875
9876   You can add your custom edge types, customizing your visualization to the extreme.
9877
9878   Example:
9879
9880   (start code js)
9881     ST.Plot.EdgeTypes.implement({
9882       'mySpecialType': {
9883         'render': function(adj, canvas) {
9884           //print your custom edge to canvas
9885         },
9886         //optional
9887         'contains': function(adj, pos) {
9888           //return true if pos is inside the arc or false otherwise
9889         }
9890       }
9891     });
9892   (end code)
9893
9894 */
9895 $jit.ST.Plot.EdgeTypes = new Class({
9896     'none': $.empty,
9897     'line': {
9898       'render': function(adj, canvas) {
9899         var orn = this.getOrientation(adj),
9900             nodeFrom = adj.nodeFrom, 
9901             nodeTo = adj.nodeTo,
9902             rel = nodeFrom._depth < nodeTo._depth,
9903             from = this.viz.geom.getEdge(rel? nodeFrom:nodeTo, 'begin', orn),
9904             to =  this.viz.geom.getEdge(rel? nodeTo:nodeFrom, 'end', orn);
9905         this.edgeHelper.line.render(from, to, canvas);
9906       },
9907       'contains': function(adj, pos) {
9908         var orn = this.getOrientation(adj),
9909             nodeFrom = adj.nodeFrom, 
9910             nodeTo = adj.nodeTo,
9911             rel = nodeFrom._depth < nodeTo._depth,
9912             from = this.viz.geom.getEdge(rel? nodeFrom:nodeTo, 'begin', orn),
9913             to =  this.viz.geom.getEdge(rel? nodeTo:nodeFrom, 'end', orn);
9914         return this.edgeHelper.line.contains(from, to, pos, this.edge.epsilon);
9915       }
9916     },
9917      'arrow': {
9918        'render': function(adj, canvas) {
9919          var orn = this.getOrientation(adj),
9920              node = adj.nodeFrom, 
9921              child = adj.nodeTo,
9922              dim = adj.getData('dim'),
9923              from = this.viz.geom.getEdge(node, 'begin', orn),
9924              to = this.viz.geom.getEdge(child, 'end', orn),
9925              direction = adj.data.$direction,
9926              inv = (direction && direction.length>1 && direction[0] != node.id);
9927          this.edgeHelper.arrow.render(from, to, dim, inv, canvas);
9928        },
9929        'contains': function(adj, pos) {
9930          var orn = this.getOrientation(adj),
9931              nodeFrom = adj.nodeFrom, 
9932              nodeTo = adj.nodeTo,
9933              rel = nodeFrom._depth < nodeTo._depth,
9934              from = this.viz.geom.getEdge(rel? nodeFrom:nodeTo, 'begin', orn),
9935              to =  this.viz.geom.getEdge(rel? nodeTo:nodeFrom, 'end', orn);
9936          return this.edgeHelper.arrow.contains(from, to, pos, this.edge.epsilon);
9937        }
9938      },
9939     'quadratic:begin': {
9940        'render': function(adj, canvas) {
9941           var orn = this.getOrientation(adj);
9942           var nodeFrom = adj.nodeFrom, 
9943               nodeTo = adj.nodeTo,
9944               rel = nodeFrom._depth < nodeTo._depth,
9945               begin = this.viz.geom.getEdge(rel? nodeFrom:nodeTo, 'begin', orn),
9946               end =  this.viz.geom.getEdge(rel? nodeTo:nodeFrom, 'end', orn),
9947               dim = adj.getData('dim'),
9948               ctx = canvas.getCtx();
9949           ctx.beginPath();
9950           ctx.moveTo(begin.x, begin.y);
9951           switch(orn) {
9952             case "left":
9953               ctx.quadraticCurveTo(begin.x + dim, begin.y, end.x, end.y);
9954               break;
9955             case "right":
9956               ctx.quadraticCurveTo(begin.x - dim, begin.y, end.x, end.y);
9957               break;
9958             case "top":
9959               ctx.quadraticCurveTo(begin.x, begin.y + dim, end.x, end.y);
9960               break;
9961             case "bottom":
9962               ctx.quadraticCurveTo(begin.x, begin.y - dim, end.x, end.y);
9963               break;
9964           }
9965           ctx.stroke();
9966         }
9967      },
9968     'quadratic:end': {
9969        'render': function(adj, canvas) {
9970           var orn = this.getOrientation(adj);
9971           var nodeFrom = adj.nodeFrom, 
9972               nodeTo = adj.nodeTo,
9973               rel = nodeFrom._depth < nodeTo._depth,
9974               begin = this.viz.geom.getEdge(rel? nodeFrom:nodeTo, 'begin', orn),
9975               end =  this.viz.geom.getEdge(rel? nodeTo:nodeFrom, 'end', orn),
9976               dim = adj.getData('dim'),
9977               ctx = canvas.getCtx();
9978           ctx.beginPath();
9979           ctx.moveTo(begin.x, begin.y);
9980           switch(orn) {
9981             case "left":
9982               ctx.quadraticCurveTo(end.x - dim, end.y, end.x, end.y);
9983               break;
9984             case "right":
9985               ctx.quadraticCurveTo(end.x + dim, end.y, end.x, end.y);
9986               break;
9987             case "top":
9988               ctx.quadraticCurveTo(end.x, end.y - dim, end.x, end.y);
9989               break;
9990             case "bottom":
9991               ctx.quadraticCurveTo(end.x, end.y + dim, end.x, end.y);
9992               break;
9993           }
9994           ctx.stroke();
9995        }
9996      },
9997     'bezier': {
9998        'render': function(adj, canvas) {
9999          var orn = this.getOrientation(adj),
10000              nodeFrom = adj.nodeFrom, 
10001              nodeTo = adj.nodeTo,
10002              rel = nodeFrom._depth < nodeTo._depth,
10003              begin = this.viz.geom.getEdge(rel? nodeFrom:nodeTo, 'begin', orn),
10004              end =  this.viz.geom.getEdge(rel? nodeTo:nodeFrom, 'end', orn),
10005              dim = adj.getData('dim'),
10006              ctx = canvas.getCtx();
10007          ctx.beginPath();
10008          ctx.moveTo(begin.x, begin.y);
10009          switch(orn) {
10010            case "left":
10011              ctx.bezierCurveTo(begin.x + dim, begin.y, end.x - dim, end.y, end.x, end.y);
10012              break;
10013            case "right":
10014              ctx.bezierCurveTo(begin.x - dim, begin.y, end.x + dim, end.y, end.x, end.y);
10015              break;
10016            case "top":
10017              ctx.bezierCurveTo(begin.x, begin.y + dim, end.x, end.y - dim, end.x, end.y);
10018              break;
10019            case "bottom":
10020              ctx.bezierCurveTo(begin.x, begin.y - dim, end.x, end.y + dim, end.x, end.y);
10021              break;
10022          }
10023          ctx.stroke();
10024        }
10025     }
10026 });
10027
10028
10029
10030 /*
10031  * File: AreaChart.js
10032  *
10033 */
10034
10035 $jit.ST.Plot.NodeTypes.implement({
10036   'areachart-stacked' : {
10037     'render' : function(node, canvas) {
10038       var pos = node.pos.getc(true), 
10039           width = node.getData('width'),
10040           height = node.getData('height'),
10041           algnPos = this.getAlignedPos(pos, width, height),
10042           x = algnPos.x, y = algnPos.y,
10043           stringArray = node.getData('stringArray'),
10044           dimArray = node.getData('dimArray'),
10045           valArray = node.getData('valueArray'),
10046           valLeft = $.reduce(valArray, function(x, y) { return x + y[0]; }, 0),
10047           valRight = $.reduce(valArray, function(x, y) { return x + y[1]; }, 0),
10048           colorArray = node.getData('colorArray'),
10049           colorLength = colorArray.length,
10050           config = node.getData('config'),
10051           gradient = node.getData('gradient'),
10052           showLabels = config.showLabels,
10053           aggregates = config.showAggregates,
10054           label = config.Label,
10055           prev = node.getData('prev');
10056
10057       var ctx = canvas.getCtx(), border = node.getData('border');
10058       if (colorArray && dimArray && stringArray) {
10059         for (var i=0, l=dimArray.length, acumLeft=0, acumRight=0, valAcum=0; i<l; i++) {
10060           ctx.fillStyle = ctx.strokeStyle = colorArray[i % colorLength];
10061           ctx.save();
10062           if(gradient && (dimArray[i][0] > 0 || dimArray[i][1] > 0)) {
10063             var h1 = acumLeft + dimArray[i][0],
10064                 h2 = acumRight + dimArray[i][1],
10065                 alpha = Math.atan((h2 - h1) / width),
10066                 delta = 55;
10067             var linear = ctx.createLinearGradient(x + width/2, 
10068                 y - (h1 + h2)/2,
10069                 x + width/2 + delta * Math.sin(alpha),
10070                 y - (h1 + h2)/2 + delta * Math.cos(alpha));
10071             var color = $.rgbToHex($.map($.hexToRgb(colorArray[i % colorLength].slice(1)), 
10072                 function(v) { return (v * 0.85) >> 0; }));
10073             linear.addColorStop(0, colorArray[i % colorLength]);
10074             linear.addColorStop(1, color);
10075             ctx.fillStyle = linear;
10076           }
10077           ctx.beginPath();
10078           ctx.moveTo(x, y - acumLeft);
10079           ctx.lineTo(x + width, y - acumRight);
10080           ctx.lineTo(x + width, y - acumRight - dimArray[i][1]);
10081           ctx.lineTo(x, y - acumLeft - dimArray[i][0]);
10082           ctx.lineTo(x, y - acumLeft);
10083           ctx.fill();
10084           ctx.restore();
10085           if(border) {
10086             var strong = border.name == stringArray[i];
10087             var perc = strong? 0.7 : 0.8;
10088             var color = $.rgbToHex($.map($.hexToRgb(colorArray[i % colorLength].slice(1)), 
10089                 function(v) { return (v * perc) >> 0; }));
10090             ctx.strokeStyle = color;
10091             ctx.lineWidth = strong? 4 : 1;
10092             ctx.save();
10093             ctx.beginPath();
10094             if(border.index === 0) {
10095               ctx.moveTo(x, y - acumLeft);
10096               ctx.lineTo(x, y - acumLeft - dimArray[i][0]);
10097             } else {
10098               ctx.moveTo(x + width, y - acumRight);
10099               ctx.lineTo(x + width, y - acumRight - dimArray[i][1]);
10100             }
10101             ctx.stroke();
10102             ctx.restore();
10103           }
10104           acumLeft += (dimArray[i][0] || 0);
10105           acumRight += (dimArray[i][1] || 0);
10106           
10107           if(dimArray[i][0] > 0)
10108             valAcum += (valArray[i][0] || 0);
10109         }
10110         if(prev && label.type == 'Native') {
10111           ctx.save();
10112           ctx.beginPath();
10113           ctx.fillStyle = ctx.strokeStyle = label.color;
10114           ctx.font = label.style + ' ' + label.size + 'px ' + label.family;
10115           ctx.textAlign = 'center';
10116           ctx.textBaseline = 'middle';
10117           var aggValue = aggregates(node.name, valLeft, valRight, node, valAcum);
10118           if(aggValue !== false) {
10119             ctx.fillText(aggValue !== true? aggValue : valAcum, x, y - acumLeft - config.labelOffset - label.size/2, width);
10120           }
10121           if(showLabels(node.name, valLeft, valRight, node)) {
10122             ctx.fillText(node.name, x, y + label.size/2 + config.labelOffset);
10123           }
10124           ctx.restore();
10125         }
10126       }
10127     },
10128     'contains': function(node, mpos) {
10129       var pos = node.pos.getc(true), 
10130           width = node.getData('width'),
10131           height = node.getData('height'),
10132           algnPos = this.getAlignedPos(pos, width, height),
10133           x = algnPos.x, y = algnPos.y,
10134           dimArray = node.getData('dimArray'),
10135           rx = mpos.x - x;
10136       //bounding box check
10137       if(mpos.x < x || mpos.x > x + width
10138         || mpos.y > y || mpos.y < y - height) {
10139         return false;
10140       }
10141       //deep check
10142       for(var i=0, l=dimArray.length, lAcum=y, rAcum=y; i<l; i++) {
10143         var dimi = dimArray[i];
10144         lAcum -= dimi[0];
10145         rAcum -= dimi[1];
10146         var intersec = lAcum + (rAcum - lAcum) * rx / width;
10147         if(mpos.y >= intersec) {
10148           var index = +(rx > width/2);
10149           return {
10150             'name': node.getData('stringArray')[i],
10151             'color': node.getData('colorArray')[i],
10152             'value': node.getData('valueArray')[i][index],
10153             'index': index
10154           };
10155         }
10156       }
10157       return false;
10158     }
10159   }
10160 });
10161
10162 /*
10163   Class: AreaChart
10164   
10165   A visualization that displays stacked area charts.
10166   
10167   Constructor Options:
10168   
10169   See <Options.AreaChart>.
10170
10171 */
10172 $jit.AreaChart = new Class({
10173   st: null,
10174   colors: ["#416D9C", "#70A35E", "#EBB056", "#C74243", "#83548B", "#909291", "#557EAA"],
10175   selected: {},
10176   busy: false,
10177   
10178   initialize: function(opt) {
10179     this.controller = this.config = 
10180       $.merge(Options("Canvas", "Margin", "Label", "AreaChart"), {
10181         Label: { type: 'Native' }
10182       }, opt);
10183     //set functions for showLabels and showAggregates
10184     var showLabels = this.config.showLabels,
10185         typeLabels = $.type(showLabels),
10186         showAggregates = this.config.showAggregates,
10187         typeAggregates = $.type(showAggregates);
10188     this.config.showLabels = typeLabels == 'function'? showLabels : $.lambda(showLabels);
10189     this.config.showAggregates = typeAggregates == 'function'? showAggregates : $.lambda(showAggregates);
10190     
10191     this.initializeViz();
10192   },
10193   
10194   initializeViz: function() {
10195     var config = this.config,
10196         that = this,
10197         nodeType = config.type.split(":")[0],
10198         nodeLabels = {};
10199
10200     var delegate = new $jit.ST({
10201       injectInto: config.injectInto,
10202       width: config.width,
10203       height: config.height,
10204       orientation: "bottom",
10205       levelDistance: 0,
10206       siblingOffset: 0,
10207       subtreeOffset: 0,
10208       withLabels: config.Label.type != 'Native',
10209       useCanvas: config.useCanvas,
10210       Label: {
10211         type: config.Label.type
10212       },
10213       Node: {
10214         overridable: true,
10215         type: 'areachart-' + nodeType,
10216         align: 'left',
10217         width: 1,
10218         height: 1
10219       },
10220       Edge: {
10221         type: 'none'
10222       },
10223       Tips: {
10224         enable: config.Tips.enable,
10225         type: 'Native',
10226         force: true,
10227         onShow: function(tip, node, contains) {
10228           var elem = contains;
10229           config.Tips.onShow(tip, elem, node);
10230         }
10231       },
10232       Events: {
10233         enable: true,
10234         type: 'Native',
10235         onClick: function(node, eventInfo, evt) {
10236           if(!config.filterOnClick && !config.Events.enable) return;
10237           var elem = eventInfo.getContains();
10238           if(elem) config.filterOnClick && that.filter(elem.name);
10239           config.Events.enable && config.Events.onClick(elem, eventInfo, evt);
10240         },
10241         onRightClick: function(node, eventInfo, evt) {
10242           if(!config.restoreOnRightClick) return;
10243           that.restore();
10244         },
10245         onMouseMove: function(node, eventInfo, evt) {
10246           if(!config.selectOnHover) return;
10247           if(node) {
10248             var elem = eventInfo.getContains();
10249             that.select(node.id, elem.name, elem.index);
10250           } else {
10251             that.select(false, false, false);
10252           }
10253         }
10254       },
10255       onCreateLabel: function(domElement, node) {
10256         var labelConf = config.Label,
10257             valueArray = node.getData('valueArray'),
10258             acumLeft = $.reduce(valueArray, function(x, y) { return x + y[0]; }, 0),
10259             acumRight = $.reduce(valueArray, function(x, y) { return x + y[1]; }, 0);
10260         if(node.getData('prev')) {
10261           var nlbs = {
10262             wrapper: document.createElement('div'),
10263             aggregate: document.createElement('div'),
10264             label: document.createElement('div')
10265           };
10266           var wrapper = nlbs.wrapper,
10267               label = nlbs.label,
10268               aggregate = nlbs.aggregate,
10269               wrapperStyle = wrapper.style,
10270               labelStyle = label.style,
10271               aggregateStyle = aggregate.style;
10272           //store node labels
10273           nodeLabels[node.id] = nlbs;
10274           //append labels
10275           wrapper.appendChild(label);
10276           wrapper.appendChild(aggregate);
10277           if(!config.showLabels(node.name, acumLeft, acumRight, node)) {
10278             label.style.display = 'none';
10279           }
10280           if(!config.showAggregates(node.name, acumLeft, acumRight, node)) {
10281             aggregate.style.display = 'none';
10282           }
10283           wrapperStyle.position = 'relative';
10284           wrapperStyle.overflow = 'visible';
10285           wrapperStyle.fontSize = labelConf.size + 'px';
10286           wrapperStyle.fontFamily = labelConf.family;
10287           wrapperStyle.color = labelConf.color;
10288           wrapperStyle.textAlign = 'center';
10289           aggregateStyle.position = labelStyle.position = 'absolute';
10290           
10291           domElement.style.width = node.getData('width') + 'px';
10292           domElement.style.height = node.getData('height') + 'px';
10293           label.innerHTML = node.name;
10294           
10295           domElement.appendChild(wrapper);
10296         }
10297       },
10298       onPlaceLabel: function(domElement, node) {
10299         if(!node.getData('prev')) return;
10300         var labels = nodeLabels[node.id],
10301             wrapperStyle = labels.wrapper.style,
10302             labelStyle = labels.label.style,
10303             aggregateStyle = labels.aggregate.style,
10304             width = node.getData('width'),
10305             height = node.getData('height'),
10306             dimArray = node.getData('dimArray'),
10307             valArray = node.getData('valueArray'),
10308             acumLeft = $.reduce(valArray, function(x, y) { return x + y[0]; }, 0),
10309             acumRight = $.reduce(valArray, function(x, y) { return x + y[1]; }, 0),
10310             font = parseInt(wrapperStyle.fontSize, 10),
10311             domStyle = domElement.style;
10312         
10313         if(dimArray && valArray) {
10314           if(config.showLabels(node.name, acumLeft, acumRight, node)) {
10315             labelStyle.display = '';
10316           } else {
10317             labelStyle.display = 'none';
10318           }
10319           var aggValue = config.showAggregates(node.name, acumLeft, acumRight, node);
10320           if(aggValue !== false) {
10321             aggregateStyle.display = '';
10322           } else {
10323             aggregateStyle.display = 'none';
10324           }
10325           wrapperStyle.width = aggregateStyle.width = labelStyle.width = domElement.style.width = width + 'px';
10326           aggregateStyle.left = labelStyle.left = -width/2 + 'px';
10327           for(var i=0, l=valArray.length, acum=0, leftAcum=0; i<l; i++) {
10328             if(dimArray[i][0] > 0) {
10329               acum+= valArray[i][0];
10330               leftAcum+= dimArray[i][0];
10331             }
10332           }
10333           aggregateStyle.top = (-font - config.labelOffset) + 'px';
10334           labelStyle.top = (config.labelOffset + leftAcum) + 'px';
10335           domElement.style.top = parseInt(domElement.style.top, 10) - leftAcum + 'px';
10336           domElement.style.height = wrapperStyle.height = leftAcum + 'px';
10337           labels.aggregate.innerHTML = aggValue !== true? aggValue : acum;
10338         }
10339       }
10340     });
10341     
10342     var size = delegate.canvas.getSize(),
10343         margin = config.Margin;
10344     delegate.config.offsetY = -size.height/2 + margin.bottom 
10345       + (config.showLabels && (config.labelOffset + config.Label.size));
10346     delegate.config.offsetX = (margin.right - margin.left)/2;
10347     this.delegate = delegate;
10348     this.canvas = this.delegate.canvas;
10349   },
10350   
10351  /*
10352   Method: loadJSON
10353  
10354   Loads JSON data into the visualization. 
10355   
10356   Parameters:
10357   
10358   json - The JSON data format. This format is described in <http://blog.thejit.org/2010/04/24/new-javascript-infovis-toolkit-visualizations/#json-data-format>.
10359   
10360   Example:
10361   (start code js)
10362   var areaChart = new $jit.AreaChart(options);
10363   areaChart.loadJSON(json);
10364   (end code)
10365  */  
10366   loadJSON: function(json) {
10367     var prefix = $.time(), 
10368         ch = [], 
10369         delegate = this.delegate,
10370         name = $.splat(json.label), 
10371         color = $.splat(json.color || this.colors),
10372         config = this.config,
10373         gradient = !!config.type.split(":")[1],
10374         animate = config.animate;
10375     
10376     for(var i=0, values=json.values, l=values.length; i<l-1; i++) {
10377       var val = values[i], prev = values[i-1], next = values[i+1];
10378       var valLeft = $.splat(values[i].values), valRight = $.splat(values[i+1].values);
10379       var valArray = $.zip(valLeft, valRight);
10380       var acumLeft = 0, acumRight = 0;
10381       ch.push({
10382         'id': prefix + val.label,
10383         'name': val.label,
10384         'data': {
10385           'value': valArray,
10386           '$valueArray': valArray,
10387           '$colorArray': color,
10388           '$stringArray': name,
10389           '$next': next.label,
10390           '$prev': prev? prev.label:false,
10391           '$config': config,
10392           '$gradient': gradient
10393         },
10394         'children': []
10395       });
10396     }
10397     var root = {
10398       'id': prefix + '$root',
10399       'name': '',
10400       'data': {
10401         '$type': 'none',
10402         '$width': 1,
10403         '$height': 1
10404       },
10405       'children': ch
10406     };
10407     delegate.loadJSON(root);
10408     
10409     this.normalizeDims();
10410     delegate.compute();
10411     delegate.select(delegate.root);
10412     if(animate) {
10413       delegate.fx.animate({
10414         modes: ['node-property:height:dimArray'],
10415         duration:1500
10416       });
10417     }
10418   },
10419   
10420  /*
10421   Method: updateJSON
10422  
10423   Use this method when updating values for the current JSON data. If the items specified by the JSON data already exist in the graph then their values will be updated.
10424   
10425   Parameters:
10426   
10427   json - (object) JSON data to be updated. The JSON format corresponds to the one described in <AreaChart.loadJSON>.
10428   onComplete - (object) A callback object to be called when the animation transition when updating the data end.
10429   
10430   Example:
10431   
10432   (start code js)
10433   areaChart.updateJSON(json, {
10434     onComplete: function() {
10435       alert('update complete!');
10436     }
10437   });
10438   (end code)
10439  */  
10440   updateJSON: function(json, onComplete) {
10441     if(this.busy) return;
10442     this.busy = true;
10443     
10444     var delegate = this.delegate,
10445         graph = delegate.graph,
10446         labels = json.label && $.splat(json.label),
10447         values = json.values,
10448         animate = this.config.animate,
10449         that = this,
10450         hashValues = {};
10451
10452     //convert the whole thing into a hash
10453     for (var i = 0, l = values.length; i < l; i++) {
10454       hashValues[values[i].label] = values[i];
10455     }
10456   
10457     graph.eachNode(function(n) {
10458       var v = hashValues[n.name],
10459           stringArray = n.getData('stringArray'),
10460           valArray = n.getData('valueArray'),
10461           next = n.getData('next');
10462       
10463       if (v) {
10464         v.values = $.splat(v.values);
10465         $.each(valArray, function(a, i) {
10466           a[0] = v.values[i];
10467           if(labels) stringArray[i] = labels[i];
10468         });
10469         n.setData('valueArray', valArray);
10470       }
10471      
10472       if(next) {
10473         v = hashValues[next];
10474         if(v) {
10475           $.each(valArray, function(a, i) {
10476             a[1] = v.values[i];
10477           });
10478         }
10479       }
10480     });
10481     this.normalizeDims();
10482     delegate.compute();
10483     delegate.select(delegate.root);
10484     if(animate) {
10485       delegate.fx.animate({
10486         modes: ['node-property:height:dimArray'],
10487         duration:1500,
10488         onComplete: function() {
10489           that.busy = false;
10490           onComplete && onComplete.onComplete();
10491         }
10492       });
10493     }
10494   },
10495   
10496 /*
10497   Method: filter
10498  
10499   Filter selected stacks, collapsing all other stacks. You can filter multiple stacks at the same time.
10500   
10501   Parameters:
10502   
10503   filters - (array) An array of strings with the name of the stacks to be filtered.
10504   callback - (object) An object with an *onComplete* callback method. 
10505   
10506   Example:
10507   
10508   (start code js)
10509   areaChart.filter(['label A', 'label C'], {
10510       onComplete: function() {
10511           console.log('done!');
10512       }
10513   });
10514   (end code)
10515   
10516   See also:
10517   
10518   <AreaChart.restore>.
10519  */  
10520   filter: function(filters, callback) {
10521     if(this.busy) return;
10522     this.busy = true;
10523     if(this.config.Tips.enable) this.delegate.tips.hide();
10524     this.select(false, false, false);
10525     var args = $.splat(filters);
10526     var rt = this.delegate.graph.getNode(this.delegate.root);
10527     var that = this;
10528     this.normalizeDims();
10529     rt.eachAdjacency(function(adj) {
10530       var n = adj.nodeTo, 
10531           dimArray = n.getData('dimArray', 'end'),
10532           stringArray = n.getData('stringArray');
10533       n.setData('dimArray', $.map(dimArray, function(d, i) {
10534         return ($.indexOf(args, stringArray[i]) > -1)? d:[0, 0];
10535       }), 'end');
10536     });
10537     this.delegate.fx.animate({
10538       modes: ['node-property:dimArray'],
10539       duration:1500,
10540       onComplete: function() {
10541         that.busy = false;
10542         callback && callback.onComplete();
10543       }
10544     });
10545   },
10546   
10547   /*
10548   Method: restore
10549  
10550   Sets all stacks that could have been filtered visible.
10551   
10552   Example:
10553   
10554   (start code js)
10555   areaChart.restore();
10556   (end code)
10557   
10558   See also:
10559   
10560   <AreaChart.filter>.
10561  */  
10562   restore: function(callback) {
10563     if(this.busy) return;
10564     this.busy = true;
10565     if(this.config.Tips.enable) this.delegate.tips.hide();
10566     this.select(false, false, false);
10567     this.normalizeDims();
10568     var that = this;
10569     this.delegate.fx.animate({
10570       modes: ['node-property:height:dimArray'],
10571       duration:1500,
10572       onComplete: function() {
10573         that.busy = false;
10574         callback && callback.onComplete();
10575       }
10576     });
10577   },
10578   //adds the little brown bar when hovering the node
10579   select: function(id, name, index) {
10580     if(!this.config.selectOnHover) return;
10581     var s = this.selected;
10582     if(s.id != id || s.name != name 
10583         || s.index != index) {
10584       s.id = id;
10585       s.name = name;
10586       s.index = index;
10587       this.delegate.graph.eachNode(function(n) {
10588         n.setData('border', false);
10589       });
10590       if(id) {
10591         var n = this.delegate.graph.getNode(id);
10592         n.setData('border', s);
10593         var link = index === 0? 'prev':'next';
10594         link = n.getData(link);
10595         if(link) {
10596           n = this.delegate.graph.getByName(link);
10597           if(n) {
10598             n.setData('border', {
10599               name: name,
10600               index: 1-index
10601             });
10602           }
10603         }
10604       }
10605       this.delegate.plot();
10606     }
10607   },
10608   
10609   /*
10610     Method: getLegend
10611    
10612     Returns an object containing as keys the legend names and as values hex strings with color values.
10613     
10614     Example:
10615     
10616     (start code js)
10617     var legend = areaChart.getLegend();
10618     (end code)
10619  */  
10620   getLegend: function() {
10621     var legend = {};
10622     var n;
10623     this.delegate.graph.getNode(this.delegate.root).eachAdjacency(function(adj) {
10624       n = adj.nodeTo;
10625     });
10626     var colors = n.getData('colorArray'),
10627         len = colors.length;
10628     $.each(n.getData('stringArray'), function(s, i) {
10629       legend[s] = colors[i % len];
10630     });
10631     return legend;
10632   },
10633   
10634   /*
10635     Method: getMaxValue
10636    
10637     Returns the maximum accumulated value for the stacks. This method is used for normalizing the graph heights according to the canvas height.
10638     
10639     Example:
10640     
10641     (start code js)
10642     var ans = areaChart.getMaxValue();
10643     (end code)
10644     
10645     In some cases it could be useful to override this method to normalize heights for a group of AreaCharts, like when doing small multiples.
10646     
10647     Example:
10648     
10649     (start code js)
10650     //will return 100 for all AreaChart instances,
10651     //displaying all of them with the same scale
10652     $jit.AreaChart.implement({
10653       'getMaxValue': function() {
10654         return 100;
10655       }
10656     });
10657     (end code)
10658     
10659 */  
10660   getMaxValue: function() {
10661     var maxValue = 0;
10662     this.delegate.graph.eachNode(function(n) {
10663       var valArray = n.getData('valueArray'),
10664           acumLeft = 0, acumRight = 0;
10665       $.each(valArray, function(v) { 
10666         acumLeft += +v[0];
10667         acumRight += +v[1];
10668       });
10669       var acum = acumRight>acumLeft? acumRight:acumLeft;
10670       maxValue = maxValue>acum? maxValue:acum;
10671     });
10672     return maxValue;
10673   },
10674   
10675   normalizeDims: function() {
10676     //number of elements
10677     var root = this.delegate.graph.getNode(this.delegate.root), l=0;
10678     root.eachAdjacency(function() {
10679       l++;
10680     });
10681     var maxValue = this.getMaxValue() || 1,
10682         size = this.delegate.canvas.getSize(),
10683         config = this.config,
10684         margin = config.Margin,
10685         labelOffset = config.labelOffset + config.Label.size,
10686         fixedDim = (size.width - (margin.left + margin.right)) / l,
10687         animate = config.animate,
10688         height = size.height - (margin.top + margin.bottom) - (config.showAggregates && labelOffset) 
10689           - (config.showLabels && labelOffset);
10690     this.delegate.graph.eachNode(function(n) {
10691       var acumLeft = 0, acumRight = 0, animateValue = [];
10692       $.each(n.getData('valueArray'), function(v) {
10693         acumLeft += +v[0];
10694         acumRight += +v[1];
10695         animateValue.push([0, 0]);
10696       });
10697       var acum = acumRight>acumLeft? acumRight:acumLeft;
10698       n.setData('width', fixedDim);
10699       if(animate) {
10700         n.setData('height', acum * height / maxValue, 'end');
10701         n.setData('dimArray', $.map(n.getData('valueArray'), function(n) { 
10702           return [n[0] * height / maxValue, n[1] * height / maxValue]; 
10703         }), 'end');
10704         var dimArray = n.getData('dimArray');
10705         if(!dimArray) {
10706           n.setData('dimArray', animateValue);
10707         }
10708       } else {
10709         n.setData('height', acum * height / maxValue);
10710         n.setData('dimArray', $.map(n.getData('valueArray'), function(n) { 
10711           return [n[0] * height / maxValue, n[1] * height / maxValue]; 
10712         }));
10713       }
10714     });
10715   }
10716 });
10717
10718
10719 /*
10720  * File: Options.BarChart.js
10721  *
10722 */
10723
10724 /*
10725   Object: Options.BarChart
10726   
10727   <BarChart> options. 
10728   Other options included in the BarChart are <Options.Canvas>, <Options.Label>, <Options.Margin>, <Options.Tips> and <Options.Events>.
10729   
10730   Syntax:
10731   
10732   (start code js)
10733
10734   Options.BarChart = {
10735     animate: true,
10736     labelOffset: 3,
10737     barsOffset: 0,
10738     type: 'stacked',
10739     hoveredColor: '#9fd4ff',
10740     orientation: 'horizontal',
10741     showAggregates: true,
10742     showLabels: true
10743   };
10744   
10745   (end code)
10746   
10747   Example:
10748   
10749   (start code js)
10750
10751   var barChart = new $jit.BarChart({
10752     animate: true,
10753     barsOffset: 10,
10754     type: 'stacked:gradient'
10755   });
10756   
10757   (end code)
10758
10759   Parameters:
10760   
10761   animate - (boolean) Default's *true*. Whether to add animated transitions when filtering/restoring stacks.
10762   offset - (number) Default's *25*. Adds margin between the visualization and the canvas.
10763   labelOffset - (number) Default's *3*. Adds margin between the label and the default place where it should be drawn.
10764   barsOffset - (number) Default's *0*. Separation between bars.
10765   type - (string) Default's *'stacked'*. Stack or grouped styles. Posible values are 'stacked', 'grouped', 'stacked:gradient', 'grouped:gradient' to add gradients.
10766   hoveredColor - (boolean|string) Default's *'#9fd4ff'*. Sets the selected color for a hovered bar stack.
10767   orientation - (string) Default's 'horizontal'. Sets the direction of the bars. Possible options are 'vertical' or 'horizontal'.
10768   showAggregates - (boolean, function) Default's *true*. Display the sum the values of each bar. Can also be a function that returns *true* or *false* to display the value of the bar or not. That same function can also return a string with the formatted data to be added.
10769   showLabels - (boolean, function) Default's *true*. Display the name of the slots. Can also be a function that returns *true* or *false* for each bar to decide whether to show the label or not.
10770   
10771 */
10772
10773 Options.BarChart = {
10774   $extend: true,
10775   
10776   animate: true,
10777   type: 'stacked', //stacked, grouped, : gradient
10778   labelOffset: 3, //label offset
10779   barsOffset: 0, //distance between bars
10780   hoveredColor: '#9fd4ff',
10781   orientation: 'horizontal',
10782   showAggregates: true,
10783   showLabels: true,
10784   Tips: {
10785     enable: false,
10786     onShow: $.empty,
10787     onHide: $.empty
10788   },
10789   Events: {
10790     enable: false,
10791     onClick: $.empty
10792   }
10793 };
10794
10795 /*
10796  * File: BarChart.js
10797  *
10798 */
10799
10800 $jit.ST.Plot.NodeTypes.implement({
10801   'barchart-stacked' : {
10802     'render' : function(node, canvas) {
10803       var pos = node.pos.getc(true), 
10804           width = node.getData('width'),
10805           height = node.getData('height'),
10806           algnPos = this.getAlignedPos(pos, width, height),
10807           x = algnPos.x, y = algnPos.y,
10808           dimArray = node.getData('dimArray'),
10809           valueArray = node.getData('valueArray'),
10810           colorArray = node.getData('colorArray'),
10811           colorLength = colorArray.length,
10812           stringArray = node.getData('stringArray');
10813
10814       var ctx = canvas.getCtx(),
10815           opt = {},
10816           border = node.getData('border'),
10817           gradient = node.getData('gradient'),
10818           config = node.getData('config'),
10819           horz = config.orientation == 'horizontal',
10820           aggregates = config.showAggregates,
10821           showLabels = config.showLabels,
10822           label = config.Label;
10823       
10824       if (colorArray && dimArray && stringArray) {
10825         for (var i=0, l=dimArray.length, acum=0, valAcum=0; i<l; i++) {
10826           ctx.fillStyle = ctx.strokeStyle = colorArray[i % colorLength];
10827           if(gradient) {
10828             var linear;
10829             if(horz) {
10830               linear = ctx.createLinearGradient(x + acum + dimArray[i]/2, y, 
10831                   x + acum + dimArray[i]/2, y + height);
10832             } else {
10833               linear = ctx.createLinearGradient(x, y - acum - dimArray[i]/2, 
10834                   x + width, y - acum- dimArray[i]/2);
10835             }
10836             var color = $.rgbToHex($.map($.hexToRgb(colorArray[i % colorLength].slice(1)), 
10837                 function(v) { return (v * 0.5) >> 0; }));
10838             linear.addColorStop(0, color);
10839             linear.addColorStop(0.5, colorArray[i % colorLength]);
10840             linear.addColorStop(1, color);
10841             ctx.fillStyle = linear;
10842           }
10843           if(horz) {
10844             ctx.fillRect(x + acum, y, dimArray[i], height);
10845           } else {
10846             ctx.fillRect(x, y - acum - dimArray[i], width, dimArray[i]);
10847           }
10848           if(border && border.name == stringArray[i]) {
10849             opt.acum = acum;
10850             opt.dimValue = dimArray[i];
10851           }
10852           acum += (dimArray[i] || 0);
10853           valAcum += (valueArray[i] || 0);
10854         }
10855         if(border) {
10856           ctx.save();
10857           ctx.lineWidth = 2;
10858           ctx.strokeStyle = border.color;
10859           if(horz) {
10860             ctx.strokeRect(x + opt.acum + 1, y + 1, opt.dimValue -2, height - 2);
10861           } else {
10862             ctx.strokeRect(x + 1, y - opt.acum - opt.dimValue + 1, width -2, opt.dimValue -2);
10863           }
10864           ctx.restore();
10865         }
10866         if(label.type == 'Native') {
10867           ctx.save();
10868           ctx.fillStyle = ctx.strokeStyle = label.color;
10869           ctx.font = label.style + ' ' + label.size + 'px ' + label.family;
10870           ctx.textBaseline = 'middle';
10871           var aggValue = aggregates(node.name, valAcum, node);
10872           if(aggValue !== false) {
10873             aggValue = aggValue !== true? aggValue : valAcum;
10874             if(horz) {
10875               ctx.textAlign = 'right';
10876               ctx.fillText(aggValue, x + acum - config.labelOffset, y + height/2);
10877             } else {
10878               ctx.textAlign = 'center';
10879               ctx.fillText(aggValue, x + width/2, y - height - label.size/2 - config.labelOffset);
10880             }
10881           }
10882           if(showLabels(node.name, valAcum, node)) {
10883             if(horz) {
10884               ctx.textAlign = 'center';
10885               ctx.translate(x - config.labelOffset - label.size/2, y + height/2);
10886               ctx.rotate(Math.PI / 2);
10887               ctx.fillText(node.name, 0, 0);
10888             } else {
10889               ctx.textAlign = 'center';
10890               ctx.fillText(node.name, x + width/2, y + label.size/2 + config.labelOffset);
10891             }
10892           }
10893           ctx.restore();
10894         }
10895       }
10896     },
10897     'contains': function(node, mpos) {
10898       var pos = node.pos.getc(true), 
10899           width = node.getData('width'),
10900           height = node.getData('height'),
10901           algnPos = this.getAlignedPos(pos, width, height),
10902           x = algnPos.x, y = algnPos.y,
10903           dimArray = node.getData('dimArray'),
10904           config = node.getData('config'),
10905           rx = mpos.x - x,
10906           horz = config.orientation == 'horizontal';
10907       //bounding box check
10908       if(horz) {
10909         if(mpos.x < x || mpos.x > x + width
10910             || mpos.y > y + height || mpos.y < y) {
10911             return false;
10912           }
10913       } else {
10914         if(mpos.x < x || mpos.x > x + width
10915             || mpos.y > y || mpos.y < y - height) {
10916             return false;
10917           }
10918       }
10919       //deep check
10920       for(var i=0, l=dimArray.length, acum=(horz? x:y); i<l; i++) {
10921         var dimi = dimArray[i];
10922         if(horz) {
10923           acum += dimi;
10924           var intersec = acum;
10925           if(mpos.x <= intersec) {
10926             return {
10927               'name': node.getData('stringArray')[i],
10928               'color': node.getData('colorArray')[i],
10929               'value': node.getData('valueArray')[i],
10930               'label': node.name
10931             };
10932           }
10933         } else {
10934           acum -= dimi;
10935           var intersec = acum;
10936           if(mpos.y >= intersec) {
10937             return {
10938               'name': node.getData('stringArray')[i],
10939               'color': node.getData('colorArray')[i],
10940               'value': node.getData('valueArray')[i],
10941               'label': node.name
10942             };
10943           }
10944         }
10945       }
10946       return false;
10947     }
10948   },
10949   'barchart-grouped' : {
10950     'render' : function(node, canvas) {
10951       var pos = node.pos.getc(true), 
10952           width = node.getData('width'),
10953           height = node.getData('height'),
10954           algnPos = this.getAlignedPos(pos, width, height),
10955           x = algnPos.x, y = algnPos.y,
10956           dimArray = node.getData('dimArray'),
10957           valueArray = node.getData('valueArray'),
10958           valueLength = valueArray.length,
10959           colorArray = node.getData('colorArray'),
10960           colorLength = colorArray.length,
10961           stringArray = node.getData('stringArray'); 
10962
10963       var ctx = canvas.getCtx(),
10964           opt = {},
10965           border = node.getData('border'),
10966           gradient = node.getData('gradient'),
10967           config = node.getData('config'),
10968           horz = config.orientation == 'horizontal',
10969           aggregates = config.showAggregates,
10970           showLabels = config.showLabels,
10971           label = config.Label,
10972           fixedDim = (horz? height : width) / valueLength;
10973       
10974       if (colorArray && dimArray && stringArray) {
10975         for (var i=0, l=valueLength, acum=0, valAcum=0; i<l; i++) {
10976           ctx.fillStyle = ctx.strokeStyle = colorArray[i % colorLength];
10977           if(gradient) {
10978             var linear;
10979             if(horz) {
10980               linear = ctx.createLinearGradient(x + dimArray[i]/2, y + fixedDim * i, 
10981                   x + dimArray[i]/2, y + fixedDim * (i + 1));
10982             } else {
10983               linear = ctx.createLinearGradient(x + fixedDim * i, y - dimArray[i]/2, 
10984                   x + fixedDim * (i + 1), y - dimArray[i]/2);
10985             }
10986             var color = $.rgbToHex($.map($.hexToRgb(colorArray[i % colorLength].slice(1)), 
10987                 function(v) { return (v * 0.5) >> 0; }));
10988             linear.addColorStop(0, color);
10989             linear.addColorStop(0.5, colorArray[i % colorLength]);
10990             linear.addColorStop(1, color);
10991             ctx.fillStyle = linear;
10992           }
10993           if(horz) {
10994             ctx.fillRect(x, y + fixedDim * i, dimArray[i], fixedDim);
10995           } else {
10996             ctx.fillRect(x + fixedDim * i, y - dimArray[i], fixedDim, dimArray[i]);
10997           }
10998           if(border && border.name == stringArray[i]) {
10999             opt.acum = fixedDim * i;
11000             opt.dimValue = dimArray[i];
11001           }
11002           acum += (dimArray[i] || 0);
11003           valAcum += (valueArray[i] || 0);
11004         }
11005         if(border) {
11006           ctx.save();
11007           ctx.lineWidth = 2;
11008           ctx.strokeStyle = border.color;
11009           if(horz) {
11010             ctx.strokeRect(x + 1, y + opt.acum + 1, opt.dimValue -2, fixedDim - 2);
11011           } else {
11012             ctx.strokeRect(x + opt.acum + 1, y - opt.dimValue + 1, fixedDim -2, opt.dimValue -2);
11013           }
11014           ctx.restore();
11015         }
11016         if(label.type == 'Native') {
11017           ctx.save();
11018           ctx.fillStyle = ctx.strokeStyle = label.color;
11019           ctx.font = label.style + ' ' + label.size + 'px ' + label.family;
11020           ctx.textBaseline = 'middle';
11021           var aggValue = aggregates(node.name, valAcum, node);
11022           if(aggValue !== false) {
11023             aggValue = aggValue !== true? aggValue : valAcum;
11024             if(horz) {
11025               ctx.textAlign = 'right';
11026               ctx.fillText(aggValue, x + Math.max.apply(null, dimArray) - config.labelOffset, y + height/2);
11027             } else {
11028               ctx.textAlign = 'center';
11029               ctx.fillText(aggValue, x + width/2, y - Math.max.apply(null, dimArray) - label.size/2 - config.labelOffset);
11030             }
11031           }
11032           if(showLabels(node.name, valAcum, node)) {
11033             if(horz) {
11034               ctx.textAlign = 'center';
11035               ctx.translate(x - config.labelOffset - label.size/2, y + height/2);
11036               ctx.rotate(Math.PI / 2);
11037               ctx.fillText(node.name, 0, 0);
11038             } else {
11039               ctx.textAlign = 'center';
11040               ctx.fillText(node.name, x + width/2, y + label.size/2 + config.labelOffset);
11041             }
11042           }
11043           ctx.restore();
11044         }
11045       }
11046     },
11047     'contains': function(node, mpos) {
11048       var pos = node.pos.getc(true), 
11049           width = node.getData('width'),
11050           height = node.getData('height'),
11051           algnPos = this.getAlignedPos(pos, width, height),
11052           x = algnPos.x, y = algnPos.y,
11053           dimArray = node.getData('dimArray'),
11054           len = dimArray.length,
11055           config = node.getData('config'),
11056           rx = mpos.x - x,
11057           horz = config.orientation == 'horizontal',
11058           fixedDim = (horz? height : width) / len;
11059       //bounding box check
11060       if(horz) {
11061         if(mpos.x < x || mpos.x > x + width
11062             || mpos.y > y + height || mpos.y < y) {
11063             return false;
11064           }
11065       } else {
11066         if(mpos.x < x || mpos.x > x + width
11067             || mpos.y > y || mpos.y < y - height) {
11068             return false;
11069           }
11070       }
11071       //deep check
11072       for(var i=0, l=dimArray.length; i<l; i++) {
11073         var dimi = dimArray[i];
11074         if(horz) {
11075           var limit = y + fixedDim * i;
11076           if(mpos.x <= x+ dimi && mpos.y >= limit && mpos.y <= limit + fixedDim) {
11077             return {
11078               'name': node.getData('stringArray')[i],
11079               'color': node.getData('colorArray')[i],
11080               'value': node.getData('valueArray')[i],
11081               'label': node.name
11082             };
11083           }
11084         } else {
11085           var limit = x + fixedDim * i;
11086           if(mpos.x >= limit && mpos.x <= limit + fixedDim && mpos.y >= y - dimi) {
11087             return {
11088               'name': node.getData('stringArray')[i],
11089               'color': node.getData('colorArray')[i],
11090               'value': node.getData('valueArray')[i],
11091               'label': node.name
11092             };
11093           }
11094         }
11095       }
11096       return false;
11097     }
11098   }
11099 });
11100
11101 /*
11102   Class: BarChart
11103   
11104   A visualization that displays stacked bar charts.
11105   
11106   Constructor Options:
11107   
11108   See <Options.BarChart>.
11109
11110 */
11111 $jit.BarChart = new Class({
11112   st: null,
11113   colors: ["#416D9C", "#70A35E", "#EBB056", "#C74243", "#83548B", "#909291", "#557EAA"],
11114   selected: {},
11115   busy: false,
11116   
11117   initialize: function(opt) {
11118     this.controller = this.config = 
11119       $.merge(Options("Canvas", "Margin", "Label", "BarChart"), {
11120         Label: { type: 'Native' }
11121       }, opt);
11122     //set functions for showLabels and showAggregates
11123     var showLabels = this.config.showLabels,
11124         typeLabels = $.type(showLabels),
11125         showAggregates = this.config.showAggregates,
11126         typeAggregates = $.type(showAggregates);
11127     this.config.showLabels = typeLabels == 'function'? showLabels : $.lambda(showLabels);
11128     this.config.showAggregates = typeAggregates == 'function'? showAggregates : $.lambda(showAggregates);
11129     
11130     this.initializeViz();
11131   },
11132   
11133   initializeViz: function() {
11134     var config = this.config, that = this;
11135     var nodeType = config.type.split(":")[0],
11136         horz = config.orientation == 'horizontal',
11137         nodeLabels = {};
11138     
11139     var delegate = new $jit.ST({
11140       injectInto: config.injectInto,
11141       width: config.width,
11142       height: config.height,
11143       orientation: horz? 'left' : 'bottom',
11144       levelDistance: 0,
11145       siblingOffset: config.barsOffset,
11146       subtreeOffset: 0,
11147       withLabels: config.Label.type != 'Native',      
11148       useCanvas: config.useCanvas,
11149       Label: {
11150         type: config.Label.type
11151       },
11152       Node: {
11153         overridable: true,
11154         type: 'barchart-' + nodeType,
11155         align: 'left',
11156         width: 1,
11157         height: 1
11158       },
11159       Edge: {
11160         type: 'none'
11161       },
11162       Tips: {
11163         enable: config.Tips.enable,
11164         type: 'Native',
11165         force: true,
11166         onShow: function(tip, node, contains) {
11167           var elem = contains;
11168           config.Tips.onShow(tip, elem, node);
11169         }
11170       },
11171       Events: {
11172         enable: true,
11173         type: 'Native',
11174         onClick: function(node, eventInfo, evt) {
11175           if(!config.Events.enable) return;
11176           var elem = eventInfo.getContains();
11177           config.Events.onClick(elem, eventInfo, evt);
11178         },
11179         onMouseMove: function(node, eventInfo, evt) {
11180           if(!config.hoveredColor) return;
11181           if(node) {
11182             var elem = eventInfo.getContains();
11183             that.select(node.id, elem.name, elem.index);
11184           } else {
11185             that.select(false, false, false);
11186           }
11187         }
11188       },
11189       onCreateLabel: function(domElement, node) {
11190         var labelConf = config.Label,
11191             valueArray = node.getData('valueArray'),
11192             acum = $.reduce(valueArray, function(x, y) { return x + y; }, 0);
11193         var nlbs = {
11194           wrapper: document.createElement('div'),
11195           aggregate: document.createElement('div'),
11196           label: document.createElement('div')
11197         };
11198         var wrapper = nlbs.wrapper,
11199             label = nlbs.label,
11200             aggregate = nlbs.aggregate,
11201             wrapperStyle = wrapper.style,
11202             labelStyle = label.style,
11203             aggregateStyle = aggregate.style;
11204         //store node labels
11205         nodeLabels[node.id] = nlbs;
11206         //append labels
11207         wrapper.appendChild(label);
11208         wrapper.appendChild(aggregate);
11209         if(!config.showLabels(node.name, acum, node)) {
11210           labelStyle.display = 'none';
11211         }
11212         if(!config.showAggregates(node.name, acum, node)) {
11213           aggregateStyle.display = 'none';
11214         }
11215         wrapperStyle.position = 'relative';
11216         wrapperStyle.overflow = 'visible';
11217         wrapperStyle.fontSize = labelConf.size + 'px';
11218         wrapperStyle.fontFamily = labelConf.family;
11219         wrapperStyle.color = labelConf.color;
11220         wrapperStyle.textAlign = 'center';
11221         aggregateStyle.position = labelStyle.position = 'absolute';
11222         
11223         domElement.style.width = node.getData('width') + 'px';
11224         domElement.style.height = node.getData('height') + 'px';
11225         aggregateStyle.left = labelStyle.left =  '0px';
11226
11227         label.innerHTML = node.name;
11228         
11229         domElement.appendChild(wrapper);
11230       },
11231       onPlaceLabel: function(domElement, node) {
11232         if(!nodeLabels[node.id]) return;
11233         var labels = nodeLabels[node.id],
11234             wrapperStyle = labels.wrapper.style,
11235             labelStyle = labels.label.style,
11236             aggregateStyle = labels.aggregate.style,
11237             grouped = config.type.split(':')[0] == 'grouped',
11238             horz = config.orientation == 'horizontal',
11239             dimArray = node.getData('dimArray'),
11240             valArray = node.getData('valueArray'),
11241             width = (grouped && horz)? Math.max.apply(null, dimArray) : node.getData('width'),
11242             height = (grouped && !horz)? Math.max.apply(null, dimArray) : node.getData('height'),
11243             font = parseInt(wrapperStyle.fontSize, 10),
11244             domStyle = domElement.style;
11245             
11246         
11247         if(dimArray && valArray) {
11248           wrapperStyle.width = aggregateStyle.width = labelStyle.width = domElement.style.width = width + 'px';
11249           for(var i=0, l=valArray.length, acum=0; i<l; i++) {
11250             if(dimArray[i] > 0) {
11251               acum+= valArray[i];
11252             }
11253           }
11254           if(config.showLabels(node.name, acum, node)) {
11255             labelStyle.display = '';
11256           } else {
11257             labelStyle.display = 'none';
11258           }
11259           var aggValue = config.showAggregates(node.name, acum, node);
11260           if(aggValue !== false) {
11261             aggregateStyle.display = '';
11262           } else {
11263             aggregateStyle.display = 'none';
11264           }
11265           if(config.orientation == 'horizontal') {
11266             aggregateStyle.textAlign = 'right';
11267             labelStyle.textAlign = 'left';
11268             labelStyle.textIndex = aggregateStyle.textIndent = config.labelOffset + 'px';
11269             aggregateStyle.top = labelStyle.top = (height-font)/2 + 'px';
11270             domElement.style.height = wrapperStyle.height = height + 'px';
11271           } else {
11272             aggregateStyle.top = (-font - config.labelOffset) + 'px';
11273             labelStyle.top = (config.labelOffset + height) + 'px';
11274             domElement.style.top = parseInt(domElement.style.top, 10) - height + 'px';
11275             domElement.style.height = wrapperStyle.height = height + 'px';
11276           }
11277           labels.aggregate.innerHTML = aggValue !== true? aggValue : acum;
11278         }
11279       }
11280     });
11281     
11282     var size = delegate.canvas.getSize(),
11283         margin = config.Margin;
11284     if(horz) {
11285       delegate.config.offsetX = size.width/2 - margin.left
11286         - (config.showLabels && (config.labelOffset + config.Label.size));    
11287       delegate.config.offsetY = (margin.bottom - margin.top)/2;
11288     } else {
11289       delegate.config.offsetY = -size.height/2 + margin.bottom 
11290         + (config.showLabels && (config.labelOffset + config.Label.size));
11291       delegate.config.offsetX = (margin.right - margin.left)/2;
11292     }
11293     this.delegate = delegate;
11294     this.canvas = this.delegate.canvas;
11295   },
11296   
11297   /*
11298     Method: loadJSON
11299    
11300     Loads JSON data into the visualization. 
11301     
11302     Parameters:
11303     
11304     json - The JSON data format. This format is described in <http://blog.thejit.org/2010/04/24/new-javascript-infovis-toolkit-visualizations/#json-data-format>.
11305     
11306     Example:
11307     (start code js)
11308     var barChart = new $jit.BarChart(options);
11309     barChart.loadJSON(json);
11310     (end code)
11311  */  
11312   loadJSON: function(json) {
11313     if(this.busy) return;
11314     this.busy = true;
11315     
11316     var prefix = $.time(), 
11317         ch = [], 
11318         delegate = this.delegate,
11319         name = $.splat(json.label), 
11320         color = $.splat(json.color || this.colors),
11321         config = this.config,
11322         gradient = !!config.type.split(":")[1],
11323         animate = config.animate,
11324         horz = config.orientation == 'horizontal',
11325         that = this;
11326     
11327     for(var i=0, values=json.values, l=values.length; i<l; i++) {
11328       var val = values[i]
11329       var valArray = $.splat(values[i].values);
11330       var acum = 0;
11331       ch.push({
11332         'id': prefix + val.label,
11333         'name': val.label,
11334         'data': {
11335           'value': valArray,
11336           '$valueArray': valArray,
11337           '$colorArray': color,
11338           '$stringArray': name,
11339           '$gradient': gradient,
11340           '$config': config
11341         },
11342         'children': []
11343       });
11344     }
11345     var root = {
11346       'id': prefix + '$root',
11347       'name': '',
11348       'data': {
11349         '$type': 'none',
11350         '$width': 1,
11351         '$height': 1
11352       },
11353       'children': ch
11354     };
11355     delegate.loadJSON(root);
11356     
11357     this.normalizeDims();
11358     delegate.compute();
11359     delegate.select(delegate.root);
11360     if(animate) {
11361       if(horz) {
11362         delegate.fx.animate({
11363           modes: ['node-property:width:dimArray'],
11364           duration:1500,
11365           onComplete: function() {
11366             that.busy = false;
11367           }
11368         });
11369       } else {
11370         delegate.fx.animate({
11371           modes: ['node-property:height:dimArray'],
11372           duration:1500,
11373           onComplete: function() {
11374             that.busy = false;
11375           }
11376         });
11377       }
11378     } else {
11379       this.busy = false;
11380     }
11381   },
11382   
11383   /*
11384     Method: updateJSON
11385    
11386     Use this method when updating values for the current JSON data. If the items specified by the JSON data already exist in the graph then their values will be updated.
11387     
11388     Parameters:
11389     
11390     json - (object) JSON data to be updated. The JSON format corresponds to the one described in <BarChart.loadJSON>.
11391     onComplete - (object) A callback object to be called when the animation transition when updating the data end.
11392     
11393     Example:
11394     
11395     (start code js)
11396     barChart.updateJSON(json, {
11397       onComplete: function() {
11398         alert('update complete!');
11399       }
11400     });
11401     (end code)
11402  */  
11403   updateJSON: function(json, onComplete) {
11404     if(this.busy) return;
11405     this.busy = true;
11406     this.select(false, false, false);
11407     var delegate = this.delegate;
11408     var graph = delegate.graph;
11409     var values = json.values;
11410     var animate = this.config.animate;
11411     var that = this;
11412     var horz = this.config.orientation == 'horizontal';
11413     $.each(values, function(v) {
11414       var n = graph.getByName(v.label);
11415       if(n) {
11416         n.setData('valueArray', $.splat(v.values));
11417         if(json.label) {
11418           n.setData('stringArray', $.splat(json.label));
11419         }
11420       }
11421     });
11422     this.normalizeDims();
11423     delegate.compute();
11424     delegate.select(delegate.root);
11425     if(animate) {
11426       if(horz) {
11427         delegate.fx.animate({
11428           modes: ['node-property:width:dimArray'],
11429           duration:1500,
11430           onComplete: function() {
11431             that.busy = false;
11432             onComplete && onComplete.onComplete();
11433           }
11434         });
11435       } else {
11436         delegate.fx.animate({
11437           modes: ['node-property:height:dimArray'],
11438           duration:1500,
11439           onComplete: function() {
11440             that.busy = false;
11441             onComplete && onComplete.onComplete();
11442           }
11443         });
11444       }
11445     }
11446   },
11447   
11448   //adds the little brown bar when hovering the node
11449   select: function(id, name) {
11450     if(!this.config.hoveredColor) return;
11451     var s = this.selected;
11452     if(s.id != id || s.name != name) {
11453       s.id = id;
11454       s.name = name;
11455       s.color = this.config.hoveredColor;
11456       this.delegate.graph.eachNode(function(n) {
11457         if(id == n.id) {
11458           n.setData('border', s);
11459         } else {
11460           n.setData('border', false);
11461         }
11462       });
11463       this.delegate.plot();
11464     }
11465   },
11466   
11467   /*
11468     Method: getLegend
11469    
11470     Returns an object containing as keys the legend names and as values hex strings with color values.
11471     
11472     Example:
11473     
11474     (start code js)
11475     var legend = barChart.getLegend();
11476     (end code)
11477   */  
11478   getLegend: function() {
11479     var legend = {};
11480     var n;
11481     this.delegate.graph.getNode(this.delegate.root).eachAdjacency(function(adj) {
11482       n = adj.nodeTo;
11483     });
11484     var colors = n.getData('colorArray'),
11485         len = colors.length;
11486     $.each(n.getData('stringArray'), function(s, i) {
11487       legend[s] = colors[i % len];
11488     });
11489     return legend;
11490   },
11491   
11492   /*
11493     Method: getMaxValue
11494    
11495     Returns the maximum accumulated value for the stacks. This method is used for normalizing the graph heights according to the canvas height.
11496     
11497     Example:
11498     
11499     (start code js)
11500     var ans = barChart.getMaxValue();
11501     (end code)
11502     
11503     In some cases it could be useful to override this method to normalize heights for a group of BarCharts, like when doing small multiples.
11504     
11505     Example:
11506     
11507     (start code js)
11508     //will return 100 for all BarChart instances,
11509     //displaying all of them with the same scale
11510     $jit.BarChart.implement({
11511       'getMaxValue': function() {
11512         return 100;
11513       }
11514     });
11515     (end code)
11516     
11517   */  
11518   getMaxValue: function() {
11519     var maxValue = 0, stacked = this.config.type.split(':')[0] == 'stacked';
11520     this.delegate.graph.eachNode(function(n) {
11521       var valArray = n.getData('valueArray'),
11522           acum = 0;
11523       if(!valArray) return;
11524       if(stacked) {
11525         $.each(valArray, function(v) { 
11526           acum += +v;
11527         });
11528       } else {
11529         acum = Math.max.apply(null, valArray);
11530       }
11531       maxValue = maxValue>acum? maxValue:acum;
11532     });
11533     return maxValue;
11534   },
11535   
11536   setBarType: function(type) {
11537     this.config.type = type;
11538     this.delegate.config.Node.type = 'barchart-' + type.split(':')[0];
11539   },
11540   
11541   normalizeDims: function() {
11542     //number of elements
11543     var root = this.delegate.graph.getNode(this.delegate.root), l=0;
11544     root.eachAdjacency(function() {
11545       l++;
11546     });
11547     var maxValue = this.getMaxValue() || 1,
11548         size = this.delegate.canvas.getSize(),
11549         config = this.config,
11550         margin = config.Margin,
11551         marginWidth = margin.left + margin.right,
11552         marginHeight = margin.top + margin.bottom,
11553         horz = config.orientation == 'horizontal',
11554         fixedDim = (size[horz? 'height':'width'] - (horz? marginHeight:marginWidth) - (l -1) * config.barsOffset) / l,
11555         animate = config.animate,
11556         height = size[horz? 'width':'height'] - (horz? marginWidth:marginHeight) 
11557           - (!horz && config.showAggregates && (config.Label.size + config.labelOffset))
11558           - (config.showLabels && (config.Label.size + config.labelOffset)),
11559         dim1 = horz? 'height':'width',
11560         dim2 = horz? 'width':'height';
11561     this.delegate.graph.eachNode(function(n) {
11562       var acum = 0, animateValue = [];
11563       $.each(n.getData('valueArray'), function(v) {
11564         acum += +v;
11565         animateValue.push(0);
11566       });
11567       n.setData(dim1, fixedDim);
11568       if(animate) {
11569         n.setData(dim2, acum * height / maxValue, 'end');
11570         n.setData('dimArray', $.map(n.getData('valueArray'), function(n) { 
11571           return n * height / maxValue; 
11572         }), 'end');
11573         var dimArray = n.getData('dimArray');
11574         if(!dimArray) {
11575           n.setData('dimArray', animateValue);
11576         }
11577       } else {
11578         n.setData(dim2, acum * height / maxValue);
11579         n.setData('dimArray', $.map(n.getData('valueArray'), function(n) { 
11580           return n * height / maxValue; 
11581         }));
11582       }
11583     });
11584   }
11585 });
11586
11587
11588 /*
11589  * File: Options.PieChart.js
11590  *
11591 */
11592 /*
11593   Object: Options.PieChart
11594   
11595   <PieChart> options. 
11596   Other options included in the PieChart are <Options.Canvas>, <Options.Label>, <Options.Tips> and <Options.Events>.
11597   
11598   Syntax:
11599   
11600   (start code js)
11601
11602   Options.PieChart = {
11603     animate: true,
11604     offset: 25,
11605     sliceOffset:0,
11606     labelOffset: 3,
11607     type: 'stacked',
11608     hoveredColor: '#9fd4ff',
11609     showLabels: true,
11610     resizeLabels: false,
11611     updateHeights: false
11612   };  
11613
11614   (end code)
11615   
11616   Example:
11617   
11618   (start code js)
11619
11620   var pie = new $jit.PieChart({
11621     animate: true,
11622     sliceOffset: 5,
11623     type: 'stacked:gradient'
11624   });  
11625
11626   (end code)
11627   
11628   Parameters:
11629   
11630   animate - (boolean) Default's *true*. Whether to add animated transitions when plotting/updating the visualization.
11631   offset - (number) Default's *25*. Adds margin between the visualization and the canvas.
11632   sliceOffset - (number) Default's *0*. Separation between the center of the canvas and each pie slice.
11633   labelOffset - (number) Default's *3*. Adds margin between the label and the default place where it should be drawn.
11634   type - (string) Default's *'stacked'*. Stack style. Posible values are 'stacked', 'stacked:gradient' to add gradients.
11635   hoveredColor - (boolean|string) Default's *'#9fd4ff'*. Sets the selected color for a hovered pie stack.
11636   showLabels - (boolean) Default's *true*. Display the name of the slots.
11637   resizeLabels - (boolean|number) Default's *false*. Resize the pie labels according to their stacked values. Set a number for *resizeLabels* to set a font size minimum.
11638   updateHeights - (boolean) Default's *false*. Only for mono-valued (most common) pie charts. Resize the height of the pie slices according to their current values.
11639
11640 */
11641 Options.PieChart = {
11642   $extend: true,
11643
11644   animate: true,
11645   offset: 25, // page offset
11646   sliceOffset:0,
11647   labelOffset: 3, // label offset
11648   type: 'stacked', // gradient
11649   hoveredColor: '#9fd4ff',
11650   Events: {
11651     enable: false,
11652     onClick: $.empty
11653   },
11654   Tips: {
11655     enable: false,
11656     onShow: $.empty,
11657     onHide: $.empty
11658   },
11659   showLabels: true,
11660   resizeLabels: false,
11661   
11662   //only valid for mono-valued datasets
11663   updateHeights: false
11664 };
11665
11666 /*
11667  * Class: Layouts.Radial
11668  * 
11669  * Implements a Radial Layout.
11670  * 
11671  * Implemented By:
11672  * 
11673  * <RGraph>, <Hypertree>
11674  * 
11675  */
11676 Layouts.Radial = new Class({
11677
11678   /*
11679    * Method: compute
11680    * 
11681    * Computes nodes' positions.
11682    * 
11683    * Parameters:
11684    * 
11685    * property - _optional_ A <Graph.Node> position property to store the new
11686    * positions. Possible values are 'pos', 'end' or 'start'.
11687    * 
11688    */
11689   compute : function(property) {
11690     var prop = $.splat(property || [ 'current', 'start', 'end' ]);
11691     NodeDim.compute(this.graph, prop, this.config);
11692     this.graph.computeLevels(this.root, 0, "ignore");
11693     var lengthFunc = this.createLevelDistanceFunc(); 
11694     this.computeAngularWidths(prop);
11695     this.computePositions(prop, lengthFunc);
11696   },
11697
11698   /*
11699    * computePositions
11700    * 
11701    * Performs the main algorithm for computing node positions.
11702    */
11703   computePositions : function(property, getLength) {
11704     var propArray = property;
11705     var graph = this.graph;
11706     var root = graph.getNode(this.root);
11707     var parent = this.parent;
11708     var config = this.config;
11709
11710     for ( var i=0, l=propArray.length; i < l; i++) {
11711       var pi = propArray[i];
11712       root.setPos($P(0, 0), pi);
11713       root.setData('span', Math.PI * 2, pi);
11714     }
11715
11716     root.angleSpan = {
11717       begin : 0,
11718       end : 2 * Math.PI
11719     };
11720
11721     graph.eachBFS(this.root, function(elem) {
11722       var angleSpan = elem.angleSpan.end - elem.angleSpan.begin;
11723       var angleInit = elem.angleSpan.begin;
11724       var len = getLength(elem);
11725       //Calculate the sum of all angular widths
11726       var totalAngularWidths = 0, subnodes = [], maxDim = {};
11727       elem.eachSubnode(function(sib) {
11728         totalAngularWidths += sib._treeAngularWidth;
11729         //get max dim
11730         for ( var i=0, l=propArray.length; i < l; i++) {
11731           var pi = propArray[i], dim = sib.getData('dim', pi);
11732           maxDim[pi] = (pi in maxDim)? (dim > maxDim[pi]? dim : maxDim[pi]) : dim;
11733         }
11734         subnodes.push(sib);
11735       }, "ignore");
11736       //Maintain children order
11737       //Second constraint for <http://bailando.sims.berkeley.edu/papers/infovis01.htm>
11738       if (parent && parent.id == elem.id && subnodes.length > 0
11739           && subnodes[0].dist) {
11740         subnodes.sort(function(a, b) {
11741           return (a.dist >= b.dist) - (a.dist <= b.dist);
11742         });
11743       }
11744       //Calculate nodes positions.
11745       for (var k = 0, ls=subnodes.length; k < ls; k++) {
11746         var child = subnodes[k];
11747         if (!child._flag) {
11748           var angleProportion = child._treeAngularWidth / totalAngularWidths * angleSpan;
11749           var theta = angleInit + angleProportion / 2;
11750
11751           for ( var i=0, l=propArray.length; i < l; i++) {
11752             var pi = propArray[i];
11753             child.setPos($P(theta, len), pi);
11754             child.setData('span', angleProportion, pi);
11755             child.setData('dim-quotient', child.getData('dim', pi) / maxDim[pi], pi);
11756           }
11757
11758           child.angleSpan = {
11759             begin : angleInit,
11760             end : angleInit + angleProportion
11761           };
11762           angleInit += angleProportion;
11763         }
11764       }
11765     }, "ignore");
11766   },
11767
11768   /*
11769    * Method: setAngularWidthForNodes
11770    * 
11771    * Sets nodes angular widths.
11772    */
11773   setAngularWidthForNodes : function(prop) {
11774     this.graph.eachBFS(this.root, function(elem, i) {
11775       var diamValue = elem.getData('angularWidth', prop[0]) || 5;
11776       elem._angularWidth = diamValue / i;
11777     }, "ignore");
11778   },
11779
11780   /*
11781    * Method: setSubtreesAngularWidth
11782    * 
11783    * Sets subtrees angular widths.
11784    */
11785   setSubtreesAngularWidth : function() {
11786     var that = this;
11787     this.graph.eachNode(function(elem) {
11788       that.setSubtreeAngularWidth(elem);
11789     }, "ignore");
11790   },
11791
11792   /*
11793    * Method: setSubtreeAngularWidth
11794    * 
11795    * Sets the angular width for a subtree.
11796    */
11797   setSubtreeAngularWidth : function(elem) {
11798     var that = this, nodeAW = elem._angularWidth, sumAW = 0;
11799     elem.eachSubnode(function(child) {
11800       that.setSubtreeAngularWidth(child);
11801       sumAW += child._treeAngularWidth;
11802     }, "ignore");
11803     elem._treeAngularWidth = Math.max(nodeAW, sumAW);
11804   },
11805
11806   /*
11807    * Method: computeAngularWidths
11808    * 
11809    * Computes nodes and subtrees angular widths.
11810    */
11811   computeAngularWidths : function(prop) {
11812     this.setAngularWidthForNodes(prop);
11813     this.setSubtreesAngularWidth();
11814   }
11815
11816 });
11817
11818
11819 /*
11820  * File: Sunburst.js
11821  */
11822
11823 /*
11824    Class: Sunburst
11825       
11826    A radial space filling tree visualization.
11827    
11828    Inspired by:
11829  
11830    Sunburst <http://www.cc.gatech.edu/gvu/ii/sunburst/>.
11831    
11832    Note:
11833    
11834    This visualization was built and engineered from scratch, taking only the paper as inspiration, and only shares some features with the visualization described in the paper.
11835    
11836   Implements:
11837   
11838   All <Loader> methods
11839   
11840    Constructor Options:
11841    
11842    Inherits options from
11843    
11844    - <Options.Canvas>
11845    - <Options.Controller>
11846    - <Options.Node>
11847    - <Options.Edge>
11848    - <Options.Label>
11849    - <Options.Events>
11850    - <Options.Tips>
11851    - <Options.NodeStyles>
11852    - <Options.Navigation>
11853    
11854    Additionally, there are other parameters and some default values changed
11855    
11856    interpolation - (string) Default's *linear*. Describes the way nodes are interpolated. Possible values are 'linear' and 'polar'.
11857    levelDistance - (number) Default's *100*. The distance between levels of the tree. 
11858    Node.type - Described in <Options.Node>. Default's to *multipie*.
11859    Node.height - Described in <Options.Node>. Default's *0*.
11860    Edge.type - Described in <Options.Edge>. Default's *none*.
11861    Label.textAlign - Described in <Options.Label>. Default's *start*.
11862    Label.textBaseline - Described in <Options.Label>. Default's *middle*.
11863      
11864    Instance Properties:
11865
11866    canvas - Access a <Canvas> instance.
11867    graph - Access a <Graph> instance.
11868    op - Access a <Sunburst.Op> instance.
11869    fx - Access a <Sunburst.Plot> instance.
11870    labels - Access a <Sunburst.Label> interface implementation.   
11871
11872 */
11873
11874 $jit.Sunburst = new Class({
11875
11876   Implements: [ Loader, Extras, Layouts.Radial ],
11877
11878   initialize: function(controller) {
11879     var $Sunburst = $jit.Sunburst;
11880
11881     var config = {
11882       interpolation: 'linear',
11883       levelDistance: 100,
11884       Node: {
11885         'type': 'multipie',
11886         'height':0
11887       },
11888       Edge: {
11889         'type': 'none'
11890       },
11891       Label: {
11892         textAlign: 'start',
11893         textBaseline: 'middle'
11894       }
11895     };
11896
11897     this.controller = this.config = $.merge(Options("Canvas", "Node", "Edge",
11898         "Fx", "Tips", "NodeStyles", "Events", "Navigation", "Controller", "Label"), config, controller);
11899
11900     var canvasConfig = this.config;
11901     if(canvasConfig.useCanvas) {
11902       this.canvas = canvasConfig.useCanvas;
11903       this.config.labelContainer = this.canvas.id + '-label';
11904     } else {
11905       if(canvasConfig.background) {
11906         canvasConfig.background = $.merge({
11907           type: 'Circles'
11908         }, canvasConfig.background);
11909       }
11910       this.canvas = new Canvas(this, canvasConfig);
11911       this.config.labelContainer = (typeof canvasConfig.injectInto == 'string'? canvasConfig.injectInto : canvasConfig.injectInto.id) + '-label';
11912     }
11913
11914     this.graphOptions = {
11915       'klass': Polar,
11916       'Node': {
11917         'selected': false,
11918         'exist': true,
11919         'drawn': true
11920       }
11921     };
11922     this.graph = new Graph(this.graphOptions, this.config.Node,
11923         this.config.Edge);
11924     this.labels = new $Sunburst.Label[canvasConfig.Label.type](this);
11925     this.fx = new $Sunburst.Plot(this, $Sunburst);
11926     this.op = new $Sunburst.Op(this);
11927     this.json = null;
11928     this.root = null;
11929     this.rotated = null;
11930     this.busy = false;
11931     // initialize extras
11932     this.initializeExtras();
11933   },
11934
11935   /* 
11936   
11937     createLevelDistanceFunc 
11938   
11939     Returns the levelDistance function used for calculating a node distance 
11940     to its origin. This function returns a function that is computed 
11941     per level and not per node, such that all nodes with the same depth will have the 
11942     same distance to the origin. The resulting function gets the 
11943     parent node as parameter and returns a float.
11944
11945    */
11946   createLevelDistanceFunc: function() {
11947     var ld = this.config.levelDistance;
11948     return function(elem) {
11949       return (elem._depth + 1) * ld;
11950     };
11951   },
11952
11953   /* 
11954      Method: refresh 
11955      
11956      Computes positions and plots the tree.
11957
11958    */
11959   refresh: function() {
11960     this.compute();
11961     this.plot();
11962   },
11963
11964   /*
11965    reposition
11966   
11967    An alias for computing new positions to _endPos_
11968
11969    See also:
11970
11971    <Sunburst.compute>
11972    
11973   */
11974   reposition: function() {
11975     this.compute('end');
11976   },
11977
11978   /*
11979   Method: rotate
11980   
11981   Rotates the graph so that the selected node is horizontal on the right.
11982
11983   Parameters:
11984   
11985   node - (object) A <Graph.Node>.
11986   method - (string) Whether to perform an animation or just replot the graph. Possible values are "replot" or "animate".
11987   opt - (object) Configuration options merged with this visualization configuration options.
11988   
11989   See also:
11990
11991   <Sunburst.rotateAngle>
11992   
11993   */
11994   rotate: function(node, method, opt) {
11995     var theta = node.getPos(opt.property || 'current').getp(true).theta;
11996     this.rotated = node;
11997     this.rotateAngle(-theta, method, opt);
11998   },
11999
12000   /*
12001   Method: rotateAngle
12002   
12003   Rotates the graph of an angle theta.
12004   
12005    Parameters:
12006    
12007    node - (object) A <Graph.Node>.
12008    method - (string) Whether to perform an animation or just replot the graph. Possible values are "replot" or "animate".
12009    opt - (object) Configuration options merged with this visualization configuration options.
12010    
12011    See also:
12012
12013    <Sunburst.rotate>
12014   
12015   */
12016   rotateAngle: function(theta, method, opt) {
12017     var that = this;
12018     var options = $.merge(this.config, opt || {}, {
12019       modes: [ 'polar' ]
12020     });
12021     var prop = opt.property || (method === "animate" ? 'end' : 'current');
12022     if(method === 'animate') {
12023       this.fx.animation.pause();
12024     }
12025     this.graph.eachNode(function(n) {
12026       var p = n.getPos(prop);
12027       p.theta += theta;
12028       if (p.theta < 0) {
12029         p.theta += Math.PI * 2;
12030       }
12031     });
12032     if (method == 'animate') {
12033       this.fx.animate(options);
12034     } else if (method == 'replot') {
12035       this.fx.plot();
12036       this.busy = false;
12037     }
12038   },
12039
12040   /*
12041    Method: plot
12042   
12043    Plots the Sunburst. This is a shortcut to *fx.plot*.
12044   */
12045   plot: function() {
12046     this.fx.plot();
12047   }
12048 });
12049
12050 $jit.Sunburst.$extend = true;
12051
12052 (function(Sunburst) {
12053
12054   /*
12055      Class: Sunburst.Op
12056
12057      Custom extension of <Graph.Op>.
12058
12059      Extends:
12060
12061      All <Graph.Op> methods
12062      
12063      See also:
12064      
12065      <Graph.Op>
12066
12067   */
12068   Sunburst.Op = new Class( {
12069
12070     Implements: Graph.Op
12071
12072   });
12073
12074   /*
12075      Class: Sunburst.Plot
12076
12077     Custom extension of <Graph.Plot>.
12078   
12079     Extends:
12080   
12081     All <Graph.Plot> methods
12082     
12083     See also:
12084     
12085     <Graph.Plot>
12086   
12087   */
12088   Sunburst.Plot = new Class( {
12089
12090     Implements: Graph.Plot
12091
12092   });
12093
12094   /*
12095     Class: Sunburst.Label
12096
12097     Custom extension of <Graph.Label>. 
12098     Contains custom <Graph.Label.SVG>, <Graph.Label.HTML> and <Graph.Label.Native> extensions.
12099   
12100     Extends:
12101   
12102     All <Graph.Label> methods and subclasses.
12103   
12104     See also:
12105   
12106     <Graph.Label>, <Graph.Label.Native>, <Graph.Label.HTML>, <Graph.Label.SVG>.
12107   
12108    */
12109   Sunburst.Label = {};
12110
12111   /*
12112      Sunburst.Label.Native
12113
12114      Custom extension of <Graph.Label.Native>.
12115
12116      Extends:
12117
12118      All <Graph.Label.Native> methods
12119
12120      See also:
12121
12122      <Graph.Label.Native>
12123   */
12124   Sunburst.Label.Native = new Class( {
12125     Implements: Graph.Label.Native,
12126
12127     initialize: function(viz) {
12128       this.viz = viz;
12129       this.label = viz.config.Label;
12130       this.config = viz.config;
12131     },
12132
12133     renderLabel: function(canvas, node, controller) {
12134       var span = node.getData('span');
12135       if(span < Math.PI /2 && Math.tan(span) * 
12136           this.config.levelDistance * node._depth < 10) {
12137         return;
12138       }
12139       var ctx = canvas.getCtx();
12140       var measure = ctx.measureText(node.name);
12141       if (node.id == this.viz.root) {
12142         var x = -measure.width / 2, y = 0, thetap = 0;
12143         var ld = 0;
12144       } else {
12145         var indent = 5;
12146         var ld = controller.levelDistance - indent;
12147         var clone = node.pos.clone();
12148         clone.rho += indent;
12149         var p = clone.getp(true);
12150         var ct = clone.getc(true);
12151         var x = ct.x, y = ct.y;
12152         // get angle in degrees
12153         var pi = Math.PI;
12154         var cond = (p.theta > pi / 2 && p.theta < 3 * pi / 2);
12155         var thetap = cond ? p.theta + pi : p.theta;
12156         if (cond) {
12157           x -= Math.abs(Math.cos(p.theta) * measure.width);
12158           y += Math.sin(p.theta) * measure.width;
12159         } else if (node.id == this.viz.root) {
12160           x -= measure.width / 2;
12161         }
12162       }
12163       ctx.save();
12164       ctx.translate(x, y);
12165       ctx.rotate(thetap);
12166       ctx.fillText(node.name, 0, 0);
12167       ctx.restore();
12168     }
12169   });
12170
12171   /*
12172      Sunburst.Label.SVG
12173
12174     Custom extension of <Graph.Label.SVG>.
12175   
12176     Extends:
12177   
12178     All <Graph.Label.SVG> methods
12179   
12180     See also:
12181   
12182     <Graph.Label.SVG>
12183   
12184   */
12185   Sunburst.Label.SVG = new Class( {
12186     Implements: Graph.Label.SVG,
12187
12188     initialize: function(viz) {
12189       this.viz = viz;
12190     },
12191
12192     /* 
12193        placeLabel
12194
12195        Overrides abstract method placeLabel in <Graph.Plot>.
12196
12197        Parameters:
12198
12199        tag - A DOM label element.
12200        node - A <Graph.Node>.
12201        controller - A configuration/controller object passed to the visualization.
12202       
12203      */
12204     placeLabel: function(tag, node, controller) {
12205       var pos = node.pos.getc(true), viz = this.viz, canvas = this.viz.canvas;
12206       var radius = canvas.getSize();
12207       var labelPos = {
12208         x: Math.round(pos.x + radius.width / 2),
12209         y: Math.round(pos.y + radius.height / 2)
12210       };
12211       tag.setAttribute('x', labelPos.x);
12212       tag.setAttribute('y', labelPos.y);
12213
12214       var bb = tag.getBBox();
12215       if (bb) {
12216         // center the label
12217     var x = tag.getAttribute('x');
12218     var y = tag.getAttribute('y');
12219     // get polar coordinates
12220     var p = node.pos.getp(true);
12221     // get angle in degrees
12222     var pi = Math.PI;
12223     var cond = (p.theta > pi / 2 && p.theta < 3 * pi / 2);
12224     if (cond) {
12225       tag.setAttribute('x', x - bb.width);
12226       tag.setAttribute('y', y - bb.height);
12227     } else if (node.id == viz.root) {
12228       tag.setAttribute('x', x - bb.width / 2);
12229     }
12230
12231     var thetap = cond ? p.theta + pi : p.theta;
12232     if(node._depth)
12233       tag.setAttribute('transform', 'rotate(' + thetap * 360 / (2 * pi) + ' ' + x
12234           + ' ' + y + ')');
12235   }
12236
12237   controller.onPlaceLabel(tag, node);
12238 }
12239   });
12240
12241   /*
12242      Sunburst.Label.HTML
12243
12244      Custom extension of <Graph.Label.HTML>.
12245
12246      Extends:
12247
12248      All <Graph.Label.HTML> methods.
12249
12250      See also:
12251
12252      <Graph.Label.HTML>
12253
12254   */
12255   Sunburst.Label.HTML = new Class( {
12256     Implements: Graph.Label.HTML,
12257
12258     initialize: function(viz) {
12259       this.viz = viz;
12260     },
12261     /* 
12262        placeLabel
12263
12264        Overrides abstract method placeLabel in <Graph.Plot>.
12265
12266        Parameters:
12267
12268        tag - A DOM label element.
12269        node - A <Graph.Node>.
12270        controller - A configuration/controller object passed to the visualization.
12271       
12272      */
12273     placeLabel: function(tag, node, controller) {
12274       var pos = node.pos.clone(), 
12275           canvas = this.viz.canvas,
12276           height = node.getData('height'),
12277           ldist = ((height || node._depth == 0)? height : this.viz.config.levelDistance) /2,
12278           radius = canvas.getSize();
12279       pos.rho += ldist;
12280       pos = pos.getc(true);
12281       
12282       var labelPos = {
12283         x: Math.round(pos.x + radius.width / 2),
12284         y: Math.round(pos.y + radius.height / 2)
12285       };
12286
12287       var style = tag.style;
12288       style.left = labelPos.x + 'px';
12289       style.top = labelPos.y + 'px';
12290       style.display = this.fitsInCanvas(labelPos, canvas) ? '' : 'none';
12291
12292       controller.onPlaceLabel(tag, node);
12293     }
12294   });
12295
12296   /*
12297     Class: Sunburst.Plot.NodeTypes
12298
12299     This class contains a list of <Graph.Node> built-in types. 
12300     Node types implemented are 'none', 'pie', 'multipie', 'gradient-pie' and 'gradient-multipie'.
12301
12302     You can add your custom node types, customizing your visualization to the extreme.
12303
12304     Example:
12305
12306     (start code js)
12307       Sunburst.Plot.NodeTypes.implement({
12308         'mySpecialType': {
12309           'render': function(node, canvas) {
12310             //print your custom node to canvas
12311           },
12312           //optional
12313           'contains': function(node, pos) {
12314             //return true if pos is inside the node or false otherwise
12315           }
12316         }
12317       });
12318     (end code)
12319
12320   */
12321   Sunburst.Plot.NodeTypes = new Class( {
12322     'none': {
12323       'render': $.empty,
12324       'contains': $.lambda(false),
12325       'anglecontains': function(node, pos) {
12326         var span = node.getData('span') / 2, theta = node.pos.theta;
12327         var begin = theta - span, end = theta + span;
12328         if (begin < 0)
12329           begin += Math.PI * 2;
12330         var atan = Math.atan2(pos.y, pos.x);
12331         if (atan < 0)
12332           atan += Math.PI * 2;
12333         if (begin > end) {
12334           return (atan > begin && atan <= Math.PI * 2) || atan < end;
12335         } else {
12336           return atan > begin && atan < end;
12337         }
12338       }
12339     },
12340
12341     'pie': {
12342       'render': function(node, canvas) {
12343         var span = node.getData('span') / 2, theta = node.pos.theta;
12344         var begin = theta - span, end = theta + span;
12345         var polarNode = node.pos.getp(true);
12346         var polar = new Polar(polarNode.rho, begin);
12347         var p1coord = polar.getc(true);
12348         polar.theta = end;
12349         var p2coord = polar.getc(true);
12350
12351         var ctx = canvas.getCtx();
12352         ctx.beginPath();
12353         ctx.moveTo(0, 0);
12354         ctx.lineTo(p1coord.x, p1coord.y);
12355         ctx.moveTo(0, 0);
12356         ctx.lineTo(p2coord.x, p2coord.y);
12357         ctx.moveTo(0, 0);
12358         ctx.arc(0, 0, polarNode.rho * node.getData('dim-quotient'), begin, end,
12359             false);
12360         ctx.fill();
12361       },
12362       'contains': function(node, pos) {
12363         if (this.nodeTypes['none'].anglecontains.call(this, node, pos)) {
12364           var rho = Math.sqrt(pos.x * pos.x + pos.y * pos.y);
12365           var ld = this.config.levelDistance, d = node._depth;
12366           return (rho <= ld * d);
12367         }
12368         return false;
12369       }
12370     },
12371     'multipie': {
12372       'render': function(node, canvas) {
12373         var height = node.getData('height');
12374         var ldist = height? height : this.config.levelDistance;
12375         var span = node.getData('span') / 2, theta = node.pos.theta;
12376         var begin = theta - span, end = theta + span;
12377         var polarNode = node.pos.getp(true);
12378
12379         var polar = new Polar(polarNode.rho, begin);
12380         var p1coord = polar.getc(true);
12381
12382         polar.theta = end;
12383         var p2coord = polar.getc(true);
12384
12385         polar.rho += ldist;
12386         var p3coord = polar.getc(true);
12387
12388         polar.theta = begin;
12389         var p4coord = polar.getc(true);
12390
12391         var ctx = canvas.getCtx();
12392         ctx.moveTo(0, 0);
12393         ctx.beginPath();
12394         ctx.arc(0, 0, polarNode.rho, begin, end, false);
12395         ctx.arc(0, 0, polarNode.rho + ldist, end, begin, true);
12396         ctx.moveTo(p1coord.x, p1coord.y);
12397         ctx.lineTo(p4coord.x, p4coord.y);
12398         ctx.moveTo(p2coord.x, p2coord.y);
12399         ctx.lineTo(p3coord.x, p3coord.y);
12400         ctx.fill();
12401
12402         if (node.collapsed) {
12403           ctx.save();
12404           ctx.lineWidth = 2;
12405           ctx.moveTo(0, 0);
12406           ctx.beginPath();
12407           ctx.arc(0, 0, polarNode.rho + ldist + 5, end - 0.01, begin + 0.01,
12408               true);
12409           ctx.stroke();
12410           ctx.restore();
12411         }
12412       },
12413       'contains': function(node, pos) {
12414         if (this.nodeTypes['none'].anglecontains.call(this, node, pos)) {
12415           var rho = Math.sqrt(pos.x * pos.x + pos.y * pos.y);
12416           var height = node.getData('height');
12417           var ldist = height? height : this.config.levelDistance;
12418           var ld = this.config.levelDistance, d = node._depth;
12419           return (rho >= ld * d) && (rho <= (ld * d + ldist));
12420         }
12421         return false;
12422       }
12423     },
12424
12425     'gradient-multipie': {
12426       'render': function(node, canvas) {
12427         var ctx = canvas.getCtx();
12428         var height = node.getData('height');
12429         var ldist = height? height : this.config.levelDistance;
12430         var radialGradient = ctx.createRadialGradient(0, 0, node.getPos().rho,
12431             0, 0, node.getPos().rho + ldist);
12432
12433         var colorArray = $.hexToRgb(node.getData('color')), ans = [];
12434         $.each(colorArray, function(i) {
12435           ans.push(parseInt(i * 0.5, 10));
12436         });
12437         var endColor = $.rgbToHex(ans);
12438         radialGradient.addColorStop(0, endColor);
12439         radialGradient.addColorStop(1, node.getData('color'));
12440         ctx.fillStyle = radialGradient;
12441         this.nodeTypes['multipie'].render.call(this, node, canvas);
12442       },
12443       'contains': function(node, pos) {
12444         return this.nodeTypes['multipie'].contains.call(this, node, pos);
12445       }
12446     },
12447
12448     'gradient-pie': {
12449       'render': function(node, canvas) {
12450         var ctx = canvas.getCtx();
12451         var radialGradient = ctx.createRadialGradient(0, 0, 0, 0, 0, node
12452             .getPos().rho);
12453
12454         var colorArray = $.hexToRgb(node.getData('color')), ans = [];
12455         $.each(colorArray, function(i) {
12456           ans.push(parseInt(i * 0.5, 10));
12457         });
12458         var endColor = $.rgbToHex(ans);
12459         radialGradient.addColorStop(1, endColor);
12460         radialGradient.addColorStop(0, node.getData('color'));
12461         ctx.fillStyle = radialGradient;
12462         this.nodeTypes['pie'].render.call(this, node, canvas);
12463       },
12464       'contains': function(node, pos) {
12465         return this.nodeTypes['pie'].contains.call(this, node, pos);
12466       }
12467     }
12468   });
12469
12470   /*
12471     Class: Sunburst.Plot.EdgeTypes
12472
12473     This class contains a list of <Graph.Adjacence> built-in types. 
12474     Edge types implemented are 'none', 'line' and 'arrow'.
12475   
12476     You can add your custom edge types, customizing your visualization to the extreme.
12477   
12478     Example:
12479   
12480     (start code js)
12481       Sunburst.Plot.EdgeTypes.implement({
12482         'mySpecialType': {
12483           'render': function(adj, canvas) {
12484             //print your custom edge to canvas
12485           },
12486           //optional
12487           'contains': function(adj, pos) {
12488             //return true if pos is inside the arc or false otherwise
12489           }
12490         }
12491       });
12492     (end code)
12493   
12494   */
12495   Sunburst.Plot.EdgeTypes = new Class({
12496     'none': $.empty,
12497     'line': {
12498       'render': function(adj, canvas) {
12499         var from = adj.nodeFrom.pos.getc(true),
12500             to = adj.nodeTo.pos.getc(true);
12501         this.edgeHelper.line.render(from, to, canvas);
12502       },
12503       'contains': function(adj, pos) {
12504         var from = adj.nodeFrom.pos.getc(true),
12505             to = adj.nodeTo.pos.getc(true);
12506         return this.edgeHelper.line.contains(from, to, pos, this.edge.epsilon);
12507       }
12508     },
12509     'arrow': {
12510       'render': function(adj, canvas) {
12511         var from = adj.nodeFrom.pos.getc(true),
12512             to = adj.nodeTo.pos.getc(true),
12513             dim = adj.getData('dim'),
12514             direction = adj.data.$direction,
12515             inv = (direction && direction.length>1 && direction[0] != adj.nodeFrom.id);
12516         this.edgeHelper.arrow.render(from, to, dim, inv, canvas);
12517       },
12518       'contains': function(adj, pos) {
12519         var from = adj.nodeFrom.pos.getc(true),
12520             to = adj.nodeTo.pos.getc(true);
12521         return this.edgeHelper.arrow.contains(from, to, pos, this.edge.epsilon);
12522       }
12523     },
12524     'hyperline': {
12525       'render': function(adj, canvas) {
12526         var from = adj.nodeFrom.pos.getc(),
12527             to = adj.nodeTo.pos.getc(),
12528             dim = Math.max(from.norm(), to.norm());
12529         this.edgeHelper.hyperline.render(from.$scale(1/dim), to.$scale(1/dim), dim, canvas);
12530       },
12531       'contains': $.lambda(false) //TODO(nico): Implement this!
12532     }
12533   });
12534
12535 })($jit.Sunburst);
12536
12537
12538 /*
12539  * File: PieChart.js
12540  *
12541 */
12542
12543 $jit.Sunburst.Plot.NodeTypes.implement({
12544   'piechart-stacked' : {
12545     'render' : function(node, canvas) {
12546       var pos = node.pos.getp(true),
12547           dimArray = node.getData('dimArray'),
12548           valueArray = node.getData('valueArray'),
12549           colorArray = node.getData('colorArray'),
12550           colorLength = colorArray.length,
12551           stringArray = node.getData('stringArray'),
12552           span = node.getData('span') / 2,
12553           theta = node.pos.theta,
12554           begin = theta - span,
12555           end = theta + span,
12556           polar = new Polar;
12557     
12558       var ctx = canvas.getCtx(), 
12559           opt = {},
12560           gradient = node.getData('gradient'),
12561           border = node.getData('border'),
12562           config = node.getData('config'),
12563           showLabels = config.showLabels,
12564           resizeLabels = config.resizeLabels,
12565           label = config.Label;
12566
12567       var xpos = config.sliceOffset * Math.cos((begin + end) /2);
12568       var ypos = config.sliceOffset * Math.sin((begin + end) /2);
12569
12570       if (colorArray && dimArray && stringArray) {
12571         for (var i=0, l=dimArray.length, acum=0, valAcum=0; i<l; i++) {
12572           var dimi = dimArray[i], colori = colorArray[i % colorLength];
12573           if(dimi <= 0) continue;
12574           ctx.fillStyle = ctx.strokeStyle = colori;
12575           if(gradient && dimi) {
12576             var radialGradient = ctx.createRadialGradient(xpos, ypos, acum + config.sliceOffset,
12577                 xpos, ypos, acum + dimi + config.sliceOffset);
12578             var colorRgb = $.hexToRgb(colori), 
12579                 ans = $.map(colorRgb, function(i) { return (i * 0.8) >> 0; }),
12580                 endColor = $.rgbToHex(ans);
12581
12582             radialGradient.addColorStop(0, colori);
12583             radialGradient.addColorStop(0.5, colori);
12584             radialGradient.addColorStop(1, endColor);
12585             ctx.fillStyle = radialGradient;
12586           }
12587           
12588           polar.rho = acum + config.sliceOffset;
12589           polar.theta = begin;
12590           var p1coord = polar.getc(true);
12591           polar.theta = end;
12592           var p2coord = polar.getc(true);
12593           polar.rho += dimi;
12594           var p3coord = polar.getc(true);
12595           polar.theta = begin;
12596           var p4coord = polar.getc(true);
12597
12598           ctx.beginPath();
12599           //fixing FF arc method + fill
12600           ctx.arc(xpos, ypos, acum + .01, begin, end, false);
12601           ctx.arc(xpos, ypos, acum + dimi + .01, end, begin, true);
12602           ctx.fill();
12603           if(border && border.name == stringArray[i]) {
12604             opt.acum = acum;
12605             opt.dimValue = dimArray[i];
12606             opt.begin = begin;
12607             opt.end = end;
12608           }
12609           acum += (dimi || 0);
12610           valAcum += (valueArray[i] || 0);
12611         }
12612         if(border) {
12613           ctx.save();
12614           ctx.globalCompositeOperation = "source-over";
12615           ctx.lineWidth = 2;
12616           ctx.strokeStyle = border.color;
12617           var s = begin < end? 1 : -1;
12618           ctx.beginPath();
12619           //fixing FF arc method + fill
12620           ctx.arc(xpos, ypos, opt.acum + .01 + 1, opt.begin, opt.end, false);
12621           ctx.arc(xpos, ypos, opt.acum + opt.dimValue + .01 - 1, opt.end, opt.begin, true);
12622           ctx.closePath();
12623           ctx.stroke();
12624           ctx.restore();
12625         }
12626         if(showLabels && label.type == 'Native') {
12627           ctx.save();
12628           ctx.fillStyle = ctx.strokeStyle = label.color;
12629           var scale = resizeLabels? node.getData('normalizedDim') : 1,
12630               fontSize = (label.size * scale) >> 0;
12631           fontSize = fontSize < +resizeLabels? +resizeLabels : fontSize;
12632           
12633           ctx.font = label.style + ' ' + fontSize + 'px ' + label.family;
12634           ctx.textBaseline = 'middle';
12635           ctx.textAlign = 'center';
12636           
12637           polar.rho = acum + config.labelOffset + config.sliceOffset;
12638           polar.theta = node.pos.theta;
12639           var cart = polar.getc(true);
12640           
12641           ctx.fillText(node.name, cart.x, cart.y);
12642           ctx.restore();
12643         }
12644       }
12645     },
12646     'contains': function(node, pos) {
12647       if (this.nodeTypes['none'].anglecontains.call(this, node, pos)) {
12648         var rho = Math.sqrt(pos.x * pos.x + pos.y * pos.y);
12649         var ld = this.config.levelDistance, d = node._depth;
12650         var config = node.getData('config');
12651         if(rho <=ld * d + config.sliceOffset) {
12652           var dimArray = node.getData('dimArray');
12653           for(var i=0,l=dimArray.length,acum=config.sliceOffset; i<l; i++) {
12654             var dimi = dimArray[i];
12655             if(rho >= acum && rho <= acum + dimi) {
12656               return {
12657                 name: node.getData('stringArray')[i],
12658                 color: node.getData('colorArray')[i],
12659                 value: node.getData('valueArray')[i],
12660                 label: node.name
12661               };
12662             }
12663             acum += dimi;
12664           }
12665         }
12666         return false;
12667         
12668       }
12669       return false;
12670     }
12671   }
12672 });
12673
12674 /*
12675   Class: PieChart
12676   
12677   A visualization that displays stacked bar charts.
12678   
12679   Constructor Options:
12680   
12681   See <Options.PieChart>.
12682
12683 */
12684 $jit.PieChart = new Class({
12685   sb: null,
12686   colors: ["#416D9C", "#70A35E", "#EBB056", "#C74243", "#83548B", "#909291", "#557EAA"],
12687   selected: {},
12688   busy: false,
12689   
12690   initialize: function(opt) {
12691     this.controller = this.config = 
12692       $.merge(Options("Canvas", "PieChart", "Label"), {
12693         Label: { type: 'Native' }
12694       }, opt);
12695     this.initializeViz();
12696   },
12697   
12698   initializeViz: function() {
12699     var config = this.config, that = this;
12700     var nodeType = config.type.split(":")[0];
12701     var delegate = new $jit.Sunburst({
12702       injectInto: config.injectInto,
12703       width: config.width,
12704       height: config.height,
12705       useCanvas: config.useCanvas,
12706       withLabels: config.Label.type != 'Native',
12707       Label: {
12708         type: config.Label.type
12709       },
12710       Node: {
12711         overridable: true,
12712         type: 'piechart-' + nodeType,
12713         width: 1,
12714         height: 1
12715       },
12716       Edge: {
12717         type: 'none'
12718       },
12719       Tips: {
12720         enable: config.Tips.enable,
12721         type: 'Native',
12722         force: true,
12723         onShow: function(tip, node, contains) {
12724           var elem = contains;
12725           config.Tips.onShow(tip, elem, node);
12726         }
12727       },
12728       Events: {
12729         enable: true,
12730         type: 'Native',
12731         onClick: function(node, eventInfo, evt) {
12732           if(!config.Events.enable) return;
12733           var elem = eventInfo.getContains();
12734           config.Events.onClick(elem, eventInfo, evt);
12735         },
12736         onMouseMove: function(node, eventInfo, evt) {
12737           if(!config.hoveredColor) return;
12738           if(node) {
12739             var elem = eventInfo.getContains();
12740             that.select(node.id, elem.name, elem.index);
12741           } else {
12742             that.select(false, false, false);
12743           }
12744         }
12745       },
12746       onCreateLabel: function(domElement, node) {
12747         var labelConf = config.Label;
12748         if(config.showLabels) {
12749           var style = domElement.style;
12750           style.fontSize = labelConf.size + 'px';
12751           style.fontFamily = labelConf.family;
12752           style.color = labelConf.color;
12753           style.textAlign = 'center';
12754           domElement.innerHTML = node.name;
12755         }
12756       },
12757       onPlaceLabel: function(domElement, node) {
12758         if(!config.showLabels) return;
12759         var pos = node.pos.getp(true),
12760             dimArray = node.getData('dimArray'),
12761             span = node.getData('span') / 2,
12762             theta = node.pos.theta,
12763             begin = theta - span,
12764             end = theta + span,
12765             polar = new Polar;
12766       
12767         var showLabels = config.showLabels,
12768             resizeLabels = config.resizeLabels,
12769             label = config.Label;
12770         
12771         if (dimArray) {
12772           for (var i=0, l=dimArray.length, acum=0; i<l; i++) {
12773             acum += dimArray[i];
12774           }
12775           var scale = resizeLabels? node.getData('normalizedDim') : 1,
12776               fontSize = (label.size * scale) >> 0;
12777           fontSize = fontSize < +resizeLabels? +resizeLabels : fontSize;
12778           domElement.style.fontSize = fontSize + 'px';
12779           polar.rho = acum + config.labelOffset + config.sliceOffset;
12780           polar.theta = (begin + end) / 2;
12781           var pos = polar.getc(true);
12782           var radius = that.canvas.getSize();
12783           var labelPos = {
12784             x: Math.round(pos.x + radius.width / 2),
12785             y: Math.round(pos.y + radius.height / 2)
12786           };
12787           domElement.style.left = labelPos.x + 'px';
12788           domElement.style.top = labelPos.y + 'px';
12789         }
12790       }
12791     });
12792     
12793     var size = delegate.canvas.getSize(),
12794         min = Math.min;
12795     delegate.config.levelDistance = min(size.width, size.height)/2 
12796       - config.offset - config.sliceOffset;
12797     this.delegate = delegate;
12798     this.canvas = this.delegate.canvas;
12799     this.canvas.getCtx().globalCompositeOperation = 'lighter';
12800   },
12801   
12802   /*
12803     Method: loadJSON
12804    
12805     Loads JSON data into the visualization. 
12806     
12807     Parameters:
12808     
12809     json - The JSON data format. This format is described in <http://blog.thejit.org/2010/04/24/new-javascript-infovis-toolkit-visualizations/#json-data-format>.
12810     
12811     Example:
12812     (start code js)
12813     var pieChart = new $jit.PieChart(options);
12814     pieChart.loadJSON(json);
12815     (end code)
12816   */  
12817   loadJSON: function(json) {
12818     var prefix = $.time(), 
12819         ch = [], 
12820         delegate = this.delegate,
12821         name = $.splat(json.label),
12822         nameLength = name.length,
12823         color = $.splat(json.color || this.colors),
12824         colorLength = color.length,
12825         config = this.config,
12826         gradient = !!config.type.split(":")[1],
12827         animate = config.animate,
12828         mono = nameLength == 1;
12829     
12830     for(var i=0, values=json.values, l=values.length; i<l; i++) {
12831       var val = values[i];
12832       var valArray = $.splat(val.values);
12833       ch.push({
12834         'id': prefix + val.label,
12835         'name': val.label,
12836         'data': {
12837           'value': valArray,
12838           '$valueArray': valArray,
12839           '$colorArray': mono? $.splat(color[i % colorLength]) : color,
12840           '$stringArray': name,
12841           '$gradient': gradient,
12842           '$config': config,
12843           '$angularWidth': $.reduce(valArray, function(x,y){return x+y;})
12844         },
12845         'children': []
12846       });
12847     }
12848     var root = {
12849       'id': prefix + '$root',
12850       'name': '',
12851       'data': {
12852         '$type': 'none',
12853         '$width': 1,
12854         '$height': 1
12855       },
12856       'children': ch
12857     };
12858     delegate.loadJSON(root);
12859     
12860     this.normalizeDims();
12861     delegate.refresh();
12862     if(animate) {
12863       delegate.fx.animate({
12864         modes: ['node-property:dimArray'],
12865         duration:1500
12866       });
12867     }
12868   },
12869   
12870   /*
12871     Method: updateJSON
12872    
12873     Use this method when updating values for the current JSON data. If the items specified by the JSON data already exist in the graph then their values will be updated.
12874     
12875     Parameters:
12876     
12877     json - (object) JSON data to be updated. The JSON format corresponds to the one described in <PieChart.loadJSON>.
12878     onComplete - (object) A callback object to be called when the animation transition when updating the data end.
12879     
12880     Example:
12881     
12882     (start code js)
12883     pieChart.updateJSON(json, {
12884       onComplete: function() {
12885         alert('update complete!');
12886       }
12887     });
12888     (end code)
12889   */  
12890   updateJSON: function(json, onComplete) {
12891     if(this.busy) return;
12892     this.busy = true;
12893     
12894     var delegate = this.delegate;
12895     var graph = delegate.graph;
12896     var values = json.values;
12897     var animate = this.config.animate;
12898     var that = this;
12899     $.each(values, function(v) {
12900       var n = graph.getByName(v.label),
12901           vals = $.splat(v.values);
12902       if(n) {
12903         n.setData('valueArray', vals);
12904         n.setData('angularWidth', $.reduce(vals, function(x,y){return x+y;}));
12905         if(json.label) {
12906           n.setData('stringArray', $.splat(json.label));
12907         }
12908       }
12909     });
12910     this.normalizeDims();
12911     if(animate) {
12912       delegate.compute('end');
12913       delegate.fx.animate({
12914         modes: ['node-property:dimArray:span', 'linear'],
12915         duration:1500,
12916         onComplete: function() {
12917           that.busy = false;
12918           onComplete && onComplete.onComplete();
12919         }
12920       });
12921     } else {
12922       delegate.refresh();
12923     }
12924   },
12925     
12926   //adds the little brown bar when hovering the node
12927   select: function(id, name) {
12928     if(!this.config.hoveredColor) return;
12929     var s = this.selected;
12930     if(s.id != id || s.name != name) {
12931       s.id = id;
12932       s.name = name;
12933       s.color = this.config.hoveredColor;
12934       this.delegate.graph.eachNode(function(n) {
12935         if(id == n.id) {
12936           n.setData('border', s);
12937         } else {
12938           n.setData('border', false);
12939         }
12940       });
12941       this.delegate.plot();
12942     }
12943   },
12944   
12945   /*
12946     Method: getLegend
12947    
12948     Returns an object containing as keys the legend names and as values hex strings with color values.
12949     
12950     Example:
12951     
12952     (start code js)
12953     var legend = pieChart.getLegend();
12954     (end code)
12955   */  
12956   getLegend: function() {
12957     var legend = {};
12958     var n;
12959     this.delegate.graph.getNode(this.delegate.root).eachAdjacency(function(adj) {
12960       n = adj.nodeTo;
12961     });
12962     var colors = n.getData('colorArray'),
12963         len = colors.length;
12964     $.each(n.getData('stringArray'), function(s, i) {
12965       legend[s] = colors[i % len];
12966     });
12967     return legend;
12968   },
12969   
12970   /*
12971     Method: getMaxValue
12972    
12973     Returns the maximum accumulated value for the stacks. This method is used for normalizing the graph heights according to the canvas height.
12974     
12975     Example:
12976     
12977     (start code js)
12978     var ans = pieChart.getMaxValue();
12979     (end code)
12980     
12981     In some cases it could be useful to override this method to normalize heights for a group of PieCharts, like when doing small multiples.
12982     
12983     Example:
12984     
12985     (start code js)
12986     //will return 100 for all PieChart instances,
12987     //displaying all of them with the same scale
12988     $jit.PieChart.implement({
12989       'getMaxValue': function() {
12990         return 100;
12991       }
12992     });
12993     (end code)
12994     
12995   */  
12996   getMaxValue: function() {
12997     var maxValue = 0;
12998     this.delegate.graph.eachNode(function(n) {
12999       var valArray = n.getData('valueArray'),
13000           acum = 0;
13001       $.each(valArray, function(v) { 
13002         acum += +v;
13003       });
13004       maxValue = maxValue>acum? maxValue:acum;
13005     });
13006     return maxValue;
13007   },
13008   
13009   normalizeDims: function() {
13010     //number of elements
13011     var root = this.delegate.graph.getNode(this.delegate.root), l=0;
13012     root.eachAdjacency(function() {
13013       l++;
13014     });
13015     var maxValue = this.getMaxValue() || 1,
13016         config = this.config,
13017         animate = config.animate,
13018         rho = this.delegate.config.levelDistance;
13019     this.delegate.graph.eachNode(function(n) {
13020       var acum = 0, animateValue = [];
13021       $.each(n.getData('valueArray'), function(v) {
13022         acum += +v;
13023         animateValue.push(1);
13024       });
13025       var stat = (animateValue.length == 1) && !config.updateHeights;
13026       if(animate) {
13027         n.setData('dimArray', $.map(n.getData('valueArray'), function(n) { 
13028           return stat? rho: (n * rho / maxValue); 
13029         }), 'end');
13030         var dimArray = n.getData('dimArray');
13031         if(!dimArray) {
13032           n.setData('dimArray', animateValue);
13033         }
13034       } else {
13035         n.setData('dimArray', $.map(n.getData('valueArray'), function(n) { 
13036           return stat? rho : (n * rho / maxValue); 
13037         }));
13038       }
13039       n.setData('normalizedDim', acum / maxValue);
13040     });
13041   }
13042 });
13043
13044
13045 /*
13046  * Class: Layouts.TM
13047  * 
13048  * Implements TreeMaps layouts (SliceAndDice, Squarified, Strip).
13049  * 
13050  * Implemented By:
13051  * 
13052  * <TM>
13053  * 
13054  */
13055 Layouts.TM = {};
13056
13057 Layouts.TM.SliceAndDice = new Class({
13058   compute: function(prop) {
13059     var root = this.graph.getNode(this.clickedNode && this.clickedNode.id || this.root);
13060     this.controller.onBeforeCompute(root);
13061     var size = this.canvas.getSize(),
13062         config = this.config,
13063         width = size.width,
13064         height = size.height;
13065     this.graph.computeLevels(this.root, 0, "ignore");
13066     //set root position and dimensions
13067     root.getPos(prop).setc(-width/2, -height/2);
13068     root.setData('width', width, prop);
13069     root.setData('height', height + config.titleHeight, prop);
13070     this.computePositions(root, root, this.layout.orientation, prop);
13071     this.controller.onAfterCompute(root);
13072   },
13073   
13074   computePositions: function(par, ch, orn, prop) {
13075     //compute children areas
13076     var totalArea = 0;
13077     par.eachSubnode(function(n) {
13078       totalArea += n.getData('area', prop);
13079     });
13080     
13081     var config = this.config,
13082         offst = config.offset,
13083         width  = par.getData('width', prop),
13084         height = Math.max(par.getData('height', prop) - config.titleHeight, 0),
13085         fact = par == ch? 1 : (ch.getData('area', prop) / totalArea);
13086
13087     var otherSize, size, dim, pos, pos2, posth, pos2th;
13088     var horizontal = (orn == "h");
13089     if(horizontal) {
13090       orn = 'v';
13091       otherSize = height;
13092       size = width * fact;
13093       dim = 'height';
13094       pos = 'y';
13095       pos2 = 'x';
13096       posth = config.titleHeight;
13097       pos2th = 0;
13098     } else {
13099       orn = 'h';    
13100       otherSize = height * fact;
13101       size = width;
13102       dim = 'width';
13103       pos = 'x';
13104       pos2 = 'y';
13105       posth = 0;
13106       pos2th = config.titleHeight;
13107     }
13108     var cpos = ch.getPos(prop);
13109     ch.setData('width', size, prop);
13110     ch.setData('height', otherSize, prop);
13111     var offsetSize = 0, tm = this;
13112     ch.eachSubnode(function(n) {
13113       var p = n.getPos(prop);
13114       p[pos] = offsetSize + cpos[pos] + posth;
13115       p[pos2] = cpos[pos2] + pos2th;
13116       tm.computePositions(ch, n, orn, prop);
13117       offsetSize += n.getData(dim, prop);
13118     });
13119   }
13120
13121 });
13122
13123 Layouts.TM.Area = {
13124  /*
13125     Method: compute
13126  
13127    Called by loadJSON to calculate recursively all node positions and lay out the tree.
13128  
13129     Parameters:
13130
13131        json - A JSON tree. See also <Loader.loadJSON>.
13132        coord - A coordinates object specifying width, height, left and top style properties.
13133  */
13134  compute: function(prop) {
13135     prop = prop || "current";
13136     var root = this.graph.getNode(this.clickedNode && this.clickedNode.id || this.root);
13137     this.controller.onBeforeCompute(root);
13138     var config = this.config,
13139         size = this.canvas.getSize(),
13140         width = size.width,
13141         height = size.height,
13142         offst = config.offset,
13143         offwdth = width - offst,
13144         offhght = height - offst;
13145     this.graph.computeLevels(this.root, 0, "ignore");
13146     //set root position and dimensions
13147     root.getPos(prop).setc(-width/2, -height/2);
13148     root.setData('width', width, prop);
13149     root.setData('height', height, prop);
13150     //create a coordinates object
13151     var coord = {
13152         'top': -height/2 + config.titleHeight,
13153         'left': -width/2,
13154         'width': offwdth,
13155         'height': offhght - config.titleHeight
13156     };
13157     this.computePositions(root, coord, prop);
13158     this.controller.onAfterCompute(root);
13159  }, 
13160  
13161  /*
13162     Method: computeDim
13163  
13164    Computes dimensions and positions of a group of nodes
13165    according to a custom layout row condition. 
13166  
13167     Parameters:
13168
13169        tail - An array of nodes.  
13170        initElem - An array of nodes (containing the initial node to be laid).
13171        w - A fixed dimension where nodes will be layed out.
13172        coord - A coordinates object specifying width, height, left and top style properties.
13173        comp - A custom comparison function
13174  */
13175  computeDim: function(tail, initElem, w, coord, comp, prop) {
13176    if(tail.length + initElem.length == 1) {
13177      var l = (tail.length == 1)? tail : initElem;
13178      this.layoutLast(l, w, coord, prop);
13179      return;
13180    }
13181    if(tail.length >= 2 && initElem.length == 0) {
13182      initElem = [tail.shift()];
13183    }
13184    if(tail.length == 0) {
13185      if(initElem.length > 0) this.layoutRow(initElem, w, coord, prop);
13186      return;
13187    }
13188    var c = tail[0];
13189    if(comp(initElem, w) >= comp([c].concat(initElem), w)) {
13190      this.computeDim(tail.slice(1), initElem.concat([c]), w, coord, comp, prop);
13191    } else {
13192      var newCoords = this.layoutRow(initElem, w, coord, prop);
13193      this.computeDim(tail, [], newCoords.dim, newCoords, comp, prop);
13194    }
13195  },
13196
13197  
13198  /*
13199     Method: worstAspectRatio
13200  
13201    Calculates the worst aspect ratio of a group of rectangles. 
13202        
13203     See also:
13204        
13205        <http://en.wikipedia.org/wiki/Aspect_ratio>
13206    
13207     Parameters:
13208
13209      ch - An array of nodes.  
13210      w  - The fixed dimension where rectangles are being laid out.
13211
13212     Returns:
13213  
13214         The worst aspect ratio.
13215
13216
13217  */
13218  worstAspectRatio: function(ch, w) {
13219    if(!ch || ch.length == 0) return Number.MAX_VALUE;
13220    var areaSum = 0, maxArea = 0, minArea = Number.MAX_VALUE;
13221    for(var i=0, l=ch.length; i<l; i++) {
13222      var area = ch[i]._area;
13223      areaSum += area; 
13224      minArea = minArea < area? minArea : area;
13225      maxArea = maxArea > area? maxArea : area; 
13226    }
13227    var sqw = w * w, sqAreaSum = areaSum * areaSum;
13228    return Math.max(sqw * maxArea / sqAreaSum,
13229            sqAreaSum / (sqw * minArea));
13230  },
13231  
13232  /*
13233     Method: avgAspectRatio
13234  
13235    Calculates the average aspect ratio of a group of rectangles. 
13236        
13237        See also:
13238        
13239        <http://en.wikipedia.org/wiki/Aspect_ratio>
13240    
13241     Parameters:
13242
13243      ch - An array of nodes.  
13244        w - The fixed dimension where rectangles are being laid out.
13245
13246     Returns:
13247  
13248         The average aspect ratio.
13249
13250
13251  */
13252  avgAspectRatio: function(ch, w) {
13253    if(!ch || ch.length == 0) return Number.MAX_VALUE;
13254    var arSum = 0;
13255    for(var i=0, l=ch.length; i<l; i++) {
13256      var area = ch[i]._area;
13257      var h = area / w;
13258      arSum += w > h? w / h : h / w;
13259    }
13260    return arSum / l;
13261  },
13262
13263  /*
13264     layoutLast
13265  
13266    Performs the layout of the last computed sibling.
13267  
13268     Parameters:
13269
13270        ch - An array of nodes.  
13271        w - A fixed dimension where nodes will be layed out.
13272      coord - A coordinates object specifying width, height, left and top style properties.
13273  */
13274  layoutLast: function(ch, w, coord, prop) {
13275    var child = ch[0];
13276    child.getPos(prop).setc(coord.left, coord.top);
13277    child.setData('width', coord.width, prop);
13278    child.setData('height', coord.height, prop);
13279  }
13280 };
13281
13282
13283 Layouts.TM.Squarified = new Class({
13284  Implements: Layouts.TM.Area,
13285  
13286  computePositions: function(node, coord, prop) {
13287    var config = this.config, 
13288        max = Math.max;
13289    
13290    if (coord.width >= coord.height) 
13291      this.layout.orientation = 'h';
13292    else
13293      this.layout.orientation = 'v';
13294    
13295    var ch = node.getSubnodes([1, 1], "ignore");
13296    if(ch.length > 0) {
13297      this.processChildrenLayout(node, ch, coord, prop);
13298      for(var i=0, l=ch.length; i<l; i++) {
13299        var chi = ch[i], 
13300            offst = config.offset,
13301            height = max(chi.getData('height', prop) - offst - config.titleHeight, 0),
13302            width = max(chi.getData('width', prop) - offst, 0),
13303            chipos = chi.getPos(prop);
13304
13305        coord = {
13306          'width': width,
13307          'height': height,
13308          'top': chipos.y + config.titleHeight,
13309          'left': chipos.x
13310        };
13311        this.computePositions(chi, coord, prop);
13312      }
13313    }
13314  },
13315
13316  /*
13317     Method: processChildrenLayout
13318  
13319    Computes children real areas and other useful parameters for performing the Squarified algorithm.
13320  
13321     Parameters:
13322
13323        par - The parent node of the json subtree.  
13324        ch - An Array of nodes
13325      coord - A coordinates object specifying width, height, left and top style properties.
13326  */
13327  processChildrenLayout: function(par, ch, coord, prop) {
13328    //compute children real areas
13329    var parentArea = coord.width * coord.height;
13330    var i, l=ch.length, totalChArea=0, chArea = [];
13331    for(i=0; i<l; i++) {
13332      chArea[i] = parseFloat(ch[i].getData('area', prop));
13333      totalChArea += chArea[i];
13334    }
13335    for(i=0; i<l; i++) {
13336      ch[i]._area = parentArea * chArea[i] / totalChArea;
13337    }
13338    var minimumSideValue = this.layout.horizontal()? coord.height : coord.width;
13339    ch.sort(function(a, b) { 
13340      var diff = b._area - a._area; 
13341      return diff? diff : (b.id == a.id? 0 : (b.id < a.id? 1 : -1)); 
13342    });
13343    var initElem = [ch[0]];
13344    var tail = ch.slice(1);
13345    this.squarify(tail, initElem, minimumSideValue, coord, prop);
13346  },
13347
13348  /*
13349    Method: squarify
13350  
13351    Performs an heuristic method to calculate div elements sizes in order to have a good aspect ratio.
13352  
13353     Parameters:
13354
13355        tail - An array of nodes.  
13356        initElem - An array of nodes, containing the initial node to be laid out.
13357        w - A fixed dimension where nodes will be laid out.
13358        coord - A coordinates object specifying width, height, left and top style properties.
13359  */
13360  squarify: function(tail, initElem, w, coord, prop) {
13361    this.computeDim(tail, initElem, w, coord, this.worstAspectRatio, prop);
13362  },
13363  
13364  /*
13365     Method: layoutRow
13366  
13367    Performs the layout of an array of nodes.
13368  
13369     Parameters:
13370
13371        ch - An array of nodes.  
13372        w - A fixed dimension where nodes will be laid out.
13373        coord - A coordinates object specifying width, height, left and top style properties.
13374  */
13375  layoutRow: function(ch, w, coord, prop) {
13376    if(this.layout.horizontal()) {
13377      return this.layoutV(ch, w, coord, prop);
13378    } else {
13379      return this.layoutH(ch, w, coord, prop);
13380    }
13381  },
13382  
13383  layoutV: function(ch, w, coord, prop) {
13384    var totalArea = 0, rnd = function(x) { return x; }; 
13385    $.each(ch, function(elem) { totalArea += elem._area; });
13386    var width = rnd(totalArea / w), top =  0; 
13387    for(var i=0, l=ch.length; i<l; i++) {
13388      var h = rnd(ch[i]._area / width);
13389      var chi = ch[i];
13390      chi.getPos(prop).setc(coord.left, coord.top + top);
13391      chi.setData('width', width, prop);
13392      chi.setData('height', h, prop);
13393      top += h;
13394    }
13395    var ans = {
13396      'height': coord.height,
13397      'width': coord.width - width,
13398      'top': coord.top,
13399      'left': coord.left + width
13400    };
13401    //take minimum side value.
13402    ans.dim = Math.min(ans.width, ans.height);
13403    if(ans.dim != ans.height) this.layout.change();
13404    return ans;
13405  },
13406  
13407  layoutH: function(ch, w, coord, prop) {
13408    var totalArea = 0; 
13409    $.each(ch, function(elem) { totalArea += elem._area; });
13410    var height = totalArea / w,
13411        top = coord.top, 
13412        left = 0;
13413    
13414    for(var i=0, l=ch.length; i<l; i++) {
13415      var chi = ch[i];
13416      var w = chi._area / height;
13417      chi.getPos(prop).setc(coord.left + left, top);
13418      chi.setData('width', w, prop);
13419      chi.setData('height', height, prop);
13420      left += w;
13421    }
13422    var ans = {
13423      'height': coord.height - height,
13424      'width': coord.width,
13425      'top': coord.top + height,
13426      'left': coord.left
13427    };
13428    ans.dim = Math.min(ans.width, ans.height);
13429    if(ans.dim != ans.width) this.layout.change();
13430    return ans;
13431  }
13432 });
13433
13434 Layouts.TM.Strip = new Class({
13435   Implements: Layouts.TM.Area,
13436
13437     /*
13438       Method: compute
13439     
13440      Called by loadJSON to calculate recursively all node positions and lay out the tree.
13441     
13442       Parameters:
13443     
13444          json - A JSON subtree. See also <Loader.loadJSON>. 
13445        coord - A coordinates object specifying width, height, left and top style properties.
13446     */
13447     computePositions: function(node, coord, prop) {
13448      var  ch = node.getSubnodes([1, 1], "ignore"), 
13449           config = this.config,
13450           max = Math.max;
13451      if(ch.length > 0) {
13452        this.processChildrenLayout(node, ch, coord, prop);
13453        for(var i=0, l=ch.length; i<l; i++) {
13454          var chi = ch[i];
13455          var offst = config.offset,
13456              height = max(chi.getData('height', prop) - offst - config.titleHeight, 0),
13457              width  = max(chi.getData('width', prop)  - offst, 0);
13458          var chipos = chi.getPos(prop);
13459          coord = {
13460            'width': width,
13461            'height': height,
13462            'top': chipos.y + config.titleHeight,
13463            'left': chipos.x
13464          };
13465          this.computePositions(chi, coord, prop);
13466        }
13467      }
13468     },
13469     
13470     /*
13471       Method: processChildrenLayout
13472     
13473      Computes children real areas and other useful parameters for performing the Strip algorithm.
13474     
13475       Parameters:
13476     
13477          par - The parent node of the json subtree.  
13478          ch - An Array of nodes
13479          coord - A coordinates object specifying width, height, left and top style properties.
13480     */
13481     processChildrenLayout: function(par, ch, coord, prop) {
13482      //compute children real areas
13483       var parentArea = coord.width * coord.height;
13484       var i, l=ch.length, totalChArea=0, chArea = [];
13485       for(i=0; i<l; i++) {
13486         chArea[i] = +ch[i].getData('area', prop);
13487         totalChArea += chArea[i];
13488       }
13489       for(i=0; i<l; i++) {
13490         ch[i]._area = parentArea * chArea[i] / totalChArea;
13491       }
13492      var side = this.layout.horizontal()? coord.width : coord.height;
13493      var initElem = [ch[0]];
13494      var tail = ch.slice(1);
13495      this.stripify(tail, initElem, side, coord, prop);
13496     },
13497     
13498     /*
13499       Method: stripify
13500     
13501      Performs an heuristic method to calculate div elements sizes in order to have 
13502      a good compromise between aspect ratio and order.
13503     
13504       Parameters:
13505     
13506          tail - An array of nodes.  
13507          initElem - An array of nodes.
13508          w - A fixed dimension where nodes will be layed out.
13509        coord - A coordinates object specifying width, height, left and top style properties.
13510     */
13511     stripify: function(tail, initElem, w, coord, prop) {
13512      this.computeDim(tail, initElem, w, coord, this.avgAspectRatio, prop);
13513     },
13514     
13515     /*
13516       Method: layoutRow
13517     
13518      Performs the layout of an array of nodes.
13519     
13520       Parameters:
13521     
13522          ch - An array of nodes.  
13523          w - A fixed dimension where nodes will be laid out.
13524          coord - A coordinates object specifying width, height, left and top style properties.
13525     */
13526     layoutRow: function(ch, w, coord, prop) {
13527      if(this.layout.horizontal()) {
13528        return this.layoutH(ch, w, coord, prop);
13529      } else {
13530        return this.layoutV(ch, w, coord, prop);
13531      }
13532     },
13533     
13534     layoutV: function(ch, w, coord, prop) {
13535      var totalArea = 0; 
13536      $.each(ch, function(elem) { totalArea += elem._area; });
13537      var width = totalArea / w, top =  0; 
13538      for(var i=0, l=ch.length; i<l; i++) {
13539        var chi = ch[i];
13540        var h = chi._area / width;
13541        chi.getPos(prop).setc(coord.left, 
13542            coord.top + (w - h - top));
13543        chi.setData('width', width, prop);
13544        chi.setData('height', h, prop);
13545        top += h;
13546      }
13547     
13548      return {
13549        'height': coord.height,
13550        'width': coord.width - width,
13551        'top': coord.top,
13552        'left': coord.left + width,
13553        'dim': w
13554      };
13555     },
13556     
13557     layoutH: function(ch, w, coord, prop) {
13558      var totalArea = 0; 
13559      $.each(ch, function(elem) { totalArea += elem._area; });
13560      var height = totalArea / w,
13561          top = coord.height - height, 
13562          left = 0;
13563      
13564      for(var i=0, l=ch.length; i<l; i++) {
13565        var chi = ch[i];
13566        var s = chi._area / height;
13567        chi.getPos(prop).setc(coord.left + left, coord.top + top);
13568        chi.setData('width', s, prop);
13569        chi.setData('height', height, prop);
13570        left += s;
13571      }
13572      return {
13573        'height': coord.height - height,
13574        'width': coord.width,
13575        'top': coord.top,
13576        'left': coord.left,
13577        'dim': w
13578      };
13579     }
13580  });
13581
13582
13583 /*
13584  * Class: Layouts.Icicle
13585  *
13586  * Implements the icicle tree layout.
13587  *
13588  * Implemented By:
13589  *
13590  * <Icicle>
13591  *
13592  */
13593
13594 Layouts.Icicle = new Class({
13595  /*
13596   * Method: compute
13597   *
13598   * Called by loadJSON to calculate all node positions.
13599   *
13600   * Parameters:
13601   *
13602   * posType - The nodes' position to compute. Either "start", "end" or
13603   *            "current". Defaults to "current".
13604   */
13605   compute: function(posType) {
13606     posType = posType || "current";
13607
13608     var root = this.graph.getNode(this.root),
13609         config = this.config,
13610         size = this.canvas.getSize(),
13611         width = size.width,
13612         height = size.height,
13613         offset = config.offset,
13614         levelsToShow = config.constrained ? config.levelsToShow : Number.MAX_VALUE;
13615
13616     this.controller.onBeforeCompute(root);
13617
13618     Graph.Util.computeLevels(this.graph, root.id, 0, "ignore");
13619
13620     var treeDepth = 0;
13621
13622     Graph.Util.eachLevel(root, 0, false, function (n, d) { if(d > treeDepth) treeDepth = d; });
13623
13624     var startNode = this.graph.getNode(this.clickedNode && this.clickedNode.id || root.id);
13625     var maxDepth = Math.min(treeDepth, levelsToShow-1);
13626     var initialDepth = startNode._depth;
13627     if(this.layout.horizontal()) {
13628       this.computeSubtree(startNode, -width/2, -height/2, width/(maxDepth+1), height, initialDepth, maxDepth, posType);
13629     } else {
13630       this.computeSubtree(startNode, -width/2, -height/2, width, height/(maxDepth+1), initialDepth, maxDepth, posType);
13631     }
13632   },
13633
13634   computeSubtree: function (root, x, y, width, height, initialDepth, maxDepth, posType) {
13635     root.getPos(posType).setc(x, y);
13636     root.setData('width', width, posType);
13637     root.setData('height', height, posType);
13638
13639     var nodeLength, prevNodeLength = 0, totalDim = 0;
13640     var children = Graph.Util.getSubnodes(root, [1, 1], 'ignore'); // next level from this node
13641
13642     if(!children.length)
13643       return;
13644
13645     $.each(children, function(e) { totalDim += e.getData('dim'); });
13646
13647     for(var i=0, l=children.length; i < l; i++) {
13648       if(this.layout.horizontal()) {
13649         nodeLength = height * children[i].getData('dim') / totalDim;
13650         this.computeSubtree(children[i], x+width, y, width, nodeLength, initialDepth, maxDepth, posType);
13651         y += nodeLength;
13652       } else {
13653         nodeLength = width * children[i].getData('dim') / totalDim;
13654         this.computeSubtree(children[i], x, y+height, nodeLength, height, initialDepth, maxDepth, posType);
13655         x += nodeLength;
13656       }
13657     }
13658   }
13659 });
13660
13661
13662
13663 /*
13664  * File: Icicle.js
13665  *
13666 */
13667
13668 /*
13669   Class: Icicle
13670   
13671   Icicle space filling visualization.
13672   
13673   Implements:
13674   
13675   All <Loader> methods
13676   
13677   Constructor Options:
13678   
13679   Inherits options from
13680   
13681   - <Options.Canvas>
13682   - <Options.Controller>
13683   - <Options.Node>
13684   - <Options.Edge>
13685   - <Options.Label>
13686   - <Options.Events>
13687   - <Options.Tips>
13688   - <Options.NodeStyles>
13689   - <Options.Navigation>
13690   
13691   Additionally, there are other parameters and some default values changed
13692
13693   orientation - (string) Default's *h*. Whether to set horizontal or vertical layouts. Possible values are 'h' and 'v'.
13694   offset - (number) Default's *2*. Boxes offset.
13695   constrained - (boolean) Default's *false*. Whether to show the entire tree when loaded or just the number of levels specified by _levelsToShow_.
13696   levelsToShow - (number) Default's *3*. The number of levels to show for a subtree. This number is relative to the selected node.
13697   animate - (boolean) Default's *false*. Whether to animate transitions.
13698   Node.type - Described in <Options.Node>. Default's *rectangle*.
13699   Label.type - Described in <Options.Label>. Default's *Native*.
13700   duration - Described in <Options.Fx>. Default's *700*.
13701   fps - Described in <Options.Fx>. Default's *45*.
13702   
13703   Instance Properties:
13704   
13705   canvas - Access a <Canvas> instance.
13706   graph - Access a <Graph> instance.
13707   op - Access a <Icicle.Op> instance.
13708   fx - Access a <Icicle.Plot> instance.
13709   labels - Access a <Icicle.Label> interface implementation.
13710
13711 */
13712
13713 $jit.Icicle = new Class({
13714   Implements: [ Loader, Extras, Layouts.Icicle ],
13715
13716   layout: {
13717     orientation: "h",
13718     vertical: function(){
13719       return this.orientation == "v";
13720     },
13721     horizontal: function(){
13722       return this.orientation == "h";
13723     },
13724     change: function(){
13725       this.orientation = this.vertical()? "h" : "v";
13726     }
13727   },
13728
13729   initialize: function(controller) {
13730     var config = {
13731       animate: false,
13732       orientation: "h",
13733       offset: 2,
13734       levelsToShow: Number.MAX_VALUE,
13735       constrained: false,
13736       Node: {
13737         type: 'rectangle',
13738         overridable: true
13739       },
13740       Edge: {
13741         type: 'none'
13742       },
13743       Label: {
13744         type: 'Native'
13745       },
13746       duration: 700,
13747       fps: 45
13748     };
13749
13750     var opts = Options("Canvas", "Node", "Edge", "Fx", "Tips", "NodeStyles",
13751                        "Events", "Navigation", "Controller", "Label");
13752     this.controller = this.config = $.merge(opts, config, controller);
13753     this.layout.orientation = this.config.orientation;
13754
13755     var canvasConfig = this.config;
13756     if (canvasConfig.useCanvas) {
13757       this.canvas = canvasConfig.useCanvas;
13758       this.config.labelContainer = this.canvas.id + '-label';
13759     } else {
13760       this.canvas = new Canvas(this, canvasConfig);
13761       this.config.labelContainer = (typeof canvasConfig.injectInto == 'string'? canvasConfig.injectInto : canvasConfig.injectInto.id) + '-label';
13762     }
13763
13764     this.graphOptions = {
13765       'klass': Complex,
13766       'Node': {
13767         'selected': false,
13768         'exist': true,
13769         'drawn': true
13770       }
13771     };
13772
13773     this.graph = new Graph(
13774       this.graphOptions, this.config.Node, this.config.Edge, this.config.Label);
13775
13776     this.labels = new $jit.Icicle.Label[this.config.Label.type](this);
13777     this.fx = new $jit.Icicle.Plot(this, $jit.Icicle);
13778     this.op = new $jit.Icicle.Op(this);
13779     this.group = new $jit.Icicle.Group(this);
13780     this.clickedNode = null;
13781
13782     this.initializeExtras();
13783   },
13784
13785   /* 
13786     Method: refresh 
13787     
13788     Computes positions and plots the tree.
13789   */
13790   refresh: function(){
13791     var labelType = this.config.Label.type;
13792     if(labelType != 'Native') {
13793       var that = this;
13794       this.graph.eachNode(function(n) { that.labels.hideLabel(n, false); });
13795     }
13796     this.compute();
13797     this.plot();
13798   },
13799
13800   /* 
13801     Method: plot 
13802     
13803     Plots the Icicle visualization. This is a shortcut to *fx.plot*. 
13804   
13805    */
13806   plot: function(){
13807     this.fx.plot(this.config);
13808   },
13809
13810   /* 
13811     Method: enter 
13812     
13813     Sets the node as root.
13814     
13815      Parameters:
13816      
13817      node - (object) A <Graph.Node>.
13818   
13819    */
13820   enter: function (node) {
13821     if (this.busy)
13822       return;
13823     this.busy = true;
13824
13825     var that = this,
13826         config = this.config;
13827
13828     var callback = {
13829       onComplete: function() {
13830         //compute positions of newly inserted nodes
13831         if(config.request)
13832           that.compute();
13833
13834         if(config.animate) {
13835           that.graph.nodeList.setDataset(['current', 'end'], {
13836             'alpha': [1, 0] //fade nodes
13837           });
13838
13839           Graph.Util.eachSubgraph(node, function(n) {
13840             n.setData('alpha', 1, 'end');
13841           }, "ignore");
13842
13843           that.fx.animate({
13844             duration: 500,
13845             modes:['node-property:alpha'],
13846             onComplete: function() {
13847               that.clickedNode = node;
13848               that.compute('end');
13849
13850               that.fx.animate({
13851                 modes:['linear', 'node-property:width:height'],
13852                 duration: 1000,
13853                 onComplete: function() {
13854                   that.busy = false;
13855                   that.clickedNode = node;
13856                 }
13857               });
13858             }
13859           });
13860         } else {
13861           that.clickedNode = node;
13862           that.busy = false;
13863           that.refresh();
13864         }
13865       }
13866     };
13867
13868     if(config.request) {
13869       this.requestNodes(clickedNode, callback);
13870     } else {
13871       callback.onComplete();
13872     }
13873   },
13874
13875   /* 
13876     Method: out 
13877     
13878     Sets the parent node of the current selected node as root.
13879   
13880    */
13881   out: function(){
13882     if(this.busy)
13883       return;
13884
13885     var that = this,
13886         GUtil = Graph.Util,
13887         config = this.config,
13888         graph = this.graph,
13889         parents = GUtil.getParents(graph.getNode(this.clickedNode && this.clickedNode.id || this.root)),
13890         parent = parents[0],
13891         clickedNode = parent,
13892         previousClickedNode = this.clickedNode;
13893
13894     this.busy = true;
13895     this.events.hoveredNode = false;
13896
13897     if(!parent) {
13898       this.busy = false;
13899       return;
13900     }
13901
13902     //final plot callback
13903     callback = {
13904       onComplete: function() {
13905         that.clickedNode = parent;
13906         if(config.request) {
13907           that.requestNodes(parent, {
13908             onComplete: function() {
13909               that.compute();
13910               that.plot();
13911               that.busy = false;
13912             }
13913           });
13914         } else {
13915           that.compute();
13916           that.plot();
13917           that.busy = false;
13918         }
13919       }
13920     };
13921
13922     //animate node positions
13923     if(config.animate) {
13924       this.clickedNode = clickedNode;
13925       this.compute('end');
13926       //animate the visible subtree only
13927       this.clickedNode = previousClickedNode;
13928       this.fx.animate({
13929         modes:['linear', 'node-property:width:height'],
13930         duration: 1000,
13931         onComplete: function() {
13932           //animate the parent subtree
13933           that.clickedNode = clickedNode;
13934           //change nodes alpha
13935           graph.nodeList.setDataset(['current', 'end'], {
13936             'alpha': [0, 1]
13937           });
13938           GUtil.eachSubgraph(previousClickedNode, function(node) {
13939             node.setData('alpha', 1);
13940           }, "ignore");
13941           that.fx.animate({
13942             duration: 500,
13943             modes:['node-property:alpha'],
13944             onComplete: function() {
13945               callback.onComplete();
13946             }
13947           });
13948         }
13949       });
13950     } else {
13951       callback.onComplete();
13952     }
13953   },
13954   requestNodes: function(node, onComplete){
13955     var handler = $.merge(this.controller, onComplete),
13956         levelsToShow = this.config.constrained ? this.config.levelsToShow : Number.MAX_VALUE;
13957
13958     if (handler.request) {
13959       var leaves = [], d = node._depth;
13960       Graph.Util.eachLevel(node, 0, levelsToShow, function(n){
13961         if (n.drawn && !Graph.Util.anySubnode(n)) {
13962           leaves.push(n);
13963           n._level = n._depth - d;
13964           if (this.config.constrained)
13965             n._level = levelsToShow - n._level;
13966
13967         }
13968       });
13969       this.group.requestNodes(leaves, handler);
13970     } else {
13971       handler.onComplete();
13972     }
13973   }
13974 });
13975
13976 /*
13977   Class: Icicle.Op
13978   
13979   Custom extension of <Graph.Op>.
13980   
13981   Extends:
13982   
13983   All <Graph.Op> methods
13984   
13985   See also:
13986   
13987   <Graph.Op>
13988   
13989   */
13990 $jit.Icicle.Op = new Class({
13991
13992   Implements: Graph.Op
13993
13994 });
13995
13996 /*
13997  * Performs operations on group of nodes.
13998  */
13999 $jit.Icicle.Group = new Class({
14000
14001   initialize: function(viz){
14002     this.viz = viz;
14003     this.canvas = viz.canvas;
14004     this.config = viz.config;
14005   },
14006
14007   /*
14008    * Calls the request method on the controller to request a subtree for each node.
14009    */
14010   requestNodes: function(nodes, controller){
14011     var counter = 0, len = nodes.length, nodeSelected = {};
14012     var complete = function(){
14013       controller.onComplete();
14014     };
14015     var viz = this.viz;
14016     if (len == 0)
14017       complete();
14018     for(var i = 0; i < len; i++) {
14019       nodeSelected[nodes[i].id] = nodes[i];
14020       controller.request(nodes[i].id, nodes[i]._level, {
14021         onComplete: function(nodeId, data){
14022           if (data && data.children) {
14023             data.id = nodeId;
14024             viz.op.sum(data, {
14025               type: 'nothing'
14026             });
14027           }
14028           if (++counter == len) {
14029             Graph.Util.computeLevels(viz.graph, viz.root, 0);
14030             complete();
14031           }
14032         }
14033       });
14034     }
14035   }
14036 });
14037
14038 /*
14039   Class: Icicle.Plot
14040   
14041   Custom extension of <Graph.Plot>.
14042   
14043   Extends:
14044   
14045   All <Graph.Plot> methods
14046   
14047   See also:
14048   
14049   <Graph.Plot>
14050   
14051   */
14052 $jit.Icicle.Plot = new Class({
14053   Implements: Graph.Plot,
14054
14055   plot: function(opt, animating){
14056     opt = opt || this.viz.controller;
14057     var viz = this.viz,
14058         graph = viz.graph,
14059         root = graph.getNode(viz.clickedNode && viz.clickedNode.id || viz.root),
14060         initialDepth = root._depth;
14061
14062     viz.canvas.clear();
14063     this.plotTree(root, $.merge(opt, {
14064       'withLabels': true,
14065       'hideLabels': false,
14066       'plotSubtree': function(root, node) {
14067         return !viz.config.constrained ||
14068                (node._depth - initialDepth < viz.config.levelsToShow);
14069       }
14070     }), animating);
14071   }
14072 });
14073
14074 /*
14075   Class: Icicle.Label
14076   
14077   Custom extension of <Graph.Label>. 
14078   Contains custom <Graph.Label.SVG>, <Graph.Label.HTML> and <Graph.Label.Native> extensions.
14079   
14080   Extends:
14081   
14082   All <Graph.Label> methods and subclasses.
14083   
14084   See also:
14085   
14086   <Graph.Label>, <Graph.Label.Native>, <Graph.Label.HTML>, <Graph.Label.SVG>.
14087   
14088   */
14089 $jit.Icicle.Label = {};
14090
14091 /*
14092   Icicle.Label.Native
14093   
14094   Custom extension of <Graph.Label.Native>.
14095   
14096   Extends:
14097   
14098   All <Graph.Label.Native> methods
14099   
14100   See also:
14101   
14102   <Graph.Label.Native>
14103
14104   */
14105 $jit.Icicle.Label.Native = new Class({
14106   Implements: Graph.Label.Native,
14107
14108   renderLabel: function(canvas, node, controller) {
14109     var ctx = canvas.getCtx(),
14110         width = node.getData('width'),
14111         height = node.getData('height'),
14112         size = node.getLabelData('size'),
14113         m = ctx.measureText(node.name);
14114
14115     // Guess as much as possible if the label will fit in the node
14116     if(height < (size * 1.5) || width < m.width)
14117       return;
14118
14119     var pos = node.pos.getc(true);
14120     ctx.fillText(node.name,
14121                  pos.x + width / 2,
14122                  pos.y + height / 2);
14123   }
14124 });
14125
14126 /*
14127   Icicle.Label.SVG
14128   
14129   Custom extension of <Graph.Label.SVG>.
14130   
14131   Extends:
14132   
14133   All <Graph.Label.SVG> methods
14134   
14135   See also:
14136   
14137   <Graph.Label.SVG>
14138 */
14139 $jit.Icicle.Label.SVG = new Class( {
14140   Implements: Graph.Label.SVG,
14141
14142   initialize: function(viz){
14143     this.viz = viz;
14144   },
14145
14146   /*
14147     placeLabel
14148    
14149     Overrides abstract method placeLabel in <Graph.Plot>.
14150    
14151     Parameters:
14152    
14153     tag - A DOM label element.
14154     node - A <Graph.Node>.
14155     controller - A configuration/controller object passed to the visualization.
14156    */
14157   placeLabel: function(tag, node, controller){
14158     var pos = node.pos.getc(true), canvas = this.viz.canvas;
14159     var radius = canvas.getSize();
14160     var labelPos = {
14161       x: Math.round(pos.x + radius.width / 2),
14162       y: Math.round(pos.y + radius.height / 2)
14163     };
14164     tag.setAttribute('x', labelPos.x);
14165     tag.setAttribute('y', labelPos.y);
14166
14167     controller.onPlaceLabel(tag, node);
14168   }
14169 });
14170
14171 /*
14172   Icicle.Label.HTML
14173   
14174   Custom extension of <Graph.Label.HTML>.
14175   
14176   Extends:
14177   
14178   All <Graph.Label.HTML> methods.
14179   
14180   See also:
14181   
14182   <Graph.Label.HTML>
14183   
14184   */
14185 $jit.Icicle.Label.HTML = new Class( {
14186   Implements: Graph.Label.HTML,
14187
14188   initialize: function(viz){
14189     this.viz = viz;
14190   },
14191
14192   /*
14193     placeLabel
14194    
14195     Overrides abstract method placeLabel in <Graph.Plot>.
14196    
14197     Parameters:
14198    
14199     tag - A DOM label element.
14200     node - A <Graph.Node>.
14201     controller - A configuration/controller object passed to the visualization.
14202    */
14203   placeLabel: function(tag, node, controller){
14204     var pos = node.pos.getc(true), canvas = this.viz.canvas;
14205     var radius = canvas.getSize();
14206     var labelPos = {
14207       x: Math.round(pos.x + radius.width / 2),
14208       y: Math.round(pos.y + radius.height / 2)
14209     };
14210
14211     var style = tag.style;
14212     style.left = labelPos.x + 'px';
14213     style.top = labelPos.y + 'px';
14214     style.display = '';
14215
14216     controller.onPlaceLabel(tag, node);
14217   }
14218 });
14219
14220 /*
14221   Class: Icicle.Plot.NodeTypes
14222   
14223   This class contains a list of <Graph.Node> built-in types. 
14224   Node types implemented are 'none', 'rectangle'.
14225   
14226   You can add your custom node types, customizing your visualization to the extreme.
14227   
14228   Example:
14229   
14230   (start code js)
14231     Icicle.Plot.NodeTypes.implement({
14232       'mySpecialType': {
14233         'render': function(node, canvas) {
14234           //print your custom node to canvas
14235         },
14236         //optional
14237         'contains': function(node, pos) {
14238           //return true if pos is inside the node or false otherwise
14239         }
14240       }
14241     });
14242   (end code)
14243   
14244   */
14245 $jit.Icicle.Plot.NodeTypes = new Class( {
14246   'none': {
14247     'render': $.empty
14248   },
14249
14250   'rectangle': {
14251     'render': function(node, canvas, animating) {
14252       var config = this.viz.config;
14253       var offset = config.offset;
14254       var width = node.getData('width');
14255       var height = node.getData('height');
14256       var border = node.getData('border');
14257       var pos = node.pos.getc(true);
14258       var posx = pos.x + offset / 2, posy = pos.y + offset / 2;
14259       var ctx = canvas.getCtx();
14260       
14261       if(width - offset < 2 || height - offset < 2) return;
14262       
14263       if(config.cushion) {
14264         var color = node.getData('color');
14265         var lg = ctx.createRadialGradient(posx + (width - offset)/2, 
14266                                           posy + (height - offset)/2, 1, 
14267                                           posx + (width-offset)/2, posy + (height-offset)/2, 
14268                                           width < height? height : width);
14269         var colorGrad = $.rgbToHex($.map($.hexToRgb(color), 
14270             function(r) { return r * 0.3 >> 0; }));
14271         lg.addColorStop(0, color);
14272         lg.addColorStop(1, colorGrad);
14273         ctx.fillStyle = lg;
14274       }
14275
14276       if (border) {
14277         ctx.strokeStyle = border;
14278         ctx.lineWidth = 3;
14279       }
14280
14281       ctx.fillRect(posx, posy, Math.max(0, width - offset), Math.max(0, height - offset));
14282       border && ctx.strokeRect(pos.x, pos.y, width, height);
14283     },
14284
14285     'contains': function(node, pos) {
14286       if(this.viz.clickedNode && !$jit.Graph.Util.isDescendantOf(node, this.viz.clickedNode.id)) return false;
14287       var npos = node.pos.getc(true),
14288           width = node.getData('width'),
14289           height = node.getData('height');
14290       return this.nodeHelper.rectangle.contains({x: npos.x + width/2, y: npos.y + height/2}, pos, width, height);
14291     }
14292   }
14293 });
14294
14295 $jit.Icicle.Plot.EdgeTypes = new Class( {
14296   'none': $.empty
14297 });
14298
14299
14300
14301 /*
14302  * File: Layouts.ForceDirected.js
14303  *
14304 */
14305
14306 /*
14307  * Class: Layouts.ForceDirected
14308  * 
14309  * Implements a Force Directed Layout.
14310  * 
14311  * Implemented By:
14312  * 
14313  * <ForceDirected>
14314  * 
14315  * Credits:
14316  * 
14317  * Marcus Cobden <http://marcuscobden.co.uk>
14318  * 
14319  */
14320 Layouts.ForceDirected = new Class({
14321
14322   getOptions: function(random) {
14323     var s = this.canvas.getSize();
14324     var w = s.width, h = s.height;
14325     //count nodes
14326     var count = 0;
14327     this.graph.eachNode(function(n) { 
14328       count++;
14329     });
14330     var k2 = w * h / count, k = Math.sqrt(k2);
14331     var l = this.config.levelDistance;
14332     
14333     return {
14334       width: w,
14335       height: h,
14336       tstart: w * 0.1,
14337       nodef: function(x) { return k2 / (x || 1); },
14338       edgef: function(x) { return /* x * x / k; */ k * (x - l); }
14339     };
14340   },
14341   
14342   compute: function(property, incremental) {
14343     var prop = $.splat(property || ['current', 'start', 'end']);
14344     var opt = this.getOptions();
14345     NodeDim.compute(this.graph, prop, this.config);
14346     this.graph.computeLevels(this.root, 0, "ignore");
14347     this.graph.eachNode(function(n) {
14348       $.each(prop, function(p) {
14349         var pos = n.getPos(p);
14350         if(pos.equals(Complex.KER)) {
14351           pos.x = opt.width/5 * (Math.random() - 0.5);
14352           pos.y = opt.height/5 * (Math.random() - 0.5);
14353         }
14354         //initialize disp vector
14355         n.disp = {};
14356         $.each(prop, function(p) {
14357           n.disp[p] = $C(0, 0);
14358         });
14359       });
14360     });
14361     this.computePositions(prop, opt, incremental);
14362   },
14363   
14364   computePositions: function(property, opt, incremental) {
14365     var times = this.config.iterations, i = 0, that = this;
14366     if(incremental) {
14367       (function iter() {
14368         for(var total=incremental.iter, j=0; j<total; j++) {
14369           opt.t = opt.tstart;
14370           if(times) opt.t *= (1 - i++/(times -1));
14371           that.computePositionStep(property, opt);
14372           if(times && i >= times) {
14373             incremental.onComplete();
14374             return;
14375           }
14376         }
14377         incremental.onStep(Math.round(i / (times -1) * 100));
14378         setTimeout(iter, 1);
14379       })();
14380     } else {
14381       for(; i < times; i++) {
14382         opt.t = opt.tstart * (1 - i/(times -1));
14383         this.computePositionStep(property, opt);
14384       }
14385     }
14386   },
14387   
14388   computePositionStep: function(property, opt) {
14389     var graph = this.graph;
14390     var min = Math.min, max = Math.max;
14391     var dpos = $C(0, 0);
14392     //calculate repulsive forces
14393     graph.eachNode(function(v) {
14394       //initialize disp
14395       $.each(property, function(p) {
14396         v.disp[p].x = 0; v.disp[p].y = 0;
14397       });
14398       graph.eachNode(function(u) {
14399         if(u.id != v.id) {
14400           $.each(property, function(p) {
14401             var vp = v.getPos(p), up = u.getPos(p);
14402             dpos.x = vp.x - up.x;
14403             dpos.y = vp.y - up.y;
14404             var norm = dpos.norm() || 1;
14405             v.disp[p].$add(dpos
14406                 .$scale(opt.nodef(norm) / norm));
14407           });
14408         }
14409       });
14410     });
14411     //calculate attractive forces
14412     var T = !!graph.getNode(this.root).visited;
14413     graph.eachNode(function(node) {
14414       node.eachAdjacency(function(adj) {
14415         var nodeTo = adj.nodeTo;
14416         if(!!nodeTo.visited === T) {
14417           $.each(property, function(p) {
14418             var vp = node.getPos(p), up = nodeTo.getPos(p);
14419             dpos.x = vp.x - up.x;
14420             dpos.y = vp.y - up.y;
14421             var norm = dpos.norm() || 1;
14422             node.disp[p].$add(dpos.$scale(-opt.edgef(norm) / norm));
14423             nodeTo.disp[p].$add(dpos.$scale(-1));
14424           });
14425         }
14426       });
14427       node.visited = !T;
14428     });
14429     //arrange positions to fit the canvas
14430     var t = opt.t, w2 = opt.width / 2, h2 = opt.height / 2;
14431     graph.eachNode(function(u) {
14432       $.each(property, function(p) {
14433         var disp = u.disp[p];
14434         var norm = disp.norm() || 1;
14435         var p = u.getPos(p);
14436         p.$add($C(disp.x * min(Math.abs(disp.x), t) / norm, 
14437             disp.y * min(Math.abs(disp.y), t) / norm));
14438         p.x = min(w2, max(-w2, p.x));
14439         p.y = min(h2, max(-h2, p.y));
14440       });
14441     });
14442   }
14443 });
14444
14445 /*
14446  * File: ForceDirected.js
14447  */
14448
14449 /*
14450    Class: ForceDirected
14451       
14452    A visualization that lays graphs using a Force-Directed layout algorithm.
14453    
14454    Inspired by:
14455   
14456    Force-Directed Drawing Algorithms (Stephen G. Kobourov) <http://www.cs.brown.edu/~rt/gdhandbook/chapters/force-directed.pdf>
14457    
14458   Implements:
14459   
14460   All <Loader> methods
14461   
14462    Constructor Options:
14463    
14464    Inherits options from
14465    
14466    - <Options.Canvas>
14467    - <Options.Controller>
14468    - <Options.Node>
14469    - <Options.Edge>
14470    - <Options.Label>
14471    - <Options.Events>
14472    - <Options.Tips>
14473    - <Options.NodeStyles>
14474    - <Options.Navigation>
14475    
14476    Additionally, there are two parameters
14477    
14478    levelDistance - (number) Default's *50*. The natural length desired for the edges.
14479    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*. 
14480      
14481    Instance Properties:
14482
14483    canvas - Access a <Canvas> instance.
14484    graph - Access a <Graph> instance.
14485    op - Access a <ForceDirected.Op> instance.
14486    fx - Access a <ForceDirected.Plot> instance.
14487    labels - Access a <ForceDirected.Label> interface implementation.
14488
14489 */
14490
14491 $jit.ForceDirected = new Class( {
14492
14493   Implements: [ Loader, Extras, Layouts.ForceDirected ],
14494
14495   initialize: function(controller) {
14496     var $ForceDirected = $jit.ForceDirected;
14497
14498     var config = {
14499       iterations: 50,
14500       levelDistance: 50
14501     };
14502
14503     this.controller = this.config = $.merge(Options("Canvas", "Node", "Edge",
14504         "Fx", "Tips", "NodeStyles", "Events", "Navigation", "Controller", "Label"), config, controller);
14505
14506     var canvasConfig = this.config;
14507     if(canvasConfig.useCanvas) {
14508       this.canvas = canvasConfig.useCanvas;
14509       this.config.labelContainer = this.canvas.id + '-label';
14510     } else {
14511       if(canvasConfig.background) {
14512         canvasConfig.background = $.merge({
14513           type: 'Circles'
14514         }, canvasConfig.background);
14515       }
14516       this.canvas = new Canvas(this, canvasConfig);
14517       this.config.labelContainer = (typeof canvasConfig.injectInto == 'string'? canvasConfig.injectInto : canvasConfig.injectInto.id) + '-label';
14518     }
14519
14520     this.graphOptions = {
14521       'klass': Complex,
14522       'Node': {
14523         'selected': false,
14524         'exist': true,
14525         'drawn': true
14526       }
14527     };
14528     this.graph = new Graph(this.graphOptions, this.config.Node,
14529         this.config.Edge);
14530     this.labels = new $ForceDirected.Label[canvasConfig.Label.type](this);
14531     this.fx = new $ForceDirected.Plot(this, $ForceDirected);
14532     this.op = new $ForceDirected.Op(this);
14533     this.json = null;
14534     this.busy = false;
14535     // initialize extras
14536     this.initializeExtras();
14537   },
14538
14539   /* 
14540     Method: refresh 
14541     
14542     Computes positions and plots the tree.
14543   */
14544   refresh: function() {
14545     this.compute();
14546     this.plot();
14547   },
14548
14549   reposition: function() {
14550     this.compute('end');
14551   },
14552
14553 /*
14554   Method: computeIncremental
14555   
14556   Performs the Force Directed algorithm incrementally.
14557   
14558   Description:
14559   
14560   ForceDirected algorithms can perform many computations and lead to JavaScript taking too much time to complete. 
14561   This method splits the algorithm into smaller parts allowing the user to track the evolution of the algorithm and 
14562   avoiding browser messages such as "This script is taking too long to complete".
14563   
14564   Parameters:
14565   
14566   opt - (object) The object properties are described below
14567   
14568   iter - (number) Default's *20*. Split the algorithm into pieces of _iter_ iterations. For example, if the _iterations_ configuration property 
14569   of your <ForceDirected> class is 100, then you could set _iter_ to 20 to split the main algorithm into 5 smaller pieces.
14570   
14571   property - (string) Default's *end*. Whether to update starting, current or ending node positions. Possible values are 'end', 'start', 'current'. 
14572   You can also set an array of these properties. If you'd like to keep the current node positions but to perform these 
14573   computations for final animation positions then you can just choose 'end'.
14574   
14575   onStep - (function) A callback function called when each "small part" of the algorithm completed. This function gets as first formal 
14576   parameter a percentage value.
14577   
14578   onComplete - A callback function called when the algorithm completed.
14579   
14580   Example:
14581   
14582   In this example I calculate the end positions and then animate the graph to those positions
14583   
14584   (start code js)
14585   var fd = new $jit.ForceDirected(...);
14586   fd.computeIncremental({
14587     iter: 20,
14588     property: 'end',
14589     onStep: function(perc) {
14590       Log.write("loading " + perc + "%");
14591     },
14592     onComplete: function() {
14593       Log.write("done");
14594       fd.animate();
14595     }
14596   });
14597   (end code)
14598   
14599   In this example I calculate all positions and (re)plot the graph
14600   
14601   (start code js)
14602   var fd = new ForceDirected(...);
14603   fd.computeIncremental({
14604     iter: 20,
14605     property: ['end', 'start', 'current'],
14606     onStep: function(perc) {
14607       Log.write("loading " + perc + "%");
14608     },
14609     onComplete: function() {
14610       Log.write("done");
14611       fd.plot();
14612     }
14613   });
14614   (end code)
14615   
14616   */
14617   computeIncremental: function(opt) {
14618     opt = $.merge( {
14619       iter: 20,
14620       property: 'end',
14621       onStep: $.empty,
14622       onComplete: $.empty
14623     }, opt || {});
14624
14625     this.config.onBeforeCompute(this.graph.getNode(this.root));
14626     this.compute(opt.property, opt);
14627   },
14628
14629   /*
14630     Method: plot
14631    
14632     Plots the ForceDirected graph. This is a shortcut to *fx.plot*.
14633    */
14634   plot: function() {
14635     this.fx.plot();
14636   },
14637
14638   /*
14639      Method: animate
14640     
14641      Animates the graph from the current positions to the 'end' node positions.
14642   */
14643   animate: function(opt) {
14644     this.fx.animate($.merge( {
14645       modes: [ 'linear' ]
14646     }, opt || {}));
14647   }
14648 });
14649
14650 $jit.ForceDirected.$extend = true;
14651
14652 (function(ForceDirected) {
14653
14654   /*
14655      Class: ForceDirected.Op
14656      
14657      Custom extension of <Graph.Op>.
14658
14659      Extends:
14660
14661      All <Graph.Op> methods
14662      
14663      See also:
14664      
14665      <Graph.Op>
14666
14667   */
14668   ForceDirected.Op = new Class( {
14669
14670     Implements: Graph.Op
14671
14672   });
14673
14674   /*
14675     Class: ForceDirected.Plot
14676     
14677     Custom extension of <Graph.Plot>.
14678   
14679     Extends:
14680   
14681     All <Graph.Plot> methods
14682     
14683     See also:
14684     
14685     <Graph.Plot>
14686   
14687   */
14688   ForceDirected.Plot = new Class( {
14689
14690     Implements: Graph.Plot
14691
14692   });
14693
14694   /*
14695     Class: ForceDirected.Label
14696     
14697     Custom extension of <Graph.Label>. 
14698     Contains custom <Graph.Label.SVG>, <Graph.Label.HTML> and <Graph.Label.Native> extensions.
14699   
14700     Extends:
14701   
14702     All <Graph.Label> methods and subclasses.
14703   
14704     See also:
14705   
14706     <Graph.Label>, <Graph.Label.Native>, <Graph.Label.HTML>, <Graph.Label.SVG>.
14707   
14708   */
14709   ForceDirected.Label = {};
14710
14711   /*
14712      ForceDirected.Label.Native
14713      
14714      Custom extension of <Graph.Label.Native>.
14715
14716      Extends:
14717
14718      All <Graph.Label.Native> methods
14719
14720      See also:
14721
14722      <Graph.Label.Native>
14723
14724   */
14725   ForceDirected.Label.Native = new Class( {
14726     Implements: Graph.Label.Native
14727   });
14728
14729   /*
14730     ForceDirected.Label.SVG
14731     
14732     Custom extension of <Graph.Label.SVG>.
14733   
14734     Extends:
14735   
14736     All <Graph.Label.SVG> methods
14737   
14738     See also:
14739   
14740     <Graph.Label.SVG>
14741   
14742   */
14743   ForceDirected.Label.SVG = new Class( {
14744     Implements: Graph.Label.SVG,
14745
14746     initialize: function(viz) {
14747       this.viz = viz;
14748     },
14749
14750     /* 
14751        placeLabel
14752
14753        Overrides abstract method placeLabel in <Graph.Label>.
14754
14755        Parameters:
14756
14757        tag - A DOM label element.
14758        node - A <Graph.Node>.
14759        controller - A configuration/controller object passed to the visualization.
14760       
14761      */
14762     placeLabel: function(tag, node, controller) {
14763       var pos = node.pos.getc(true), 
14764           canvas = this.viz.canvas,
14765           ox = canvas.translateOffsetX,
14766           oy = canvas.translateOffsetY,
14767           sx = canvas.scaleOffsetX,
14768           sy = canvas.scaleOffsetY,
14769           radius = canvas.getSize();
14770       var labelPos = {
14771         x: Math.round(pos.x * sx + ox + radius.width / 2),
14772         y: Math.round(pos.y * sy + oy + radius.height / 2)
14773       };
14774       tag.setAttribute('x', labelPos.x);
14775       tag.setAttribute('y', labelPos.y);
14776
14777       controller.onPlaceLabel(tag, node);
14778     }
14779   });
14780
14781   /*
14782      ForceDirected.Label.HTML
14783      
14784      Custom extension of <Graph.Label.HTML>.
14785
14786      Extends:
14787
14788      All <Graph.Label.HTML> methods.
14789
14790      See also:
14791
14792      <Graph.Label.HTML>
14793
14794   */
14795   ForceDirected.Label.HTML = new Class( {
14796     Implements: Graph.Label.HTML,
14797
14798     initialize: function(viz) {
14799       this.viz = viz;
14800     },
14801     /* 
14802        placeLabel
14803
14804        Overrides abstract method placeLabel in <Graph.Plot>.
14805
14806        Parameters:
14807
14808        tag - A DOM label element.
14809        node - A <Graph.Node>.
14810        controller - A configuration/controller object passed to the visualization.
14811       
14812      */
14813     placeLabel: function(tag, node, controller) {
14814       var pos = node.pos.getc(true), 
14815           canvas = this.viz.canvas,
14816           ox = canvas.translateOffsetX,
14817           oy = canvas.translateOffsetY,
14818           sx = canvas.scaleOffsetX,
14819           sy = canvas.scaleOffsetY,
14820           radius = canvas.getSize();
14821       var labelPos = {
14822         x: Math.round(pos.x * sx + ox + radius.width / 2),
14823         y: Math.round(pos.y * sy + oy + radius.height / 2)
14824       };
14825       var style = tag.style;
14826       style.left = labelPos.x + 'px';
14827       style.top = labelPos.y + 'px';
14828       style.display = this.fitsInCanvas(labelPos, canvas) ? '' : 'none';
14829
14830       controller.onPlaceLabel(tag, node);
14831     }
14832   });
14833
14834   /*
14835     Class: ForceDirected.Plot.NodeTypes
14836
14837     This class contains a list of <Graph.Node> built-in types. 
14838     Node types implemented are 'none', 'circle', 'triangle', 'rectangle', 'star', 'ellipse' and 'square'.
14839
14840     You can add your custom node types, customizing your visualization to the extreme.
14841
14842     Example:
14843
14844     (start code js)
14845       ForceDirected.Plot.NodeTypes.implement({
14846         'mySpecialType': {
14847           'render': function(node, canvas) {
14848             //print your custom node to canvas
14849           },
14850           //optional
14851           'contains': function(node, pos) {
14852             //return true if pos is inside the node or false otherwise
14853           }
14854         }
14855       });
14856     (end code)
14857
14858   */
14859   ForceDirected.Plot.NodeTypes = new Class({
14860     'none': {
14861       'render': $.empty,
14862       'contains': $.lambda(false)
14863     },
14864     'circle': {
14865       'render': function(node, canvas){
14866         var pos = node.pos.getc(true), 
14867             dim = node.getData('dim');
14868         this.nodeHelper.circle.render('fill', pos, dim, canvas);
14869       },
14870       'contains': function(node, pos){
14871         var npos = node.pos.getc(true), 
14872             dim = node.getData('dim');
14873         return this.nodeHelper.circle.contains(npos, pos, dim);
14874       }
14875     },
14876     'ellipse': {
14877       'render': function(node, canvas){
14878         var pos = node.pos.getc(true), 
14879             width = node.getData('width'), 
14880             height = node.getData('height');
14881         this.nodeHelper.ellipse.render('fill', pos, width, height, canvas);
14882         },
14883       'contains': function(node, pos){
14884         var npos = node.pos.getc(true), 
14885             width = node.getData('width'), 
14886             height = node.getData('height');
14887         return this.nodeHelper.ellipse.contains(npos, pos, width, height);
14888       }
14889     },
14890     'square': {
14891       'render': function(node, canvas){
14892         var pos = node.pos.getc(true), 
14893             dim = node.getData('dim');
14894         this.nodeHelper.square.render('fill', pos, dim, canvas);
14895       },
14896       'contains': function(node, pos){
14897         var npos = node.pos.getc(true), 
14898             dim = node.getData('dim');
14899         return this.nodeHelper.square.contains(npos, pos, dim);
14900       }
14901     },
14902     'rectangle': {
14903       'render': function(node, canvas){
14904         var pos = node.pos.getc(true), 
14905             width = node.getData('width'), 
14906             height = node.getData('height');
14907         this.nodeHelper.rectangle.render('fill', pos, width, height, canvas);
14908       },
14909       'contains': function(node, pos){
14910         var npos = node.pos.getc(true), 
14911             width = node.getData('width'), 
14912             height = node.getData('height');
14913         return this.nodeHelper.rectangle.contains(npos, pos, width, height);
14914       }
14915     },
14916     'triangle': {
14917       'render': function(node, canvas){
14918         var pos = node.pos.getc(true), 
14919             dim = node.getData('dim');
14920         this.nodeHelper.triangle.render('fill', pos, dim, canvas);
14921       },
14922       'contains': function(node, pos) {
14923         var npos = node.pos.getc(true), 
14924             dim = node.getData('dim');
14925         return this.nodeHelper.triangle.contains(npos, pos, dim);
14926       }
14927     },
14928     'star': {
14929       'render': function(node, canvas){
14930         var pos = node.pos.getc(true),
14931             dim = node.getData('dim');
14932         this.nodeHelper.star.render('fill', pos, dim, canvas);
14933       },
14934       'contains': function(node, pos) {
14935         var npos = node.pos.getc(true),
14936             dim = node.getData('dim');
14937         return this.nodeHelper.star.contains(npos, pos, dim);
14938       }
14939     }
14940   });
14941
14942   /*
14943     Class: ForceDirected.Plot.EdgeTypes
14944   
14945     This class contains a list of <Graph.Adjacence> built-in types. 
14946     Edge types implemented are 'none', 'line' and 'arrow'.
14947   
14948     You can add your custom edge types, customizing your visualization to the extreme.
14949   
14950     Example:
14951   
14952     (start code js)
14953       ForceDirected.Plot.EdgeTypes.implement({
14954         'mySpecialType': {
14955           'render': function(adj, canvas) {
14956             //print your custom edge to canvas
14957           },
14958           //optional
14959           'contains': function(adj, pos) {
14960             //return true if pos is inside the arc or false otherwise
14961           }
14962         }
14963       });
14964     (end code)
14965   
14966   */
14967   ForceDirected.Plot.EdgeTypes = new Class({
14968     'none': $.empty,
14969     'line': {
14970       'render': function(adj, canvas) {
14971         var from = adj.nodeFrom.pos.getc(true),
14972             to = adj.nodeTo.pos.getc(true);
14973         this.edgeHelper.line.render(from, to, canvas);
14974       },
14975       'contains': function(adj, pos) {
14976         var from = adj.nodeFrom.pos.getc(true),
14977             to = adj.nodeTo.pos.getc(true);
14978         return this.edgeHelper.line.contains(from, to, pos, this.edge.epsilon);
14979       }
14980     },
14981     'arrow': {
14982       'render': function(adj, canvas) {
14983         var from = adj.nodeFrom.pos.getc(true),
14984             to = adj.nodeTo.pos.getc(true),
14985             dim = adj.getData('dim'),
14986             direction = adj.data.$direction,
14987             inv = (direction && direction.length>1 && direction[0] != adj.nodeFrom.id);
14988         this.edgeHelper.arrow.render(from, to, dim, inv, canvas);
14989       },
14990       'contains': function(adj, pos) {
14991         var from = adj.nodeFrom.pos.getc(true),
14992             to = adj.nodeTo.pos.getc(true);
14993         return this.edgeHelper.arrow.contains(from, to, pos, this.edge.epsilon);
14994       }
14995     }
14996   });
14997
14998 })($jit.ForceDirected);
14999
15000
15001 /*
15002  * File: Treemap.js
15003  *
15004 */
15005
15006 $jit.TM = {};
15007
15008 var TM = $jit.TM;
15009
15010 $jit.TM.$extend = true;
15011
15012 /*
15013   Class: TM.Base
15014   
15015   Abstract class providing base functionality for <TM.Squarified>, <TM.Strip> and <TM.SliceAndDice> visualizations.
15016   
15017   Implements:
15018   
15019   All <Loader> methods
15020   
15021   Constructor Options:
15022   
15023   Inherits options from
15024   
15025   - <Options.Canvas>
15026   - <Options.Controller>
15027   - <Options.Node>
15028   - <Options.Edge>
15029   - <Options.Label>
15030   - <Options.Events>
15031   - <Options.Tips>
15032   - <Options.NodeStyles>
15033   - <Options.Navigation>
15034   
15035   Additionally, there are other parameters and some default values changed
15036
15037   orientation - (string) Default's *h*. Whether to set horizontal or vertical layouts. Possible values are 'h' and 'v'.
15038   titleHeight - (number) Default's *13*. The height of the title rectangle for inner (non-leaf) nodes.
15039   offset - (number) Default's *2*. Boxes offset.
15040   constrained - (boolean) Default's *false*. Whether to show the entire tree when loaded or just the number of levels specified by _levelsToShow_.
15041   levelsToShow - (number) Default's *3*. The number of levels to show for a subtree. This number is relative to the selected node.
15042   animate - (boolean) Default's *false*. Whether to animate transitions.
15043   Node.type - Described in <Options.Node>. Default's *rectangle*.
15044   duration - Described in <Options.Fx>. Default's *700*.
15045   fps - Described in <Options.Fx>. Default's *45*.
15046   
15047   Instance Properties:
15048   
15049   canvas - Access a <Canvas> instance.
15050   graph - Access a <Graph> instance.
15051   op - Access a <TM.Op> instance.
15052   fx - Access a <TM.Plot> instance.
15053   labels - Access a <TM.Label> interface implementation.
15054
15055   Inspired by:
15056   
15057   Squarified Treemaps (Mark Bruls, Kees Huizing, and Jarke J. van Wijk) <http://www.win.tue.nl/~vanwijk/stm.pdf>
15058   
15059   Tree visualization with tree-maps: 2-d space-filling approach (Ben Shneiderman) <http://hcil.cs.umd.edu/trs/91-03/91-03.html>
15060   
15061    Note:
15062    
15063    This visualization was built and engineered from scratch, taking only the paper as inspiration, and only shares some features with the visualization described in the paper.
15064
15065 */
15066 TM.Base = {
15067   layout: {
15068     orientation: "h",
15069     vertical: function(){
15070       return this.orientation == "v";
15071     },
15072     horizontal: function(){
15073       return this.orientation == "h";
15074     },
15075     change: function(){
15076       this.orientation = this.vertical()? "h" : "v";
15077     }
15078   },
15079
15080   initialize: function(controller){
15081     var config = {
15082       orientation: "h",
15083       titleHeight: 13,
15084       offset: 2,
15085       levelsToShow: 0,
15086       constrained: false,
15087       animate: false,
15088       Node: {
15089         type: 'rectangle',
15090         overridable: true,
15091         //we all know why this is not zero,
15092         //right, Firefox?
15093         width: 3,
15094         height: 3,
15095         color: '#444'
15096       },
15097       Label: {
15098         textAlign: 'center',
15099         textBaseline: 'top'
15100       },
15101       Edge: {
15102         type: 'none'
15103       },
15104       duration: 700,
15105       fps: 45
15106     };
15107
15108     this.controller = this.config = $.merge(Options("Canvas", "Node", "Edge",
15109         "Fx", "Controller", "Tips", "NodeStyles", "Events", "Navigation", "Label"), config, controller);
15110     this.layout.orientation = this.config.orientation;
15111
15112     var canvasConfig = this.config;
15113     if (canvasConfig.useCanvas) {
15114       this.canvas = canvasConfig.useCanvas;
15115       this.config.labelContainer = this.canvas.id + '-label';
15116     } else {
15117       if(canvasConfig.background) {
15118         canvasConfig.background = $.merge({
15119           type: 'Circles'
15120         }, canvasConfig.background);
15121       }
15122       this.canvas = new Canvas(this, canvasConfig);
15123       this.config.labelContainer = (typeof canvasConfig.injectInto == 'string'? canvasConfig.injectInto : canvasConfig.injectInto.id) + '-label';
15124     }
15125
15126     this.graphOptions = {
15127       'klass': Complex,
15128       'Node': {
15129         'selected': false,
15130         'exist': true,
15131         'drawn': true
15132       }
15133     };
15134     this.graph = new Graph(this.graphOptions, this.config.Node,
15135         this.config.Edge);
15136     this.labels = new TM.Label[canvasConfig.Label.type](this);
15137     this.fx = new TM.Plot(this);
15138     this.op = new TM.Op(this);
15139     this.group = new TM.Group(this);
15140     this.geom = new TM.Geom(this);
15141     this.clickedNode = null;
15142     this.busy = false;
15143     // initialize extras
15144     this.initializeExtras();
15145   },
15146
15147   /* 
15148     Method: refresh 
15149     
15150     Computes positions and plots the tree.
15151   */
15152   refresh: function(){
15153     if(this.busy) return;
15154     this.busy = true;
15155     var that = this;
15156     if(this.config.animate) {
15157       this.compute('end');
15158       this.config.levelsToShow > 0 && this.geom.setRightLevelToShow(this.graph.getNode(this.clickedNode 
15159           && this.clickedNode.id || this.root));
15160       this.fx.animate($.merge(this.config, {
15161         modes: ['linear', 'node-property:width:height'],
15162         onComplete: function() {
15163           that.busy = false;
15164         }
15165       }));
15166     } else {
15167       var labelType = this.config.Label.type;
15168       if(labelType != 'Native') {
15169         var that = this;
15170         this.graph.eachNode(function(n) { that.labels.hideLabel(n, false); });
15171       }
15172       this.busy = false;
15173       this.compute();
15174       this.config.levelsToShow > 0 && this.geom.setRightLevelToShow(this.graph.getNode(this.clickedNode 
15175           && this.clickedNode.id || this.root));
15176       this.plot();
15177     }
15178   },
15179
15180   /* 
15181     Method: plot 
15182     
15183     Plots the TreeMap. This is a shortcut to *fx.plot*. 
15184   
15185    */
15186   plot: function(){
15187     this.fx.plot();
15188   },
15189
15190   /* 
15191   Method: leaf 
15192   
15193   Returns whether the node is a leaf.
15194   
15195    Parameters:
15196    
15197    n - (object) A <Graph.Node>.
15198
15199  */
15200   leaf: function(n){
15201     return n.getSubnodes([
15202         1, 1
15203     ], "ignore").length == 0;
15204   },
15205   
15206   /* 
15207   Method: enter 
15208   
15209   Sets the node as root.
15210   
15211    Parameters:
15212    
15213    n - (object) A <Graph.Node>.
15214
15215  */
15216   enter: function(n){
15217     if(this.busy) return;
15218     this.busy = true;
15219     
15220     var that = this,
15221         config = this.config,
15222         graph = this.graph,
15223         clickedNode = n,
15224         previousClickedNode = this.clickedNode;
15225
15226     var callback = {
15227       onComplete: function() {
15228         //ensure that nodes are shown for that level
15229         if(config.levelsToShow > 0) {
15230           that.geom.setRightLevelToShow(n);
15231         }
15232         //compute positions of newly inserted nodes
15233         if(config.levelsToShow > 0 || config.request) that.compute();
15234         if(config.animate) {
15235           //fade nodes
15236           graph.nodeList.setData('alpha', 0, 'end');
15237           n.eachSubgraph(function(n) {
15238             n.setData('alpha', 1, 'end');
15239           }, "ignore");
15240           that.fx.animate({
15241             duration: 500,
15242             modes:['node-property:alpha'],
15243             onComplete: function() {
15244               //compute end positions
15245               that.clickedNode = clickedNode;
15246               that.compute('end');
15247               //animate positions
15248               //TODO(nico) commenting this line didn't seem to throw errors...
15249               that.clickedNode = previousClickedNode;
15250               that.fx.animate({
15251                 modes:['linear', 'node-property:width:height'],
15252                 duration: 1000,
15253                 onComplete: function() { 
15254                   that.busy = false;
15255                   //TODO(nico) check comment above
15256                   that.clickedNode = clickedNode;
15257                 }
15258               });
15259             }
15260           });
15261         } else {
15262           that.busy = false;
15263           that.clickedNode = n;
15264           that.refresh();
15265         }
15266       }
15267     };
15268     if(config.request) {
15269       this.requestNodes(clickedNode, callback);
15270     } else {
15271       callback.onComplete();
15272     }
15273   },
15274
15275   /* 
15276   Method: out 
15277   
15278   Sets the parent node of the current selected node as root.
15279
15280  */
15281   out: function(){
15282     if(this.busy) return;
15283     this.busy = true;
15284     this.events.hoveredNode = false;
15285     var that = this,
15286         config = this.config,
15287         graph = this.graph,
15288         parents = graph.getNode(this.clickedNode 
15289             && this.clickedNode.id || this.root).getParents(),
15290         parent = parents[0],
15291         clickedNode = parent,
15292         previousClickedNode = this.clickedNode;
15293     
15294     //if no parents return
15295     if(!parent) {
15296       this.busy = false;
15297       return;
15298     }
15299     //final plot callback
15300     callback = {
15301       onComplete: function() {
15302         that.clickedNode = parent;
15303         if(config.request) {
15304           that.requestNodes(parent, {
15305             onComplete: function() {
15306               that.compute();
15307               that.plot();
15308               that.busy = false;
15309             }
15310           });
15311         } else {
15312           that.compute();
15313           that.plot();
15314           that.busy = false;
15315         }
15316       }
15317     };
15318     //prune tree
15319     if (config.levelsToShow > 0)
15320       this.geom.setRightLevelToShow(parent);
15321     //animate node positions
15322     if(config.animate) {
15323       this.clickedNode = clickedNode;
15324       this.compute('end');
15325       //animate the visible subtree only
15326       this.clickedNode = previousClickedNode;
15327       this.fx.animate({
15328         modes:['linear', 'node-property:width:height'],
15329         duration: 1000,
15330         onComplete: function() {
15331           //animate the parent subtree
15332           that.clickedNode = clickedNode;
15333           //change nodes alpha
15334           graph.eachNode(function(n) {
15335             n.setDataset(['current', 'end'], {
15336               'alpha': [0, 1]
15337             });
15338           }, "ignore");
15339           previousClickedNode.eachSubgraph(function(node) {
15340             node.setData('alpha', 1);
15341           }, "ignore");
15342           that.fx.animate({
15343             duration: 500,
15344             modes:['node-property:alpha'],
15345             onComplete: function() {
15346               callback.onComplete();
15347             }
15348           });
15349         }
15350       });
15351     } else {
15352       callback.onComplete();
15353     }
15354   },
15355
15356   requestNodes: function(node, onComplete){
15357     var handler = $.merge(this.controller, onComplete), 
15358         lev = this.config.levelsToShow;
15359     if (handler.request) {
15360       var leaves = [], d = node._depth;
15361       node.eachLevel(0, lev, function(n){
15362         var nodeLevel = lev - (n._depth - d);
15363         if (n.drawn && !n.anySubnode() && nodeLevel > 0) {
15364           leaves.push(n);
15365           n._level = nodeLevel;
15366         }
15367       });
15368       this.group.requestNodes(leaves, handler);
15369     } else {
15370       handler.onComplete();
15371     }
15372   },
15373   
15374   reposition: function() {
15375     this.compute('end');
15376   }
15377 };
15378
15379 /*
15380   Class: TM.Op
15381   
15382   Custom extension of <Graph.Op>.
15383   
15384   Extends:
15385   
15386   All <Graph.Op> methods
15387   
15388   See also:
15389   
15390   <Graph.Op>
15391   
15392   */
15393 TM.Op = new Class({
15394   Implements: Graph.Op,
15395
15396   initialize: function(viz){
15397     this.viz = viz;
15398   }
15399 });
15400
15401 //extend level methods of Graph.Geom
15402 TM.Geom = new Class({
15403   Implements: Graph.Geom,
15404   
15405   getRightLevelToShow: function() {
15406     return this.viz.config.levelsToShow;
15407   },
15408   
15409   setRightLevelToShow: function(node) {
15410     var level = this.getRightLevelToShow(), 
15411         fx = this.viz.labels;
15412     node.eachLevel(0, level+1, function(n) {
15413       var d = n._depth - node._depth;
15414       if(d > level) {
15415         n.drawn = false; 
15416         n.exist = false;
15417         n.ignore = true;
15418         fx.hideLabel(n, false);
15419       } else {
15420         n.drawn = true;
15421         n.exist = true;
15422         delete n.ignore;
15423       }
15424     });
15425     node.drawn = true;
15426     delete node.ignore;
15427   }
15428 });
15429
15430 /*
15431
15432 Performs operations on group of nodes.
15433
15434 */
15435 TM.Group = new Class( {
15436
15437   initialize: function(viz){
15438     this.viz = viz;
15439     this.canvas = viz.canvas;
15440     this.config = viz.config;
15441   },
15442
15443   /*
15444   
15445     Calls the request method on the controller to request a subtree for each node. 
15446   */
15447   requestNodes: function(nodes, controller){
15448     var counter = 0, len = nodes.length, nodeSelected = {};
15449     var complete = function(){
15450       controller.onComplete();
15451     };
15452     var viz = this.viz;
15453     if (len == 0)
15454       complete();
15455     for ( var i = 0; i < len; i++) {
15456       nodeSelected[nodes[i].id] = nodes[i];
15457       controller.request(nodes[i].id, nodes[i]._level, {
15458         onComplete: function(nodeId, data){
15459           if (data && data.children) {
15460             data.id = nodeId;
15461             viz.op.sum(data, {
15462               type: 'nothing'
15463             });
15464           }
15465           if (++counter == len) {
15466             viz.graph.computeLevels(viz.root, 0);
15467             complete();
15468           }
15469         }
15470       });
15471     }
15472   }
15473 });
15474
15475 /*
15476   Class: TM.Plot
15477   
15478   Custom extension of <Graph.Plot>.
15479   
15480   Extends:
15481   
15482   All <Graph.Plot> methods
15483   
15484   See also:
15485   
15486   <Graph.Plot>
15487   
15488   */
15489 TM.Plot = new Class({
15490
15491   Implements: Graph.Plot,
15492
15493   initialize: function(viz){
15494     this.viz = viz;
15495     this.config = viz.config;
15496     this.node = this.config.Node;
15497     this.edge = this.config.Edge;
15498     this.animation = new Animation;
15499     this.nodeTypes = new TM.Plot.NodeTypes;
15500     this.edgeTypes = new TM.Plot.EdgeTypes;
15501     this.labels = viz.labels;
15502   },
15503
15504   plot: function(opt, animating){
15505     var viz = this.viz, 
15506         graph = viz.graph;
15507     viz.canvas.clear();
15508     this.plotTree(graph.getNode(viz.clickedNode && viz.clickedNode.id || viz.root), $.merge(viz.config, opt || {}, {
15509       'withLabels': true,
15510       'hideLabels': false,
15511       'plotSubtree': function(n, ch){
15512         return n.anySubnode("exist");
15513       }
15514     }), animating);
15515   }
15516 });
15517
15518 /*
15519   Class: TM.Label
15520   
15521   Custom extension of <Graph.Label>. 
15522   Contains custom <Graph.Label.SVG>, <Graph.Label.HTML> and <Graph.Label.Native> extensions.
15523
15524   Extends:
15525
15526   All <Graph.Label> methods and subclasses.
15527
15528   See also:
15529
15530   <Graph.Label>, <Graph.Label.Native>, <Graph.Label.HTML>, <Graph.Label.SVG>.
15531   
15532 */
15533 TM.Label = {};
15534
15535 /*
15536  TM.Label.Native
15537
15538  Custom extension of <Graph.Label.Native>.
15539
15540  Extends:
15541
15542  All <Graph.Label.Native> methods
15543
15544  See also:
15545
15546  <Graph.Label.Native>
15547 */
15548 TM.Label.Native = new Class({
15549   Implements: Graph.Label.Native,
15550
15551   initialize: function(viz) {
15552     this.config = viz.config;
15553     this.leaf = viz.leaf;
15554   },
15555   
15556   renderLabel: function(canvas, node, controller){
15557     if(!this.leaf(node) && !this.config.titleHeight) return;
15558     var pos = node.pos.getc(true), 
15559         ctx = canvas.getCtx(),
15560         width = node.getData('width'),
15561         height = node.getData('height'),
15562         x = pos.x + width/2,
15563         y = pos.y;
15564         
15565     ctx.fillText(node.name, x, y, width);
15566   }
15567 });
15568
15569 /*
15570  TM.Label.SVG
15571
15572   Custom extension of <Graph.Label.SVG>.
15573
15574   Extends:
15575
15576   All <Graph.Label.SVG> methods
15577
15578   See also:
15579
15580   <Graph.Label.SVG>
15581 */
15582 TM.Label.SVG = new Class( {
15583   Implements: Graph.Label.SVG,
15584
15585   initialize: function(viz){
15586     this.viz = viz;
15587     this.leaf = viz.leaf;
15588     this.config = viz.config;
15589   },
15590
15591   /* 
15592   placeLabel
15593
15594   Overrides abstract method placeLabel in <Graph.Plot>.
15595
15596   Parameters:
15597
15598   tag - A DOM label element.
15599   node - A <Graph.Node>.
15600   controller - A configuration/controller object passed to the visualization.
15601   
15602   */
15603   placeLabel: function(tag, node, controller){
15604     var pos = node.pos.getc(true), 
15605         canvas = this.viz.canvas,
15606         ox = canvas.translateOffsetX,
15607         oy = canvas.translateOffsetY,
15608         sx = canvas.scaleOffsetX,
15609         sy = canvas.scaleOffsetY,
15610         radius = canvas.getSize();
15611     var labelPos = {
15612       x: Math.round(pos.x * sx + ox + radius.width / 2),
15613       y: Math.round(pos.y * sy + oy + radius.height / 2)
15614     };
15615     tag.setAttribute('x', labelPos.x);
15616     tag.setAttribute('y', labelPos.y);
15617
15618     if(!this.leaf(node) && !this.config.titleHeight) {
15619       tag.style.display = 'none';
15620     }
15621     controller.onPlaceLabel(tag, node);
15622   }
15623 });
15624
15625 /*
15626  TM.Label.HTML
15627
15628  Custom extension of <Graph.Label.HTML>.
15629
15630  Extends:
15631
15632  All <Graph.Label.HTML> methods.
15633
15634  See also:
15635
15636  <Graph.Label.HTML>
15637
15638 */
15639 TM.Label.HTML = new Class( {
15640   Implements: Graph.Label.HTML,
15641
15642   initialize: function(viz){
15643     this.viz = viz;
15644     this.leaf = viz.leaf;
15645     this.config = viz.config;
15646   },
15647
15648   /* 
15649     placeLabel
15650   
15651     Overrides abstract method placeLabel in <Graph.Plot>.
15652   
15653     Parameters:
15654   
15655     tag - A DOM label element.
15656     node - A <Graph.Node>.
15657     controller - A configuration/controller object passed to the visualization.
15658   
15659   */
15660   placeLabel: function(tag, node, controller){
15661     var pos = node.pos.getc(true), 
15662         canvas = this.viz.canvas,
15663         ox = canvas.translateOffsetX,
15664         oy = canvas.translateOffsetY,
15665         sx = canvas.scaleOffsetX,
15666         sy = canvas.scaleOffsetY,
15667         radius = canvas.getSize();
15668     var labelPos = {
15669       x: Math.round(pos.x * sx + ox + radius.width / 2),
15670       y: Math.round(pos.y * sy + oy + radius.height / 2)
15671     };
15672
15673     var style = tag.style;
15674     style.left = labelPos.x + 'px';
15675     style.top = labelPos.y + 'px';
15676     style.width = node.getData('width') * sx + 'px';
15677     style.height = node.getData('height') * sy + 'px';
15678     style.zIndex = node._depth * 100;
15679     style.display = '';
15680
15681     if(!this.leaf(node) && !this.config.titleHeight) {
15682       tag.style.display = 'none';
15683     }
15684     controller.onPlaceLabel(tag, node);
15685   }
15686 });
15687
15688 /*
15689   Class: TM.Plot.NodeTypes
15690
15691   This class contains a list of <Graph.Node> built-in types. 
15692   Node types implemented are 'none', 'rectangle'.
15693
15694   You can add your custom node types, customizing your visualization to the extreme.
15695
15696   Example:
15697
15698   (start code js)
15699     TM.Plot.NodeTypes.implement({
15700       'mySpecialType': {
15701         'render': function(node, canvas) {
15702           //print your custom node to canvas
15703         },
15704         //optional
15705         'contains': function(node, pos) {
15706           //return true if pos is inside the node or false otherwise
15707         }
15708       }
15709     });
15710   (end code)
15711
15712 */
15713 TM.Plot.NodeTypes = new Class( {
15714   'none': {
15715     'render': $.empty
15716   },
15717
15718   'rectangle': {
15719     'render': function(node, canvas, animating){
15720       var leaf = this.viz.leaf(node),
15721           config = this.config,
15722           offst = config.offset,
15723           titleHeight = config.titleHeight,
15724           pos = node.pos.getc(true),
15725           width = node.getData('width'),
15726           height = node.getData('height'),
15727           border = node.getData('border'),
15728           ctx = canvas.getCtx(),
15729           posx = pos.x + offst / 2, 
15730           posy = pos.y + offst / 2;
15731       if(width <= offst || height <= offst) return;
15732       if (leaf) {
15733         if(config.cushion) {
15734           var lg = ctx.createRadialGradient(posx + (width-offst)/2, posy + (height-offst)/2, 1, 
15735               posx + (width-offst)/2, posy + (height-offst)/2, width < height? height : width);
15736           var color = node.getData('color');
15737           var colorGrad = $.rgbToHex($.map($.hexToRgb(color), 
15738               function(r) { return r * 0.2 >> 0; }));
15739           lg.addColorStop(0, color);
15740           lg.addColorStop(1, colorGrad);
15741           ctx.fillStyle = lg;
15742         }
15743         ctx.fillRect(posx, posy, width - offst, height - offst);
15744         if(border) {
15745           ctx.save();
15746           ctx.strokeStyle = border;
15747           ctx.strokeRect(posx, posy, width - offst, height - offst);
15748           ctx.restore();
15749         }
15750       } else if(titleHeight > 0){
15751         ctx.fillRect(pos.x + offst / 2, pos.y + offst / 2, width - offst,
15752             titleHeight - offst);
15753         if(border) {
15754           ctx.save();
15755           ctx.strokeStyle = border;
15756           ctx.strokeRect(pos.x + offst / 2, pos.y + offst / 2, width - offst,
15757               height - offst);
15758           ctx.restore();
15759         }
15760       }
15761     },
15762     'contains': function(node, pos) {
15763       if(this.viz.clickedNode && !node.isDescendantOf(this.viz.clickedNode.id) || node.ignore) return false;
15764       var npos = node.pos.getc(true),
15765           width = node.getData('width'), 
15766           leaf = this.viz.leaf(node),
15767           height = leaf? node.getData('height') : this.config.titleHeight;
15768       return this.nodeHelper.rectangle.contains({x: npos.x + width/2, y: npos.y + height/2}, pos, width, height);
15769     }
15770   }
15771 });
15772
15773 TM.Plot.EdgeTypes = new Class( {
15774   'none': $.empty
15775 });
15776
15777 /*
15778   Class: TM.SliceAndDice
15779   
15780   A slice and dice TreeMap visualization.
15781   
15782   Implements:
15783   
15784   All <TM.Base> methods and properties.
15785 */
15786 TM.SliceAndDice = new Class( {
15787   Implements: [
15788       Loader, Extras, TM.Base, Layouts.TM.SliceAndDice
15789   ]
15790 });
15791
15792 /*
15793   Class: TM.Squarified
15794   
15795   A squarified TreeMap visualization.
15796
15797   Implements:
15798   
15799   All <TM.Base> methods and properties.
15800 */
15801 TM.Squarified = new Class( {
15802   Implements: [
15803       Loader, Extras, TM.Base, Layouts.TM.Squarified
15804   ]
15805 });
15806
15807 /*
15808   Class: TM.Strip
15809   
15810   A strip TreeMap visualization.
15811
15812   Implements:
15813   
15814   All <TM.Base> methods and properties.
15815 */
15816 TM.Strip = new Class( {
15817   Implements: [
15818       Loader, Extras, TM.Base, Layouts.TM.Strip
15819   ]
15820 });
15821
15822
15823 /*
15824  * File: RGraph.js
15825  *
15826  */
15827
15828 /*
15829    Class: RGraph
15830    
15831    A radial graph visualization with advanced animations.
15832    
15833    Inspired by:
15834  
15835    Animated Exploration of Dynamic Graphs with Radial Layout (Ka-Ping Yee, Danyel Fisher, Rachna Dhamija, Marti Hearst) <http://bailando.sims.berkeley.edu/papers/infovis01.htm>
15836    
15837    Note:
15838    
15839    This visualization was built and engineered from scratch, taking only the paper as inspiration, and only shares some features with the visualization described in the paper.
15840    
15841   Implements:
15842   
15843   All <Loader> methods
15844   
15845    Constructor Options:
15846    
15847    Inherits options from
15848    
15849    - <Options.Canvas>
15850    - <Options.Controller>
15851    - <Options.Node>
15852    - <Options.Edge>
15853    - <Options.Label>
15854    - <Options.Events>
15855    - <Options.Tips>
15856    - <Options.NodeStyles>
15857    - <Options.Navigation>
15858    
15859    Additionally, there are other parameters and some default values changed
15860    
15861    interpolation - (string) Default's *linear*. Describes the way nodes are interpolated. Possible values are 'linear' and 'polar'.
15862    levelDistance - (number) Default's *100*. The distance between levels of the tree. 
15863      
15864    Instance Properties:
15865
15866    canvas - Access a <Canvas> instance.
15867    graph - Access a <Graph> instance.
15868    op - Access a <RGraph.Op> instance.
15869    fx - Access a <RGraph.Plot> instance.
15870    labels - Access a <RGraph.Label> interface implementation.   
15871 */
15872
15873 $jit.RGraph = new Class( {
15874
15875   Implements: [
15876       Loader, Extras, Layouts.Radial
15877   ],
15878
15879   initialize: function(controller){
15880     var $RGraph = $jit.RGraph;
15881
15882     var config = {
15883       interpolation: 'linear',
15884       levelDistance: 100
15885     };
15886
15887     this.controller = this.config = $.merge(Options("Canvas", "Node", "Edge",
15888         "Fx", "Controller", "Tips", "NodeStyles", "Events", "Navigation", "Label"), config, controller);
15889
15890     var canvasConfig = this.config;
15891     if(canvasConfig.useCanvas) {
15892       this.canvas = canvasConfig.useCanvas;
15893       this.config.labelContainer = this.canvas.id + '-label';
15894     } else {
15895       if(canvasConfig.background) {
15896         canvasConfig.background = $.merge({
15897           type: 'Circles'
15898         }, canvasConfig.background);
15899       }
15900       this.canvas = new Canvas(this, canvasConfig);
15901       this.config.labelContainer = (typeof canvasConfig.injectInto == 'string'? canvasConfig.injectInto : canvasConfig.injectInto.id) + '-label';
15902     }
15903
15904     this.graphOptions = {
15905       'klass': Polar,
15906       'Node': {
15907         'selected': false,
15908         'exist': true,
15909         'drawn': true
15910       }
15911     };
15912     this.graph = new Graph(this.graphOptions, this.config.Node,
15913         this.config.Edge);
15914     this.labels = new $RGraph.Label[canvasConfig.Label.type](this);
15915     this.fx = new $RGraph.Plot(this, $RGraph);
15916     this.op = new $RGraph.Op(this);
15917     this.json = null;
15918     this.root = null;
15919     this.busy = false;
15920     this.parent = false;
15921     // initialize extras
15922     this.initializeExtras();
15923   },
15924
15925   /* 
15926   
15927     createLevelDistanceFunc 
15928   
15929     Returns the levelDistance function used for calculating a node distance 
15930     to its origin. This function returns a function that is computed 
15931     per level and not per node, such that all nodes with the same depth will have the 
15932     same distance to the origin. The resulting function gets the 
15933     parent node as parameter and returns a float.
15934
15935    */
15936   createLevelDistanceFunc: function(){
15937     var ld = this.config.levelDistance;
15938     return function(elem){
15939       return (elem._depth + 1) * ld;
15940     };
15941   },
15942
15943   /* 
15944      Method: refresh 
15945      
15946      Computes positions and plots the tree.
15947
15948    */
15949   refresh: function(){
15950     this.compute();
15951     this.plot();
15952   },
15953
15954   reposition: function(){
15955     this.compute('end');
15956   },
15957
15958   /*
15959    Method: plot
15960   
15961    Plots the RGraph. This is a shortcut to *fx.plot*.
15962   */
15963   plot: function(){
15964     this.fx.plot();
15965   },
15966   /*
15967    getNodeAndParentAngle
15968   
15969    Returns the _parent_ of the given node, also calculating its angle span.
15970   */
15971   getNodeAndParentAngle: function(id){
15972     var theta = false;
15973     var n = this.graph.getNode(id);
15974     var ps = n.getParents();
15975     var p = (ps.length > 0)? ps[0] : false;
15976     if (p) {
15977       var posParent = p.pos.getc(), posChild = n.pos.getc();
15978       var newPos = posParent.add(posChild.scale(-1));
15979       theta = Math.atan2(newPos.y, newPos.x);
15980       if (theta < 0)
15981         theta += 2 * Math.PI;
15982     }
15983     return {
15984       parent: p,
15985       theta: theta
15986     };
15987   },
15988   /*
15989    tagChildren
15990   
15991    Enumerates the children in order to maintain child ordering (second constraint of the paper).
15992   */
15993   tagChildren: function(par, id){
15994     if (par.angleSpan) {
15995       var adjs = [];
15996       par.eachAdjacency(function(elem){
15997         adjs.push(elem.nodeTo);
15998       }, "ignore");
15999       var len = adjs.length;
16000       for ( var i = 0; i < len && id != adjs[i].id; i++)
16001         ;
16002       for ( var j = (i + 1) % len, k = 0; id != adjs[j].id; j = (j + 1) % len) {
16003         adjs[j].dist = k++;
16004       }
16005     }
16006   },
16007   /* 
16008   Method: onClick 
16009   
16010   Animates the <RGraph> to center the node specified by *id*.
16011
16012    Parameters:
16013
16014    id - A <Graph.Node> id.
16015    opt - (optional|object) An object containing some extra properties described below
16016    hideLabels - (boolean) Default's *true*. Hide labels when performing the animation.
16017
16018    Example:
16019
16020    (start code js)
16021      rgraph.onClick('someid');
16022      //or also...
16023      rgraph.onClick('someid', {
16024       hideLabels: false
16025      });
16026     (end code)
16027     
16028   */
16029   onClick: function(id, opt){
16030     if (this.root != id && !this.busy) {
16031       this.busy = true;
16032       this.root = id;
16033       var that = this;
16034       this.controller.onBeforeCompute(this.graph.getNode(id));
16035       var obj = this.getNodeAndParentAngle(id);
16036
16037       // second constraint
16038       this.tagChildren(obj.parent, id);
16039       this.parent = obj.parent;
16040       this.compute('end');
16041
16042       // first constraint
16043       var thetaDiff = obj.theta - obj.parent.endPos.theta;
16044       this.graph.eachNode(function(elem){
16045         elem.endPos.set(elem.endPos.getp().add($P(thetaDiff, 0)));
16046       });
16047
16048       var mode = this.config.interpolation;
16049       opt = $.merge( {
16050         onComplete: $.empty
16051       }, opt || {});
16052
16053       this.fx.animate($.merge( {
16054         hideLabels: true,
16055         modes: [
16056           mode
16057         ]
16058       }, opt, {
16059         onComplete: function(){
16060           that.busy = false;
16061           opt.onComplete();
16062         }
16063       }));
16064     }
16065   }
16066 });
16067
16068 $jit.RGraph.$extend = true;
16069
16070 (function(RGraph){
16071
16072   /*
16073      Class: RGraph.Op
16074      
16075      Custom extension of <Graph.Op>.
16076
16077      Extends:
16078
16079      All <Graph.Op> methods
16080      
16081      See also:
16082      
16083      <Graph.Op>
16084
16085   */
16086   RGraph.Op = new Class( {
16087
16088     Implements: Graph.Op
16089
16090   });
16091
16092   /*
16093      Class: RGraph.Plot
16094     
16095     Custom extension of <Graph.Plot>.
16096   
16097     Extends:
16098   
16099     All <Graph.Plot> methods
16100     
16101     See also:
16102     
16103     <Graph.Plot>
16104   
16105   */
16106   RGraph.Plot = new Class( {
16107
16108     Implements: Graph.Plot
16109
16110   });
16111
16112   /*
16113     Object: RGraph.Label
16114
16115     Custom extension of <Graph.Label>. 
16116     Contains custom <Graph.Label.SVG>, <Graph.Label.HTML> and <Graph.Label.Native> extensions.
16117   
16118     Extends:
16119   
16120     All <Graph.Label> methods and subclasses.
16121   
16122     See also:
16123   
16124     <Graph.Label>, <Graph.Label.Native>, <Graph.Label.HTML>, <Graph.Label.SVG>.
16125   
16126    */
16127   RGraph.Label = {};
16128
16129   /*
16130      RGraph.Label.Native
16131
16132      Custom extension of <Graph.Label.Native>.
16133
16134      Extends:
16135
16136      All <Graph.Label.Native> methods
16137
16138      See also:
16139
16140      <Graph.Label.Native>
16141
16142   */
16143   RGraph.Label.Native = new Class( {
16144     Implements: Graph.Label.Native
16145   });
16146
16147   /*
16148      RGraph.Label.SVG
16149     
16150     Custom extension of <Graph.Label.SVG>.
16151   
16152     Extends:
16153   
16154     All <Graph.Label.SVG> methods
16155   
16156     See also:
16157   
16158     <Graph.Label.SVG>
16159   
16160   */
16161   RGraph.Label.SVG = new Class( {
16162     Implements: Graph.Label.SVG,
16163
16164     initialize: function(viz){
16165       this.viz = viz;
16166     },
16167
16168     /* 
16169        placeLabel
16170
16171        Overrides abstract method placeLabel in <Graph.Plot>.
16172
16173        Parameters:
16174
16175        tag - A DOM label element.
16176        node - A <Graph.Node>.
16177        controller - A configuration/controller object passed to the visualization.
16178       
16179      */
16180     placeLabel: function(tag, node, controller){
16181       var pos = node.pos.getc(true), 
16182           canvas = this.viz.canvas,
16183           ox = canvas.translateOffsetX,
16184           oy = canvas.translateOffsetY,
16185           sx = canvas.scaleOffsetX,
16186           sy = canvas.scaleOffsetY,
16187           radius = canvas.getSize();
16188       var labelPos = {
16189         x: Math.round(pos.x * sx + ox + radius.width / 2),
16190         y: Math.round(pos.y * sy + oy + radius.height / 2)
16191       };
16192       tag.setAttribute('x', labelPos.x);
16193       tag.setAttribute('y', labelPos.y);
16194
16195       controller.onPlaceLabel(tag, node);
16196     }
16197   });
16198
16199   /*
16200      RGraph.Label.HTML
16201
16202      Custom extension of <Graph.Label.HTML>.
16203
16204      Extends:
16205
16206      All <Graph.Label.HTML> methods.
16207
16208      See also:
16209
16210      <Graph.Label.HTML>
16211
16212   */
16213   RGraph.Label.HTML = new Class( {
16214     Implements: Graph.Label.HTML,
16215
16216     initialize: function(viz){
16217       this.viz = viz;
16218     },
16219     /* 
16220        placeLabel
16221
16222        Overrides abstract method placeLabel in <Graph.Plot>.
16223
16224        Parameters:
16225
16226        tag - A DOM label element.
16227        node - A <Graph.Node>.
16228        controller - A configuration/controller object passed to the visualization.
16229       
16230      */
16231     placeLabel: function(tag, node, controller){
16232       var pos = node.pos.getc(true), 
16233           canvas = this.viz.canvas,
16234           ox = canvas.translateOffsetX,
16235           oy = canvas.translateOffsetY,
16236           sx = canvas.scaleOffsetX,
16237           sy = canvas.scaleOffsetY,
16238           radius = canvas.getSize();
16239       var labelPos = {
16240         x: Math.round(pos.x * sx + ox + radius.width / 2),
16241         y: Math.round(pos.y * sy + oy + radius.height / 2)
16242       };
16243
16244       var style = tag.style;
16245       style.left = labelPos.x + 'px';
16246       style.top = labelPos.y + 'px';
16247       style.display = this.fitsInCanvas(labelPos, canvas)? '' : 'none';
16248
16249       controller.onPlaceLabel(tag, node);
16250     }
16251   });
16252
16253   /*
16254     Class: RGraph.Plot.NodeTypes
16255
16256     This class contains a list of <Graph.Node> built-in types. 
16257     Node types implemented are 'none', 'circle', 'triangle', 'rectangle', 'star', 'ellipse' and 'square'.
16258
16259     You can add your custom node types, customizing your visualization to the extreme.
16260
16261     Example:
16262
16263     (start code js)
16264       RGraph.Plot.NodeTypes.implement({
16265         'mySpecialType': {
16266           'render': function(node, canvas) {
16267             //print your custom node to canvas
16268           },
16269           //optional
16270           'contains': function(node, pos) {
16271             //return true if pos is inside the node or false otherwise
16272           }
16273         }
16274       });
16275     (end code)
16276
16277   */
16278   RGraph.Plot.NodeTypes = new Class({
16279     'none': {
16280       'render': $.empty,
16281       'contains': $.lambda(false)
16282     },
16283     'circle': {
16284       'render': function(node, canvas){
16285         var pos = node.pos.getc(true), 
16286             dim = node.getData('dim');
16287         this.nodeHelper.circle.render('fill', pos, dim, canvas);
16288       },
16289       'contains': function(node, pos){
16290         var npos = node.pos.getc(true), 
16291             dim = node.getData('dim');
16292         return this.nodeHelper.circle.contains(npos, pos, dim);
16293       }
16294     },
16295     'ellipse': {
16296       'render': function(node, canvas){
16297         var pos = node.pos.getc(true), 
16298             width = node.getData('width'), 
16299             height = node.getData('height');
16300         this.nodeHelper.ellipse.render('fill', pos, width, height, canvas);
16301         },
16302       'contains': function(node, pos){
16303         var npos = node.pos.getc(true), 
16304             width = node.getData('width'), 
16305             height = node.getData('height');
16306         return this.nodeHelper.ellipse.contains(npos, pos, width, height);
16307       }
16308     },
16309     'square': {
16310       'render': function(node, canvas){
16311         var pos = node.pos.getc(true), 
16312             dim = node.getData('dim');
16313         this.nodeHelper.square.render('fill', pos, dim, canvas);
16314       },
16315       'contains': function(node, pos){
16316         var npos = node.pos.getc(true), 
16317             dim = node.getData('dim');
16318         return this.nodeHelper.square.contains(npos, pos, dim);
16319       }
16320     },
16321     'rectangle': {
16322       'render': function(node, canvas){
16323         var pos = node.pos.getc(true), 
16324             width = node.getData('width'), 
16325             height = node.getData('height');
16326         this.nodeHelper.rectangle.render('fill', pos, width, height, canvas);
16327       },
16328       'contains': function(node, pos){
16329         var npos = node.pos.getc(true), 
16330             width = node.getData('width'), 
16331             height = node.getData('height');
16332         return this.nodeHelper.rectangle.contains(npos, pos, width, height);
16333       }
16334     },
16335     'triangle': {
16336       'render': function(node, canvas){
16337         var pos = node.pos.getc(true), 
16338             dim = node.getData('dim');
16339         this.nodeHelper.triangle.render('fill', pos, dim, canvas);
16340       },
16341       'contains': function(node, pos) {
16342         var npos = node.pos.getc(true), 
16343             dim = node.getData('dim');
16344         return this.nodeHelper.triangle.contains(npos, pos, dim);
16345       }
16346     },
16347     'star': {
16348       'render': function(node, canvas){
16349         var pos = node.pos.getc(true),
16350             dim = node.getData('dim');
16351         this.nodeHelper.star.render('fill', pos, dim, canvas);
16352       },
16353       'contains': function(node, pos) {
16354         var npos = node.pos.getc(true),
16355             dim = node.getData('dim');
16356         return this.nodeHelper.star.contains(npos, pos, dim);
16357       }
16358     }
16359   });
16360
16361   /*
16362     Class: RGraph.Plot.EdgeTypes
16363
16364     This class contains a list of <Graph.Adjacence> built-in types. 
16365     Edge types implemented are 'none', 'line' and 'arrow'.
16366   
16367     You can add your custom edge types, customizing your visualization to the extreme.
16368   
16369     Example:
16370   
16371     (start code js)
16372       RGraph.Plot.EdgeTypes.implement({
16373         'mySpecialType': {
16374           'render': function(adj, canvas) {
16375             //print your custom edge to canvas
16376           },
16377           //optional
16378           'contains': function(adj, pos) {
16379             //return true if pos is inside the arc or false otherwise
16380           }
16381         }
16382       });
16383     (end code)
16384   
16385   */
16386   RGraph.Plot.EdgeTypes = new Class({
16387     'none': $.empty,
16388     'line': {
16389       'render': function(adj, canvas) {
16390         var from = adj.nodeFrom.pos.getc(true),
16391             to = adj.nodeTo.pos.getc(true);
16392         this.edgeHelper.line.render(from, to, canvas);
16393       },
16394       'contains': function(adj, pos) {
16395         var from = adj.nodeFrom.pos.getc(true),
16396             to = adj.nodeTo.pos.getc(true);
16397         return this.edgeHelper.line.contains(from, to, pos, this.edge.epsilon);
16398       }
16399     },
16400     'arrow': {
16401       'render': function(adj, canvas) {
16402         var from = adj.nodeFrom.pos.getc(true),
16403             to = adj.nodeTo.pos.getc(true),
16404             dim = adj.getData('dim'),
16405             direction = adj.data.$direction,
16406             inv = (direction && direction.length>1 && direction[0] != adj.nodeFrom.id);
16407         this.edgeHelper.arrow.render(from, to, dim, inv, canvas);
16408       },
16409       'contains': function(adj, pos) {
16410         var from = adj.nodeFrom.pos.getc(true),
16411             to = adj.nodeTo.pos.getc(true);
16412         return this.edgeHelper.arrow.contains(from, to, pos, this.edge.epsilon);
16413       }
16414     }
16415   });
16416
16417 })($jit.RGraph);
16418
16419
16420 /*
16421  * File: Hypertree.js
16422  * 
16423 */
16424
16425 /* 
16426      Complex 
16427      
16428      A multi-purpose Complex Class with common methods. Extended for the Hypertree. 
16429  
16430 */
16431 /* 
16432    moebiusTransformation 
16433  
16434    Calculates a moebius transformation for this point / complex. 
16435     For more information go to: 
16436         http://en.wikipedia.org/wiki/Moebius_transformation. 
16437  
16438    Parameters: 
16439  
16440       c - An initialized Complex instance representing a translation Vector. 
16441 */
16442
16443 Complex.prototype.moebiusTransformation = function(c) {
16444   var num = this.add(c);
16445   var den = c.$conjugate().$prod(this);
16446   den.x++;
16447   return num.$div(den);
16448 };
16449
16450 /* 
16451     moebiusTransformation 
16452      
16453     Calculates a moebius transformation for the hyperbolic tree. 
16454      
16455     <http://en.wikipedia.org/wiki/Moebius_transformation> 
16456       
16457      Parameters: 
16458      
16459         graph - A <Graph> instance.
16460         pos - A <Complex>.
16461         prop - A property array.
16462         theta - Rotation angle. 
16463         startPos - _optional_ start position. 
16464 */
16465 Graph.Util.moebiusTransformation = function(graph, pos, prop, startPos, flags) {
16466   this.eachNode(graph, function(elem) {
16467     for ( var i = 0; i < prop.length; i++) {
16468       var p = pos[i].scale(-1), property = startPos ? startPos : prop[i];
16469       elem.getPos(prop[i]).set(elem.getPos(property).getc().moebiusTransformation(p));
16470     }
16471   }, flags);
16472 };
16473
16474 /* 
16475    Class: Hypertree 
16476    
16477    A Hyperbolic Tree/Graph visualization.
16478    
16479    Inspired by:
16480  
16481    A Focus+Context Technique Based on Hyperbolic Geometry for Visualizing Large Hierarchies (John Lamping, Ramana Rao, and Peter Pirolli). 
16482    <http://www.cs.tau.ac.il/~asharf/shrek/Projects/HypBrowser/startree-chi95.pdf>
16483  
16484   Note:
16485  
16486   This visualization was built and engineered from scratch, taking only the paper as inspiration, and only shares some features with the Hypertree described in the paper.
16487
16488   Implements:
16489   
16490   All <Loader> methods
16491   
16492   Constructor Options:
16493   
16494   Inherits options from
16495   
16496   - <Options.Canvas>
16497   - <Options.Controller>
16498   - <Options.Node>
16499   - <Options.Edge>
16500   - <Options.Label>
16501   - <Options.Events>
16502   - <Options.Tips>
16503   - <Options.NodeStyles>
16504   - <Options.Navigation>
16505   
16506   Additionally, there are other parameters and some default values changed
16507   
16508   radius - (string|number) Default's *auto*. The radius of the disc to plot the <Hypertree> in. 'auto' will take the smaller value from the width and height canvas dimensions. You can also set this to a custom value, for example *250*.
16509   offset - (number) Default's *0*. A number in the range [0, 1) that will be substracted to each node position to make a more compact <Hypertree>. This will avoid placing nodes too far from each other when a there's a selected node.
16510   fps - Described in <Options.Fx>. It's default value has been changed to *35*.
16511   duration - Described in <Options.Fx>. It's default value has been changed to *1500*.
16512   Edge.type - Described in <Options.Edge>. It's default value has been changed to *hyperline*. 
16513   
16514   Instance Properties:
16515   
16516   canvas - Access a <Canvas> instance.
16517   graph - Access a <Graph> instance.
16518   op - Access a <Hypertree.Op> instance.
16519   fx - Access a <Hypertree.Plot> instance.
16520   labels - Access a <Hypertree.Label> interface implementation.
16521
16522 */
16523
16524 $jit.Hypertree = new Class( {
16525
16526   Implements: [ Loader, Extras, Layouts.Radial ],
16527
16528   initialize: function(controller) {
16529     var $Hypertree = $jit.Hypertree;
16530
16531     var config = {
16532       radius: "auto",
16533       offset: 0,
16534       Edge: {
16535         type: 'hyperline'
16536       },
16537       duration: 1500,
16538       fps: 35
16539     };
16540     this.controller = this.config = $.merge(Options("Canvas", "Node", "Edge",
16541         "Fx", "Tips", "NodeStyles", "Events", "Navigation", "Controller", "Label"), config, controller);
16542
16543     var canvasConfig = this.config;
16544     if(canvasConfig.useCanvas) {
16545       this.canvas = canvasConfig.useCanvas;
16546       this.config.labelContainer = this.canvas.id + '-label';
16547     } else {
16548       if(canvasConfig.background) {
16549         canvasConfig.background = $.merge({
16550           type: 'Circles'
16551         }, canvasConfig.background);
16552       }
16553       this.canvas = new Canvas(this, canvasConfig);
16554       this.config.labelContainer = (typeof canvasConfig.injectInto == 'string'? canvasConfig.injectInto : canvasConfig.injectInto.id) + '-label';
16555     }
16556
16557     this.graphOptions = {
16558       'klass': Polar,
16559       'Node': {
16560         'selected': false,
16561         'exist': true,
16562         'drawn': true
16563       }
16564     };
16565     this.graph = new Graph(this.graphOptions, this.config.Node,
16566         this.config.Edge);
16567     this.labels = new $Hypertree.Label[canvasConfig.Label.type](this);
16568     this.fx = new $Hypertree.Plot(this, $Hypertree);
16569     this.op = new $Hypertree.Op(this);
16570     this.json = null;
16571     this.root = null;
16572     this.busy = false;
16573     // initialize extras
16574     this.initializeExtras();
16575   },
16576
16577   /* 
16578   
16579   createLevelDistanceFunc 
16580
16581   Returns the levelDistance function used for calculating a node distance 
16582   to its origin. This function returns a function that is computed 
16583   per level and not per node, such that all nodes with the same depth will have the 
16584   same distance to the origin. The resulting function gets the 
16585   parent node as parameter and returns a float.
16586
16587   */
16588   createLevelDistanceFunc: function() {
16589     // get max viz. length.
16590     var r = this.getRadius();
16591     // get max depth.
16592     var depth = 0, max = Math.max, config = this.config;
16593     this.graph.eachNode(function(node) {
16594       depth = max(node._depth, depth);
16595     }, "ignore");
16596     depth++;
16597     // node distance generator
16598     var genDistFunc = function(a) {
16599       return function(node) {
16600         node.scale = r;
16601         var d = node._depth + 1;
16602         var acum = 0, pow = Math.pow;
16603         while (d) {
16604           acum += pow(a, d--);
16605         }
16606         return acum - config.offset;
16607       };
16608     };
16609     // estimate better edge length.
16610     for ( var i = 0.51; i <= 1; i += 0.01) {
16611       var valSeries = (1 - Math.pow(i, depth)) / (1 - i);
16612       if (valSeries >= 2) { return genDistFunc(i - 0.01); }
16613     }
16614     return genDistFunc(0.75);
16615   },
16616
16617   /* 
16618     Method: getRadius 
16619     
16620     Returns the current radius of the visualization. If *config.radius* is *auto* then it 
16621     calculates the radius by taking the smaller size of the <Canvas> widget.
16622     
16623     See also:
16624     
16625     <Canvas.getSize>
16626    
16627   */
16628   getRadius: function() {
16629     var rad = this.config.radius;
16630     if (rad !== "auto") { return rad; }
16631     var s = this.canvas.getSize();
16632     return Math.min(s.width, s.height) / 2;
16633   },
16634
16635   /* 
16636     Method: refresh 
16637     
16638     Computes positions and plots the tree.
16639
16640     Parameters:
16641
16642     reposition - (optional|boolean) Set this to *true* to force all positions (current, start, end) to match.
16643
16644    */
16645   refresh: function(reposition) {
16646     if (reposition) {
16647       this.reposition();
16648       this.graph.eachNode(function(node) {
16649         node.startPos.rho = node.pos.rho = node.endPos.rho;
16650         node.startPos.theta = node.pos.theta = node.endPos.theta;
16651       });
16652     } else {
16653       this.compute();
16654     }
16655     this.plot();
16656   },
16657
16658   /* 
16659    reposition 
16660    
16661    Computes nodes' positions and restores the tree to its previous position.
16662
16663    For calculating nodes' positions the root must be placed on its origin. This method does this 
16664      and then attemps to restore the hypertree to its previous position.
16665     
16666   */
16667   reposition: function() {
16668     this.compute('end');
16669     var vector = this.graph.getNode(this.root).pos.getc().scale(-1);
16670     Graph.Util.moebiusTransformation(this.graph, [ vector ], [ 'end' ],
16671         'end', "ignore");
16672     this.graph.eachNode(function(node) {
16673       if (node.ignore) {
16674         node.endPos.rho = node.pos.rho;
16675         node.endPos.theta = node.pos.theta;
16676       }
16677     });
16678   },
16679
16680   /* 
16681    Method: plot 
16682    
16683    Plots the <Hypertree>. This is a shortcut to *fx.plot*. 
16684
16685   */
16686   plot: function() {
16687     this.fx.plot();
16688   },
16689
16690   /* 
16691    Method: onClick 
16692    
16693    Animates the <Hypertree> to center the node specified by *id*.
16694
16695    Parameters:
16696
16697    id - A <Graph.Node> id.
16698    opt - (optional|object) An object containing some extra properties described below
16699    hideLabels - (boolean) Default's *true*. Hide labels when performing the animation.
16700
16701    Example:
16702
16703    (start code js)
16704      ht.onClick('someid');
16705      //or also...
16706      ht.onClick('someid', {
16707       hideLabels: false
16708      });
16709     (end code)
16710     
16711   */
16712   onClick: function(id, opt) {
16713     var pos = this.graph.getNode(id).pos.getc(true);
16714     this.move(pos, opt);
16715   },
16716
16717   /* 
16718    Method: move 
16719
16720    Translates the tree to the given position. 
16721
16722    Parameters:
16723
16724    pos - (object) A *x, y* coordinate object where x, y in [0, 1), to move the tree to.
16725    opt - This object has been defined in <Hypertree.onClick>
16726    
16727    Example:
16728    
16729    (start code js)
16730      ht.move({ x: 0, y: 0.7 }, {
16731        hideLabels: false
16732      });
16733    (end code)
16734
16735   */
16736   move: function(pos, opt) {
16737     var versor = $C(pos.x, pos.y);
16738     if (this.busy === false && versor.norm() < 1) {
16739       this.busy = true;
16740       var root = this.graph.getClosestNodeToPos(versor), that = this;
16741       this.graph.computeLevels(root.id, 0);
16742       this.controller.onBeforeCompute(root);
16743       opt = $.merge( {
16744         onComplete: $.empty
16745       }, opt || {});
16746       this.fx.animate($.merge( {
16747         modes: [ 'moebius' ],
16748         hideLabels: true
16749       }, opt, {
16750         onComplete: function() {
16751           that.busy = false;
16752           opt.onComplete();
16753         }
16754       }), versor);
16755     }
16756   }
16757 });
16758
16759 $jit.Hypertree.$extend = true;
16760
16761 (function(Hypertree) {
16762
16763   /* 
16764      Class: Hypertree.Op 
16765    
16766      Custom extension of <Graph.Op>.
16767
16768      Extends:
16769
16770      All <Graph.Op> methods
16771      
16772      See also:
16773      
16774      <Graph.Op>
16775
16776   */
16777   Hypertree.Op = new Class( {
16778
16779     Implements: Graph.Op
16780
16781   });
16782
16783   /* 
16784      Class: Hypertree.Plot 
16785    
16786     Custom extension of <Graph.Plot>.
16787   
16788     Extends:
16789   
16790     All <Graph.Plot> methods
16791     
16792     See also:
16793     
16794     <Graph.Plot>
16795   
16796   */
16797   Hypertree.Plot = new Class( {
16798
16799     Implements: Graph.Plot
16800
16801   });
16802
16803   /*
16804     Object: Hypertree.Label
16805
16806     Custom extension of <Graph.Label>. 
16807     Contains custom <Graph.Label.SVG>, <Graph.Label.HTML> and <Graph.Label.Native> extensions.
16808   
16809     Extends:
16810   
16811     All <Graph.Label> methods and subclasses.
16812   
16813     See also:
16814   
16815     <Graph.Label>, <Graph.Label.Native>, <Graph.Label.HTML>, <Graph.Label.SVG>.
16816
16817    */
16818   Hypertree.Label = {};
16819
16820   /*
16821      Hypertree.Label.Native
16822
16823      Custom extension of <Graph.Label.Native>.
16824
16825      Extends:
16826
16827      All <Graph.Label.Native> methods
16828
16829      See also:
16830
16831      <Graph.Label.Native>
16832
16833   */
16834   Hypertree.Label.Native = new Class( {
16835     Implements: Graph.Label.Native,
16836
16837     initialize: function(viz) {
16838       this.viz = viz;
16839     },
16840
16841     renderLabel: function(canvas, node, controller) {
16842       var ctx = canvas.getCtx();
16843       var coord = node.pos.getc(true);
16844       var s = this.viz.getRadius();
16845       ctx.fillText(node.name, coord.x * s, coord.y * s);
16846     }
16847   });
16848
16849   /*
16850      Hypertree.Label.SVG
16851
16852     Custom extension of <Graph.Label.SVG>.
16853   
16854     Extends:
16855   
16856     All <Graph.Label.SVG> methods
16857   
16858     See also:
16859   
16860     <Graph.Label.SVG>
16861   
16862   */
16863   Hypertree.Label.SVG = new Class( {
16864     Implements: Graph.Label.SVG,
16865
16866     initialize: function(viz) {
16867       this.viz = viz;
16868     },
16869
16870     /* 
16871        placeLabel
16872
16873        Overrides abstract method placeLabel in <Graph.Plot>.
16874
16875        Parameters:
16876
16877        tag - A DOM label element.
16878        node - A <Graph.Node>.
16879        controller - A configuration/controller object passed to the visualization.
16880       
16881      */
16882     placeLabel: function(tag, node, controller) {
16883       var pos = node.pos.getc(true), 
16884           canvas = this.viz.canvas,
16885           ox = canvas.translateOffsetX,
16886           oy = canvas.translateOffsetY,
16887           sx = canvas.scaleOffsetX,
16888           sy = canvas.scaleOffsetY,
16889           radius = canvas.getSize(),
16890           r = this.viz.getRadius();
16891       var labelPos = {
16892         x: Math.round((pos.x * sx) * r + ox + radius.width / 2),
16893         y: Math.round((pos.y * sy) * r + oy + radius.height / 2)
16894       };
16895       tag.setAttribute('x', labelPos.x);
16896       tag.setAttribute('y', labelPos.y);
16897       controller.onPlaceLabel(tag, node);
16898     }
16899   });
16900
16901   /*
16902      Hypertree.Label.HTML
16903
16904      Custom extension of <Graph.Label.HTML>.
16905
16906      Extends:
16907
16908      All <Graph.Label.HTML> methods.
16909
16910      See also:
16911
16912      <Graph.Label.HTML>
16913
16914   */
16915   Hypertree.Label.HTML = new Class( {
16916     Implements: Graph.Label.HTML,
16917
16918     initialize: function(viz) {
16919       this.viz = viz;
16920     },
16921     /* 
16922        placeLabel
16923
16924        Overrides abstract method placeLabel in <Graph.Plot>.
16925
16926        Parameters:
16927
16928        tag - A DOM label element.
16929        node - A <Graph.Node>.
16930        controller - A configuration/controller object passed to the visualization.
16931       
16932      */
16933     placeLabel: function(tag, node, controller) {
16934       var pos = node.pos.getc(true), 
16935           canvas = this.viz.canvas,
16936           ox = canvas.translateOffsetX,
16937           oy = canvas.translateOffsetY,
16938           sx = canvas.scaleOffsetX,
16939           sy = canvas.scaleOffsetY,
16940           radius = canvas.getSize(),
16941           r = this.viz.getRadius();
16942       var labelPos = {
16943         x: Math.round((pos.x * sx) * r + ox + radius.width / 2),
16944         y: Math.round((pos.y * sy) * r + oy + radius.height / 2)
16945       };
16946       var style = tag.style;
16947       style.left = labelPos.x + 'px';
16948       style.top = labelPos.y + 'px';
16949       style.display = this.fitsInCanvas(labelPos, canvas) ? '' : 'none';
16950
16951       controller.onPlaceLabel(tag, node);
16952     }
16953   });
16954
16955   /*
16956     Class: Hypertree.Plot.NodeTypes
16957
16958     This class contains a list of <Graph.Node> built-in types. 
16959     Node types implemented are 'none', 'circle', 'triangle', 'rectangle', 'star', 'ellipse' and 'square'.
16960
16961     You can add your custom node types, customizing your visualization to the extreme.
16962
16963     Example:
16964
16965     (start code js)
16966       Hypertree.Plot.NodeTypes.implement({
16967         'mySpecialType': {
16968           'render': function(node, canvas) {
16969             //print your custom node to canvas
16970           },
16971           //optional
16972           'contains': function(node, pos) {
16973             //return true if pos is inside the node or false otherwise
16974           }
16975         }
16976       });
16977     (end code)
16978
16979   */
16980   Hypertree.Plot.NodeTypes = new Class({
16981     'none': {
16982       'render': $.empty,
16983       'contains': $.lambda(false)
16984     },
16985     'circle': {
16986       'render': function(node, canvas) {
16987         var nconfig = this.node,
16988             dim = node.getData('dim'),
16989             p = node.pos.getc();
16990         dim = nconfig.transform? dim * (1 - p.squaredNorm()) : dim;
16991         p.$scale(node.scale);
16992         if (dim > 0.2) {
16993           this.nodeHelper.circle.render('fill', p, dim, canvas);
16994         }
16995       },
16996       'contains': function(node, pos) {
16997         var dim = node.getData('dim'),
16998             npos = node.pos.getc().$scale(node.scale);
16999         return this.nodeHelper.circle.contains(npos, pos, dim);
17000       }
17001     },
17002     'ellipse': {
17003       'render': function(node, canvas) {
17004         var pos = node.pos.getc().$scale(node.scale),
17005             width = node.getData('width'),
17006             height = node.getData('height');
17007         this.nodeHelper.ellipse.render('fill', pos, width, height, canvas);
17008       },
17009       'contains': function(node, pos) {
17010         var width = node.getData('width'),
17011             height = node.getData('height'),
17012             npos = node.pos.getc().$scale(node.scale);
17013         return this.nodeHelper.circle.contains(npos, pos, width, height);
17014       }
17015     },
17016     'square': {
17017       'render': function(node, canvas) {
17018         var nconfig = this.node,
17019             dim = node.getData('dim'),
17020             p = node.pos.getc();
17021         dim = nconfig.transform? dim * (1 - p.squaredNorm()) : dim;
17022         p.$scale(node.scale);
17023         if (dim > 0.2) {
17024           this.nodeHelper.square.render('fill', p, dim, canvas);
17025         }
17026       },
17027       'contains': function(node, pos) {
17028         var dim = node.getData('dim'),
17029             npos = node.pos.getc().$scale(node.scale);
17030         return this.nodeHelper.square.contains(npos, pos, dim);
17031       }
17032     },
17033     'rectangle': {
17034       'render': function(node, canvas) {
17035         var nconfig = this.node,
17036             width = node.getData('width'),
17037             height = node.getData('height'),
17038             pos = node.pos.getc();
17039         width = nconfig.transform? width * (1 - pos.squaredNorm()) : width;
17040         height = nconfig.transform? height * (1 - pos.squaredNorm()) : height;
17041         pos.$scale(node.scale);
17042         if (width > 0.2 && height > 0.2) {
17043           this.nodeHelper.rectangle.render('fill', pos, width, height, canvas);
17044         }
17045       },
17046       'contains': function(node, pos) {
17047         var width = node.getData('width'),
17048             height = node.getData('height'),
17049             npos = node.pos.getc().$scale(node.scale);
17050         return this.nodeHelper.rectangle.contains(npos, pos, width, height);
17051       }
17052     },
17053     'triangle': {
17054       'render': function(node, canvas) {
17055         var nconfig = this.node,
17056             dim = node.getData('dim'),
17057             p = node.pos.getc();
17058         dim = nconfig.transform? dim * (1 - p.squaredNorm()) : dim;
17059         p.$scale(node.scale);
17060         if (dim > 0.2) {
17061           this.nodeHelper.triangle.render('fill', p, dim, canvas);
17062         }
17063       },
17064       'contains': function(node, pos) {
17065         var dim = node.getData('dim'),
17066             npos = node.pos.getc().$scale(node.scale);
17067         return this.nodeHelper.triangle.contains(npos, pos, dim);
17068       }
17069     },
17070     'star': {
17071       'render': function(node, canvas) {
17072         var nconfig = this.node,
17073             dim = node.getData('dim'),
17074             p = node.pos.getc();
17075         dim = nconfig.transform? dim * (1 - p.squaredNorm()) : dim;
17076         p.$scale(node.scale);
17077         if (dim > 0.2) {
17078           this.nodeHelper.star.render('fill', p, dim, canvas);
17079         }
17080       },
17081       'contains': function(node, pos) {
17082         var dim = node.getData('dim'),
17083             npos = node.pos.getc().$scale(node.scale);
17084         return this.nodeHelper.star.contains(npos, pos, dim);
17085       }
17086     }
17087   });
17088
17089   /*
17090    Class: Hypertree.Plot.EdgeTypes
17091
17092     This class contains a list of <Graph.Adjacence> built-in types. 
17093     Edge types implemented are 'none', 'line', 'arrow' and 'hyperline'.
17094   
17095     You can add your custom edge types, customizing your visualization to the extreme.
17096   
17097     Example:
17098   
17099     (start code js)
17100       Hypertree.Plot.EdgeTypes.implement({
17101         'mySpecialType': {
17102           'render': function(adj, canvas) {
17103             //print your custom edge to canvas
17104           },
17105           //optional
17106           'contains': function(adj, pos) {
17107             //return true if pos is inside the arc or false otherwise
17108           }
17109         }
17110       });
17111     (end code)
17112   
17113   */
17114   Hypertree.Plot.EdgeTypes = new Class({
17115     'none': $.empty,
17116     'line': {
17117       'render': function(adj, canvas) {
17118         var from = adj.nodeFrom.pos.getc(true),
17119           to = adj.nodeTo.pos.getc(true),
17120           r = adj.nodeFrom.scale;
17121           this.edgeHelper.line.render({x:from.x*r, y:from.y*r}, {x:to.x*r, y:to.y*r}, canvas);
17122       },
17123       'contains': function(adj, pos) {
17124         var from = adj.nodeFrom.pos.getc(true),
17125             to = adj.nodeTo.pos.getc(true),
17126             r = adj.nodeFrom.scale;
17127             this.edgeHelper.line.contains({x:from.x*r, y:from.y*r}, {x:to.x*r, y:to.y*r}, pos, this.edge.epsilon);
17128       }
17129     },
17130     'arrow': {
17131       'render': function(adj, canvas) {
17132         var from = adj.nodeFrom.pos.getc(true),
17133             to = adj.nodeTo.pos.getc(true),
17134             r = adj.nodeFrom.scale,
17135             dim = adj.getData('dim'),
17136             direction = adj.data.$direction,
17137             inv = (direction && direction.length>1 && direction[0] != adj.nodeFrom.id);
17138         this.edgeHelper.arrow.render({x:from.x*r, y:from.y*r}, {x:to.x*r, y:to.y*r}, dim, inv, canvas);
17139       },
17140       'contains': function(adj, pos) {
17141         var from = adj.nodeFrom.pos.getc(true),
17142             to = adj.nodeTo.pos.getc(true),
17143             r = adj.nodeFrom.scale;
17144         this.edgeHelper.arrow.contains({x:from.x*r, y:from.y*r}, {x:to.x*r, y:to.y*r}, pos, this.edge.epsilon);
17145       }
17146     },
17147     'hyperline': {
17148       'render': function(adj, canvas) {
17149         var from = adj.nodeFrom.pos.getc(),
17150             to = adj.nodeTo.pos.getc(),
17151             dim = this.viz.getRadius();
17152         this.edgeHelper.hyperline.render(from, to, dim, canvas);
17153       },
17154       'contains': $.lambda(false)
17155     }
17156   });
17157
17158 })($jit.Hypertree);
17159
17160
17161
17162
17163  })();