Polish sizeme_graph.pl
[p5sagit/Devel-Size.git] / static / public / 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     var val;
4193
4194     if(force) {
4195       val = data[dollar];
4196     }
4197     else if(!this.Config.overridable) {
4198       val = prefixConfig[prop] || 0;
4199     }
4200     else {
4201       val = (dollar in data) ?
4202         data[dollar] : ((dollar in this.data) ? this.data[dollar] : (prefixConfig[prop] || 0));
4203     }
4204     if (dollar == 'XXX$area') {
4205         val = Math.log(val);
4206     }
4207     return val;
4208   }
4209
4210   var setDataInternal = function(prefix, prop, value, type) {
4211     type = type || 'current';
4212     prefix = '$' + (prefix ? prefix + '-' : '');
4213
4214     var data;
4215
4216     if(type == 'current') {
4217       data = this.data;
4218     } else if(type == 'start') {
4219       data = this.startData;
4220     } else if(type == 'end') {
4221       data = this.endData;
4222     }
4223
4224     data[prefix + prop] = value;
4225   }
4226
4227   var removeDataInternal = function(prefix, properties) {
4228     prefix = '$' + (prefix ? prefix + '-' : '');
4229     var that = this;
4230     $.each(properties, function(prop) {
4231       var pref = prefix + prop;
4232       delete that.data[pref];
4233       delete that.endData[pref];
4234       delete that.startData[pref];
4235     });
4236   }
4237
4238   Accessors = {
4239     /*
4240     Method: getData
4241
4242     Returns the specified data value property.
4243     This is useful for querying special/reserved <Graph.Node> data properties
4244     (i.e dollar prefixed properties).
4245
4246     Parameters:
4247
4248       prop  - (string) The name of the property. The dollar sign is not needed. For
4249               example *getData(width)* will return *data.$width*.
4250       type  - (string) The type of the data property queried. Default's "current". You can access *start* and *end* 
4251               data properties also. These properties are used when making animations.
4252       force - (boolean) Whether to obtain the true value of the property (equivalent to
4253               *data.$prop*) or to check for *node.overridable = true* first.
4254
4255     Returns:
4256
4257       The value of the dollar prefixed property or the global Node/Edge property
4258       value if *overridable=false*
4259
4260     Example:
4261     (start code js)
4262      node.getData('width'); //will return node.data.$width if Node.overridable=true;
4263     (end code)
4264     */
4265     getData: function(prop, type, force) {
4266       return getDataInternal.call(this, "", prop, type, force, this.Config);
4267     },
4268
4269
4270     /*
4271     Method: setData
4272
4273     Sets the current data property with some specific value.
4274     This method is only useful for reserved (dollar prefixed) properties.
4275
4276     Parameters:
4277
4278       prop  - (string) The name of the property. The dollar sign is not necessary. For
4279               example *setData(width)* will set *data.$width*.
4280       value - (mixed) The value to store.
4281       type  - (string) The type of the data property to store. Default's "current" but
4282               can also be "start" or "end".
4283
4284     Example:
4285     
4286     (start code js)
4287      node.setData('width', 30);
4288     (end code)
4289     
4290     If we were to make an animation of a node/edge width then we could do
4291     
4292     (start code js)
4293       var node = viz.getNode('nodeId');
4294       //set start and end values
4295       node.setData('width', 10, 'start');
4296       node.setData('width', 30, 'end');
4297       //will animate nodes width property
4298       viz.fx.animate({
4299         modes: ['node-property:width'],
4300         duration: 1000
4301       });
4302     (end code)
4303     */
4304     setData: function(prop, value, type) {
4305       setDataInternal.call(this, "", prop, value, type);
4306     },
4307
4308     /*
4309     Method: setDataset
4310
4311     Convenience method to set multiple data values at once.
4312     
4313     Parameters:
4314     
4315     types - (array|string) A set of 'current', 'end' or 'start' values.
4316     obj - (object) A hash containing the names and values of the properties to be altered.
4317
4318     Example:
4319     (start code js)
4320       node.setDataset(['current', 'end'], {
4321         'width': [100, 5],
4322         'color': ['#fff', '#ccc']
4323       });
4324       //...or also
4325       node.setDataset('end', {
4326         'width': 5,
4327         'color': '#ccc'
4328       });
4329     (end code)
4330     
4331     See also: 
4332     
4333     <Accessors.setData>
4334     
4335     */
4336     setDataset: function(types, obj) {
4337       types = $.splat(types);
4338       for(var attr in obj) {
4339         for(var i=0, val = $.splat(obj[attr]), l=types.length; i<l; i++) {
4340           this.setData(attr, val[i], types[i]);
4341         }
4342       }
4343     },
4344     
4345     /*
4346     Method: removeData
4347
4348     Remove data properties.
4349
4350     Parameters:
4351
4352     One or more property names as arguments. The dollar sign is not needed.
4353
4354     Example:
4355     (start code js)
4356     node.removeData('width'); //now the default width value is returned
4357     (end code)
4358     */
4359     removeData: function() {
4360       removeDataInternal.call(this, "", Array.prototype.slice.call(arguments));
4361     },
4362
4363     /*
4364     Method: getCanvasStyle
4365
4366     Returns the specified canvas style data value property. This is useful for
4367     querying special/reserved <Graph.Node> canvas style data properties (i.e.
4368     dollar prefixed properties that match with $canvas-<name of canvas style>).
4369
4370     Parameters:
4371
4372       prop  - (string) The name of the property. The dollar sign is not needed. For
4373               example *getCanvasStyle(shadowBlur)* will return *data[$canvas-shadowBlur]*.
4374       type  - (string) The type of the data property queried. Default's *current*. You can access *start* and *end* 
4375               data properties also.
4376               
4377     Example:
4378     (start code js)
4379       node.getCanvasStyle('shadowBlur');
4380     (end code)
4381     
4382     See also:
4383     
4384     <Accessors.getData>
4385     */
4386     getCanvasStyle: function(prop, type, force) {
4387       return getDataInternal.call(
4388           this, 'canvas', prop, type, force, this.Config.CanvasStyles);
4389     },
4390
4391     /*
4392     Method: setCanvasStyle
4393
4394     Sets the canvas style data property with some specific value.
4395     This method is only useful for reserved (dollar prefixed) properties.
4396     
4397     Parameters:
4398     
4399     prop - (string) Name of the property. Can be any canvas property like 'shadowBlur', 'shadowColor', 'strokeStyle', etc.
4400     value - (mixed) The value to set to the property.
4401     type - (string) Default's *current*. Whether to set *start*, *current* or *end* type properties.
4402     
4403     Example:
4404     
4405     (start code js)
4406      node.setCanvasStyle('shadowBlur', 30);
4407     (end code)
4408     
4409     If we were to make an animation of a node/edge shadowBlur canvas style then we could do
4410     
4411     (start code js)
4412       var node = viz.getNode('nodeId');
4413       //set start and end values
4414       node.setCanvasStyle('shadowBlur', 10, 'start');
4415       node.setCanvasStyle('shadowBlur', 30, 'end');
4416       //will animate nodes canvas style property for nodes
4417       viz.fx.animate({
4418         modes: ['node-style:shadowBlur'],
4419         duration: 1000
4420       });
4421     (end code)
4422     
4423     See also:
4424     
4425     <Accessors.setData>.
4426     */
4427     setCanvasStyle: function(prop, value, type) {
4428       setDataInternal.call(this, 'canvas', prop, value, type);
4429     },
4430
4431     /*
4432     Method: setCanvasStyles
4433
4434     Convenience method to set multiple styles at once.
4435
4436     Parameters:
4437     
4438     types - (array|string) A set of 'current', 'end' or 'start' values.
4439     obj - (object) A hash containing the names and values of the properties to be altered.
4440
4441     See also:
4442     
4443     <Accessors.setDataset>.
4444     */
4445     setCanvasStyles: function(types, obj) {
4446       types = $.splat(types);
4447       for(var attr in obj) {
4448         for(var i=0, val = $.splat(obj[attr]), l=types.length; i<l; i++) {
4449           this.setCanvasStyle(attr, val[i], types[i]);
4450         }
4451       }
4452     },
4453
4454     /*
4455     Method: removeCanvasStyle
4456
4457     Remove canvas style properties from data.
4458
4459     Parameters:
4460     
4461     A variable number of canvas style strings.
4462
4463     See also:
4464     
4465     <Accessors.removeData>.
4466     */
4467     removeCanvasStyle: function() {
4468       removeDataInternal.call(this, 'canvas', Array.prototype.slice.call(arguments));
4469     },
4470
4471     /*
4472     Method: getLabelData
4473
4474     Returns the specified label data value property. This is useful for
4475     querying special/reserved <Graph.Node> label options (i.e.
4476     dollar prefixed properties that match with $label-<name of label style>).
4477
4478     Parameters:
4479
4480       prop  - (string) The name of the property. The dollar sign prefix is not needed. For
4481               example *getLabelData(size)* will return *data[$label-size]*.
4482       type  - (string) The type of the data property queried. Default's *current*. You can access *start* and *end* 
4483               data properties also.
4484               
4485     See also:
4486     
4487     <Accessors.getData>.
4488     */
4489     getLabelData: function(prop, type, force) {
4490       return getDataInternal.call(
4491           this, 'label', prop, type, force, this.Label);
4492     },
4493
4494     /*
4495     Method: setLabelData
4496
4497     Sets the current label data with some specific value.
4498     This method is only useful for reserved (dollar prefixed) properties.
4499
4500     Parameters:
4501     
4502     prop - (string) Name of the property. Can be any canvas property like 'shadowBlur', 'shadowColor', 'strokeStyle', etc.
4503     value - (mixed) The value to set to the property.
4504     type - (string) Default's *current*. Whether to set *start*, *current* or *end* type properties.
4505     
4506     Example:
4507     
4508     (start code js)
4509      node.setLabelData('size', 30);
4510     (end code)
4511     
4512     If we were to make an animation of a node label size then we could do
4513     
4514     (start code js)
4515       var node = viz.getNode('nodeId');
4516       //set start and end values
4517       node.setLabelData('size', 10, 'start');
4518       node.setLabelData('size', 30, 'end');
4519       //will animate nodes label size
4520       viz.fx.animate({
4521         modes: ['label-property:size'],
4522         duration: 1000
4523       });
4524     (end code)
4525     
4526     See also:
4527     
4528     <Accessors.setData>.
4529     */
4530     setLabelData: function(prop, value, type) {
4531       setDataInternal.call(this, 'label', prop, value, type);
4532     },
4533
4534     /*
4535     Method: setLabelDataset
4536
4537     Convenience function to set multiple label data at once.
4538
4539     Parameters:
4540     
4541     types - (array|string) A set of 'current', 'end' or 'start' values.
4542     obj - (object) A hash containing the names and values of the properties to be altered.
4543
4544     See also:
4545     
4546     <Accessors.setDataset>.
4547     */
4548     setLabelDataset: function(types, obj) {
4549       types = $.splat(types);
4550       for(var attr in obj) {
4551         for(var i=0, val = $.splat(obj[attr]), l=types.length; i<l; i++) {
4552           this.setLabelData(attr, val[i], types[i]);
4553         }
4554       }
4555     },
4556
4557     /*
4558     Method: removeLabelData
4559
4560     Remove label properties from data.
4561     
4562     Parameters:
4563     
4564     A variable number of label property strings.
4565
4566     See also:
4567     
4568     <Accessors.removeData>.
4569     */
4570     removeLabelData: function() {
4571       removeDataInternal.call(this, 'label', Array.prototype.slice.call(arguments));
4572     }
4573   };
4574 })();
4575
4576 /*
4577      Class: Graph.Node
4578
4579      A <Graph> node.
4580      
4581      Implements:
4582      
4583      <Accessors> methods.
4584      
4585      The following <Graph.Util> methods are implemented by <Graph.Node>
4586      
4587     - <Graph.Util.eachAdjacency>
4588     - <Graph.Util.eachLevel>
4589     - <Graph.Util.eachSubgraph>
4590     - <Graph.Util.eachSubnode>
4591     - <Graph.Util.anySubnode>
4592     - <Graph.Util.getSubnodes>
4593     - <Graph.Util.getParents>
4594     - <Graph.Util.isDescendantOf>     
4595 */
4596 Graph.Node = new Class({
4597     
4598   initialize: function(opt, klass, Node, Edge, Label) {
4599     var innerOptions = {
4600       'id': '',
4601       'name': '',
4602       'data': {},
4603       'startData': {},
4604       'endData': {},
4605       'adjacencies': {},
4606
4607       'selected': false,
4608       'drawn': false,
4609       'exist': false,
4610
4611       'angleSpan': {
4612         'begin': 0,
4613         'end' : 0
4614       },
4615
4616       'pos': new klass,
4617       'startPos': new klass,
4618       'endPos': new klass
4619     };
4620     
4621     $.extend(this, $.extend(innerOptions, opt));
4622     this.Config = this.Node = Node;
4623     this.Edge = Edge;
4624     this.Label = Label;
4625   },
4626
4627     /*
4628        Method: adjacentTo
4629     
4630        Indicates if the node is adjacent to the node specified by id
4631
4632        Parameters:
4633     
4634           id - (string) A node id.
4635     
4636        Example:
4637        (start code js)
4638         node.adjacentTo('nodeId') == true;
4639        (end code)
4640     */
4641     adjacentTo: function(node) {
4642         return node.id in this.adjacencies;
4643     },
4644
4645     /*
4646        Method: getAdjacency
4647     
4648        Returns a <Graph.Adjacence> object connecting the current <Graph.Node> and the node having *id* as id.
4649
4650        Parameters:
4651     
4652           id - (string) A node id.
4653     */  
4654     getAdjacency: function(id) {
4655         return this.adjacencies[id];
4656     },
4657
4658     /*
4659       Method: getPos
4660    
4661       Returns the position of the node.
4662   
4663       Parameters:
4664    
4665          type - (string) Default's *current*. Possible values are "start", "end" or "current".
4666    
4667       Returns:
4668    
4669         A <Complex> or <Polar> instance.
4670   
4671       Example:
4672       (start code js)
4673        var pos = node.getPos('end');
4674       (end code)
4675    */
4676    getPos: function(type) {
4677        type = type || "current";
4678        if(type == "current") {
4679          return this.pos;
4680        } else if(type == "end") {
4681          return this.endPos;
4682        } else if(type == "start") {
4683          return this.startPos;
4684        }
4685    },
4686    /*
4687      Method: setPos
4688   
4689      Sets the node's position.
4690   
4691      Parameters:
4692   
4693         value - (object) A <Complex> or <Polar> instance.
4694         type - (string) Default's *current*. Possible values are "start", "end" or "current".
4695   
4696      Example:
4697      (start code js)
4698       node.setPos(new $jit.Complex(0, 0), 'end');
4699      (end code)
4700   */
4701   setPos: function(value, type) {
4702       type = type || "current";
4703       var pos;
4704       if(type == "current") {
4705         pos = this.pos;
4706       } else if(type == "end") {
4707         pos = this.endPos;
4708       } else if(type == "start") {
4709         pos = this.startPos;
4710       }
4711       pos.set(value);
4712   }
4713 });
4714
4715 Graph.Node.implement(Accessors);
4716
4717 /*
4718      Class: Graph.Adjacence
4719
4720      A <Graph> adjacence (or edge) connecting two <Graph.Nodes>.
4721      
4722      Implements:
4723      
4724      <Accessors> methods.
4725
4726      See also:
4727
4728      <Graph>, <Graph.Node>
4729
4730      Properties:
4731      
4732       nodeFrom - A <Graph.Node> connected by this edge.
4733       nodeTo - Another  <Graph.Node> connected by this edge.
4734       data - Node data property containing a hash (i.e {}) with custom options.
4735 */
4736 Graph.Adjacence = new Class({
4737   
4738   initialize: function(nodeFrom, nodeTo, data, Edge, Label) {
4739     this.nodeFrom = nodeFrom;
4740     this.nodeTo = nodeTo;
4741     this.data = data || {};
4742     this.startData = {};
4743     this.endData = {};
4744     this.Config = this.Edge = Edge;
4745     this.Label = Label;
4746   }
4747 });
4748
4749 Graph.Adjacence.implement(Accessors);
4750
4751 /*
4752    Object: Graph.Util
4753
4754    <Graph> traversal and processing utility object.
4755    
4756    Note:
4757    
4758    For your convenience some of these methods have also been appended to <Graph> and <Graph.Node> classes.
4759 */
4760 Graph.Util = {
4761     /*
4762        filter
4763     
4764        For internal use only. Provides a filtering function based on flags.
4765     */
4766     filter: function(param) {
4767         if(!param || !($.type(param) == 'string')) return function() { return true; };
4768         var props = param.split(" ");
4769         return function(elem) {
4770             for(var i=0; i<props.length; i++) { 
4771               if(elem[props[i]]) { 
4772                 return false; 
4773               }
4774             }
4775             return true;
4776         };
4777     },
4778     /*
4779        Method: getNode
4780     
4781        Returns a <Graph.Node> by *id*.
4782        
4783        Also implemented by:
4784        
4785        <Graph>
4786
4787        Parameters:
4788
4789        graph - (object) A <Graph> instance.
4790        id - (string) A <Graph.Node> id.
4791
4792        Example:
4793
4794        (start code js)
4795          $jit.Graph.Util.getNode(graph, 'nodeid');
4796          //or...
4797          graph.getNode('nodeid');
4798        (end code)
4799     */
4800     getNode: function(graph, id) {
4801         return graph.nodes[id];
4802     },
4803     
4804     /*
4805        Method: eachNode
4806     
4807        Iterates over <Graph> nodes performing an *action*.
4808        
4809        Also implemented by:
4810        
4811        <Graph>.
4812
4813        Parameters:
4814
4815        graph - (object) A <Graph> instance.
4816        action - (function) A callback function having a <Graph.Node> as first formal parameter.
4817
4818        Example:
4819        (start code js)
4820          $jit.Graph.Util.eachNode(graph, function(node) {
4821           alert(node.name);
4822          });
4823          //or...
4824          graph.eachNode(function(node) {
4825            alert(node.name);
4826          });
4827        (end code)
4828     */
4829     eachNode: function(graph, action, flags) {
4830         var filter = this.filter(flags);
4831         for(var i in graph.nodes) {
4832           if(filter(graph.nodes[i])) action(graph.nodes[i]);
4833         } 
4834     },
4835     
4836     /*
4837       Method: each
4838    
4839       Iterates over <Graph> nodes performing an *action*. It's an alias for <Graph.Util.eachNode>.
4840       
4841       Also implemented by:
4842       
4843       <Graph>.
4844   
4845       Parameters:
4846   
4847       graph - (object) A <Graph> instance.
4848       action - (function) A callback function having a <Graph.Node> as first formal parameter.
4849   
4850       Example:
4851       (start code js)
4852         $jit.Graph.Util.each(graph, function(node) {
4853          alert(node.name);
4854         });
4855         //or...
4856         graph.each(function(node) {
4857           alert(node.name);
4858         });
4859       (end code)
4860    */
4861    each: function(graph, action, flags) {
4862       this.eachNode(graph, action, flags); 
4863    },
4864
4865  /*
4866        Method: eachAdjacency
4867     
4868        Iterates over <Graph.Node> adjacencies applying the *action* function.
4869        
4870        Also implemented by:
4871        
4872        <Graph.Node>.
4873
4874        Parameters:
4875
4876        node - (object) A <Graph.Node>.
4877        action - (function) A callback function having <Graph.Adjacence> as first formal parameter.
4878
4879        Example:
4880        (start code js)
4881          $jit.Graph.Util.eachAdjacency(node, function(adj) {
4882           alert(adj.nodeTo.name);
4883          });
4884          //or...
4885          node.eachAdjacency(function(adj) {
4886            alert(adj.nodeTo.name);
4887          });
4888        (end code)
4889     */
4890     eachAdjacency: function(node, action, flags) {
4891         var adj = node.adjacencies, filter = this.filter(flags);
4892         for(var id in adj) {
4893           var a = adj[id];
4894           if(filter(a)) {
4895             if(a.nodeFrom != node) {
4896               var tmp = a.nodeFrom;
4897               a.nodeFrom = a.nodeTo;
4898               a.nodeTo = tmp;
4899             }
4900             action(a, id);
4901           }
4902         }
4903     },
4904
4905      /*
4906        Method: computeLevels
4907     
4908        Performs a BFS traversal setting the correct depth for each node.
4909         
4910        Also implemented by:
4911        
4912        <Graph>.
4913        
4914        Note:
4915        
4916        The depth of each node can then be accessed by 
4917        >node._depth
4918
4919        Parameters:
4920
4921        graph - (object) A <Graph>.
4922        id - (string) A starting node id for the BFS traversal.
4923        startDepth - (optional|number) A minimum depth value. Default's 0.
4924
4925     */
4926     computeLevels: function(graph, id, startDepth, flags) {
4927         startDepth = startDepth || 0;
4928         var filter = this.filter(flags);
4929         this.eachNode(graph, function(elem) {
4930             elem._flag = false;
4931             elem._depth = -1;
4932         }, flags);
4933         var root = graph.getNode(id);
4934         root._depth = startDepth;
4935         var queue = [root];
4936         while(queue.length != 0) {
4937             var node = queue.pop();
4938             node._flag = true;
4939             this.eachAdjacency(node, function(adj) {
4940                 var n = adj.nodeTo;
4941                 if(n._flag == false && filter(n)) {
4942                     if(n._depth < 0) n._depth = node._depth + 1 + startDepth;
4943                     queue.unshift(n);
4944                 }
4945             }, flags);
4946         }
4947     },
4948
4949     /*
4950        Method: eachBFS
4951     
4952        Performs a BFS traversal applying *action* to each <Graph.Node>.
4953        
4954        Also implemented by:
4955        
4956        <Graph>.
4957
4958        Parameters:
4959
4960        graph - (object) A <Graph>.
4961        id - (string) A starting node id for the BFS traversal.
4962        action - (function) A callback function having a <Graph.Node> as first formal parameter.
4963
4964        Example:
4965        (start code js)
4966          $jit.Graph.Util.eachBFS(graph, 'mynodeid', function(node) {
4967           alert(node.name);
4968          });
4969          //or...
4970          graph.eachBFS('mynodeid', function(node) {
4971            alert(node.name);
4972          });
4973        (end code)
4974     */
4975     eachBFS: function(graph, id, action, flags) {
4976         var filter = this.filter(flags);
4977         this.clean(graph);
4978         var queue = [graph.getNode(id)];
4979         while(queue.length != 0) {
4980             var node = queue.pop();
4981             node._flag = true;
4982             action(node, node._depth);
4983             this.eachAdjacency(node, function(adj) {
4984                 var n = adj.nodeTo;
4985                 if(n._flag == false && filter(n)) {
4986                     n._flag = true;
4987                     queue.unshift(n);
4988                 }
4989             }, flags);
4990         }
4991     },
4992     
4993     /*
4994        Method: eachLevel
4995     
4996        Iterates over a node's subgraph applying *action* to the nodes of relative depth between *levelBegin* and *levelEnd*.
4997        
4998        Also implemented by:
4999        
5000        <Graph.Node>.
5001
5002        Parameters:
5003        
5004        node - (object) A <Graph.Node>.
5005        levelBegin - (number) A relative level value.
5006        levelEnd - (number) A relative level value.
5007        action - (function) A callback function having a <Graph.Node> as first formal parameter.
5008
5009     */
5010     eachLevel: function(node, levelBegin, levelEnd, action, flags) {
5011         var d = node._depth, filter = this.filter(flags), that = this;
5012         levelEnd = levelEnd === false? Number.MAX_VALUE -d : levelEnd;
5013         (function loopLevel(node, levelBegin, levelEnd) {
5014             var d = node._depth;
5015             if(d >= levelBegin && d <= levelEnd && filter(node)) action(node, d);
5016             if(d < levelEnd) {
5017                 that.eachAdjacency(node, function(adj) {
5018                     var n = adj.nodeTo;
5019                     if(n._depth > d) loopLevel(n, levelBegin, levelEnd);
5020                 });
5021             }
5022         })(node, levelBegin + d, levelEnd + d);      
5023     },
5024
5025     /*
5026        Method: eachSubgraph
5027     
5028        Iterates over a node's children recursively.
5029        
5030        Also implemented by:
5031        
5032        <Graph.Node>.
5033
5034        Parameters:
5035        node - (object) A <Graph.Node>.
5036        action - (function) A callback function having a <Graph.Node> as first formal parameter.
5037
5038        Example:
5039        (start code js)
5040          $jit.Graph.Util.eachSubgraph(node, function(node) {
5041            alert(node.name);
5042          });
5043          //or...
5044          node.eachSubgraph(function(node) {
5045            alert(node.name);
5046          });
5047        (end code)
5048     */
5049     eachSubgraph: function(node, action, flags) {
5050       this.eachLevel(node, 0, false, action, flags);
5051     },
5052
5053     /*
5054        Method: eachSubnode
5055     
5056        Iterates over a node's children (without deeper recursion).
5057        
5058        Also implemented by:
5059        
5060        <Graph.Node>.
5061        
5062        Parameters:
5063        node - (object) A <Graph.Node>.
5064        action - (function) A callback function having a <Graph.Node> as first formal parameter.
5065
5066        Example:
5067        (start code js)
5068          $jit.Graph.Util.eachSubnode(node, function(node) {
5069           alert(node.name);
5070          });
5071          //or...
5072          node.eachSubnode(function(node) {
5073            alert(node.name);
5074          });
5075        (end code)
5076     */
5077     eachSubnode: function(node, action, flags) {
5078         this.eachLevel(node, 1, 1, action, flags);
5079     },
5080
5081     /*
5082        Method: anySubnode
5083     
5084        Returns *true* if any subnode matches the given condition.
5085        
5086        Also implemented by:
5087        
5088        <Graph.Node>.
5089
5090        Parameters:
5091        node - (object) A <Graph.Node>.
5092        cond - (function) A callback function returning a Boolean instance. This function has as first formal parameter a <Graph.Node>.
5093
5094        Example:
5095        (start code js)
5096          $jit.Graph.Util.anySubnode(node, function(node) { return node.name == "mynodename"; });
5097          //or...
5098          node.anySubnode(function(node) { return node.name == 'mynodename'; });
5099        (end code)
5100     */
5101     anySubnode: function(node, cond, flags) {
5102       var flag = false;
5103       cond = cond || $.lambda(true);
5104       var c = $.type(cond) == 'string'? function(n) { return n[cond]; } : cond;
5105       this.eachSubnode(node, function(elem) {
5106         if(c(elem)) flag = true;
5107       }, flags);
5108       return flag;
5109     },
5110   
5111     /*
5112        Method: getSubnodes
5113     
5114        Collects all subnodes for a specified node. 
5115        The *level* parameter filters nodes having relative depth of *level* from the root node. 
5116        
5117        Also implemented by:
5118        
5119        <Graph.Node>.
5120
5121        Parameters:
5122        node - (object) A <Graph.Node>.
5123        level - (optional|number) Default's *0*. A starting relative depth for collecting nodes.
5124
5125        Returns:
5126        An array of nodes.
5127
5128     */
5129     getSubnodes: function(node, level, flags) {
5130         var ans = [], that = this;
5131         level = level || 0;
5132         var levelStart, levelEnd;
5133         if($.type(level) == 'array') {
5134             levelStart = level[0];
5135             levelEnd = level[1];
5136         } else {
5137             levelStart = level;
5138             levelEnd = Number.MAX_VALUE - node._depth;
5139         }
5140         this.eachLevel(node, levelStart, levelEnd, function(n) {
5141             ans.push(n);
5142         }, flags);
5143         return ans;
5144     },
5145   
5146   
5147     /*
5148        Method: getParents
5149     
5150        Returns an Array of <Graph.Nodes> which are parents of the given node.
5151        
5152        Also implemented by:
5153        
5154        <Graph.Node>.
5155
5156        Parameters:
5157        node - (object) A <Graph.Node>.
5158
5159        Returns:
5160        An Array of <Graph.Nodes>.
5161
5162        Example:
5163        (start code js)
5164          var pars = $jit.Graph.Util.getParents(node);
5165          //or...
5166          var pars = node.getParents();
5167          
5168          if(pars.length > 0) {
5169            //do stuff with parents
5170          }
5171        (end code)
5172     */
5173     getParents: function(node) {
5174         var ans = [];
5175         this.eachAdjacency(node, function(adj) {
5176             var n = adj.nodeTo;
5177             if(n._depth < node._depth) ans.push(n);
5178         });
5179         return ans;
5180     },
5181     
5182     /*
5183     Method: isDescendantOf
5184  
5185     Returns a boolean indicating if some node is descendant of the node with the given id. 
5186
5187     Also implemented by:
5188     
5189     <Graph.Node>.
5190     
5191     
5192     Parameters:
5193     node - (object) A <Graph.Node>.
5194     id - (string) A <Graph.Node> id.
5195
5196     Example:
5197     (start code js)
5198       $jit.Graph.Util.isDescendantOf(node, "nodeid"); //true|false
5199       //or...
5200       node.isDescendantOf('nodeid');//true|false
5201     (end code)
5202  */
5203  isDescendantOf: function(node, id) {
5204     if(node.id == id) return true;
5205     var pars = this.getParents(node), ans = false;
5206     for ( var i = 0; !ans && i < pars.length; i++) {
5207     ans = ans || this.isDescendantOf(pars[i], id);
5208   }
5209     return ans;
5210  },
5211
5212  /*
5213      Method: clean
5214   
5215      Cleans flags from nodes.
5216
5217      Also implemented by:
5218      
5219      <Graph>.
5220      
5221      Parameters:
5222      graph - A <Graph> instance.
5223   */
5224   clean: function(graph) { this.eachNode(graph, function(elem) { elem._flag = false; }); },
5225   
5226   /* 
5227     Method: getClosestNodeToOrigin 
5228   
5229     Returns the closest node to the center of canvas.
5230   
5231     Also implemented by:
5232     
5233     <Graph>.
5234     
5235     Parameters:
5236    
5237      graph - (object) A <Graph> instance.
5238      prop - (optional|string) Default's 'current'. A <Graph.Node> position property. Possible properties are 'start', 'current' or 'end'.
5239   
5240   */
5241   getClosestNodeToOrigin: function(graph, prop, flags) {
5242    return this.getClosestNodeToPos(graph, Polar.KER, prop, flags);
5243   },
5244   
5245   /* 
5246     Method: getClosestNodeToPos
5247   
5248     Returns the closest node to the given position.
5249   
5250     Also implemented by:
5251     
5252     <Graph>.
5253     
5254     Parameters:
5255    
5256      graph - (object) A <Graph> instance.
5257      pos - (object) A <Complex> or <Polar> instance.
5258      prop - (optional|string) Default's *current*. A <Graph.Node> position property. Possible properties are 'start', 'current' or 'end'.
5259   
5260   */
5261   getClosestNodeToPos: function(graph, pos, prop, flags) {
5262    var node = null;
5263    prop = prop || 'current';
5264    pos = pos && pos.getc(true) || Complex.KER;
5265    var distance = function(a, b) {
5266      var d1 = a.x - b.x, d2 = a.y - b.y;
5267      return d1 * d1 + d2 * d2;
5268    };
5269    this.eachNode(graph, function(elem) {
5270      node = (node == null || distance(elem.getPos(prop).getc(true), pos) < distance(
5271          node.getPos(prop).getc(true), pos)) ? elem : node;
5272    }, flags);
5273    return node;
5274   } 
5275 };
5276
5277 //Append graph methods to <Graph>
5278 $.each(['get', 'getNode', 'each', 'eachNode', 'computeLevels', 'eachBFS', 'clean', 'getClosestNodeToPos', 'getClosestNodeToOrigin'], function(m) {
5279   Graph.prototype[m] = function() {
5280     return Graph.Util[m].apply(Graph.Util, [this].concat(Array.prototype.slice.call(arguments)));
5281   };
5282 });
5283
5284 //Append node methods to <Graph.Node>
5285 $.each(['eachAdjacency', 'eachLevel', 'eachSubgraph', 'eachSubnode', 'anySubnode', 'getSubnodes', 'getParents', 'isDescendantOf'], function(m) {
5286   Graph.Node.prototype[m] = function() {
5287     return Graph.Util[m].apply(Graph.Util, [this].concat(Array.prototype.slice.call(arguments)));
5288   };
5289 });
5290
5291 /*
5292  * File: Graph.Op.js
5293  *
5294 */
5295
5296 /*
5297    Object: Graph.Op
5298
5299    Perform <Graph> operations like adding/removing <Graph.Nodes> or <Graph.Adjacences>, 
5300    morphing a <Graph> into another <Graph>, contracting or expanding subtrees, etc.
5301
5302 */
5303 Graph.Op = {
5304
5305     options: {
5306       type: 'nothing',
5307       duration: 2000,
5308       hideLabels: true,
5309       fps:30
5310     },
5311     
5312     initialize: function(viz) {
5313       this.viz = viz;
5314     },
5315
5316     /*
5317        Method: removeNode
5318     
5319        Removes one or more <Graph.Nodes> from the visualization. 
5320        It can also perform several animations like fading sequentially, fading concurrently, iterating or replotting.
5321
5322        Parameters:
5323     
5324         node - (string|array) The node's id. Can also be an array having many ids.
5325         opt - (object) Animation options. It's an object with optional properties described below
5326         type - (string) Default's *nothing*. Type of the animation. Can be "nothing", "replot", "fade:seq",  "fade:con" or "iter".
5327         duration - Described in <Options.Fx>.
5328         fps - Described in <Options.Fx>.
5329         transition - Described in <Options.Fx>.
5330         hideLabels - (boolean) Default's *true*. Hide labels during the animation.
5331    
5332       Example:
5333       (start code js)
5334         var viz = new $jit.Viz(options);
5335         viz.op.removeNode('nodeId', {
5336           type: 'fade:seq',
5337           duration: 1000,
5338           hideLabels: false,
5339           transition: $jit.Trans.Quart.easeOut
5340         });
5341         //or also
5342         viz.op.removeNode(['someId', 'otherId'], {
5343           type: 'fade:con',
5344           duration: 1500
5345         });
5346       (end code)
5347     */
5348   
5349     removeNode: function(node, opt) {
5350         var viz = this.viz;
5351         var options = $.merge(this.options, viz.controller, opt);
5352         var n = $.splat(node);
5353         var i, that, nodeObj;
5354         switch(options.type) {
5355             case 'nothing':
5356                 for(i=0; i<n.length; i++) viz.graph.removeNode(n[i]);
5357                 break;
5358             
5359             case 'replot':
5360                 this.removeNode(n, { type: 'nothing' });
5361                 viz.labels.clearLabels();
5362                 viz.refresh(true);
5363                 break;
5364             
5365             case 'fade:seq': case 'fade':
5366                 that = this;
5367                 //set alpha to 0 for nodes to remove.
5368                 for(i=0; i<n.length; i++) {
5369                     nodeObj = viz.graph.getNode(n[i]);
5370                     nodeObj.setData('alpha', 0, 'end');
5371                 }
5372                 viz.fx.animate($.merge(options, {
5373                     modes: ['node-property:alpha'],
5374                     onComplete: function() {
5375                         that.removeNode(n, { type: 'nothing' });
5376                         viz.labels.clearLabels();
5377                         viz.reposition();
5378                         viz.fx.animate($.merge(options, {
5379                             modes: ['linear']
5380                         }));
5381                     }
5382                 }));
5383                 break;
5384             
5385             case 'fade:con':
5386                 that = this;
5387                 //set alpha to 0 for nodes to remove. Tag them for being ignored on computing positions.
5388                 for(i=0; i<n.length; i++) {
5389                     nodeObj = viz.graph.getNode(n[i]);
5390                     nodeObj.setData('alpha', 0, 'end');
5391                     nodeObj.ignore = true;
5392                 }
5393                 viz.reposition();
5394                 viz.fx.animate($.merge(options, {
5395                     modes: ['node-property:alpha', 'linear'],
5396                     onComplete: function() {
5397                         that.removeNode(n, { type: 'nothing' });
5398                         options.onComplete && options.onComplete();
5399                     }
5400                 }));
5401                 break;
5402             
5403             case 'iter':
5404                 that = this;
5405                 viz.fx.sequence({
5406                     condition: function() { return n.length != 0; },
5407                     step: function() { that.removeNode(n.shift(), { type: 'nothing' });  viz.labels.clearLabels(); },
5408                     onComplete: function() { options.onComplete && options.onComplete(); },
5409                     duration: Math.ceil(options.duration / n.length)
5410                 });
5411                 break;
5412                 
5413             default: this.doError();
5414         }
5415     },
5416     
5417     /*
5418        Method: removeEdge
5419     
5420        Removes one or more <Graph.Adjacences> from the visualization. 
5421        It can also perform several animations like fading sequentially, fading concurrently, iterating or replotting.
5422
5423        Parameters:
5424     
5425        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'], ...]).
5426        opt - (object) Animation options. It's an object with optional properties described below
5427        type - (string) Default's *nothing*. Type of the animation. Can be "nothing", "replot", "fade:seq",  "fade:con" or "iter".
5428        duration - Described in <Options.Fx>.
5429        fps - Described in <Options.Fx>.
5430        transition - Described in <Options.Fx>.
5431        hideLabels - (boolean) Default's *true*. Hide labels during the animation.
5432    
5433       Example:
5434       (start code js)
5435         var viz = new $jit.Viz(options);
5436         viz.op.removeEdge(['nodeId', 'otherId'], {
5437           type: 'fade:seq',
5438           duration: 1000,
5439           hideLabels: false,
5440           transition: $jit.Trans.Quart.easeOut
5441         });
5442         //or also
5443         viz.op.removeEdge([['someId', 'otherId'], ['id3', 'id4']], {
5444           type: 'fade:con',
5445           duration: 1500
5446         });
5447       (end code)
5448     
5449     */
5450     removeEdge: function(vertex, opt) {
5451         var viz = this.viz;
5452         var options = $.merge(this.options, viz.controller, opt);
5453         var v = ($.type(vertex[0]) == 'string')? [vertex] : vertex;
5454         var i, that, adj;
5455         switch(options.type) {
5456             case 'nothing':
5457                 for(i=0; i<v.length; i++)   viz.graph.removeAdjacence(v[i][0], v[i][1]);
5458                 break;
5459             
5460             case 'replot':
5461                 this.removeEdge(v, { type: 'nothing' });
5462                 viz.refresh(true);
5463                 break;
5464             
5465             case 'fade:seq': case 'fade':
5466                 that = this;
5467                 //set alpha to 0 for edges to remove.
5468                 for(i=0; i<v.length; i++) {
5469                     adj = viz.graph.getAdjacence(v[i][0], v[i][1]);
5470                     if(adj) {
5471                         adj.setData('alpha', 0,'end');
5472                     }
5473                 }
5474                 viz.fx.animate($.merge(options, {
5475                     modes: ['edge-property:alpha'],
5476                     onComplete: function() {
5477                         that.removeEdge(v, { type: 'nothing' });
5478                         viz.reposition();
5479                         viz.fx.animate($.merge(options, {
5480                             modes: ['linear']
5481                         }));
5482                     }
5483                 }));
5484                 break;
5485             
5486             case 'fade:con':
5487                 that = this;
5488                 //set alpha to 0 for nodes to remove. Tag them for being ignored when computing positions.
5489                 for(i=0; i<v.length; i++) {
5490                     adj = viz.graph.getAdjacence(v[i][0], v[i][1]);
5491                     if(adj) {
5492                         adj.setData('alpha',0 ,'end');
5493                         adj.ignore = true;
5494                     }
5495                 }
5496                 viz.reposition();
5497                 viz.fx.animate($.merge(options, {
5498                     modes: ['edge-property:alpha', 'linear'],
5499                     onComplete: function() {
5500                         that.removeEdge(v, { type: 'nothing' });
5501                         options.onComplete && options.onComplete();
5502                     }
5503                 }));
5504                 break;
5505             
5506             case 'iter':
5507                 that = this;
5508                 viz.fx.sequence({
5509                     condition: function() { return v.length != 0; },
5510                     step: function() { that.removeEdge(v.shift(), { type: 'nothing' }); viz.labels.clearLabels(); },
5511                     onComplete: function() { options.onComplete(); },
5512                     duration: Math.ceil(options.duration / v.length)
5513                 });
5514                 break;
5515                 
5516             default: this.doError();
5517         }
5518     },
5519     
5520     /*
5521        Method: sum
5522     
5523        Adds a new graph to the visualization. 
5524        The JSON graph (or tree) must at least have a common node with the current graph plotted by the visualization. 
5525        The resulting graph can be defined as follows <http://mathworld.wolfram.com/GraphSum.html>
5526
5527        Parameters:
5528     
5529        json - (object) A json tree or graph structure. See also <Loader.loadJSON>.
5530        opt - (object) Animation options. It's an object with optional properties described below
5531        type - (string) Default's *nothing*. Type of the animation. Can be "nothing", "replot", "fade:seq",  "fade:con".
5532        duration - Described in <Options.Fx>.
5533        fps - Described in <Options.Fx>.
5534        transition - Described in <Options.Fx>.
5535        hideLabels - (boolean) Default's *true*. Hide labels during the animation.
5536    
5537       Example:
5538       (start code js)
5539         //...json contains a tree or graph structure...
5540
5541         var viz = new $jit.Viz(options);
5542         viz.op.sum(json, {
5543           type: 'fade:seq',
5544           duration: 1000,
5545           hideLabels: false,
5546           transition: $jit.Trans.Quart.easeOut
5547         });
5548         //or also
5549         viz.op.sum(json, {
5550           type: 'fade:con',
5551           duration: 1500
5552         });
5553       (end code)
5554     
5555     */
5556     sum: function(json, opt) {
5557         var viz = this.viz;
5558         var options = $.merge(this.options, viz.controller, opt), root = viz.root;
5559         var graph;
5560         viz.root = opt.id || viz.root;
5561         switch(options.type) {
5562             case 'nothing':
5563                 graph = viz.construct(json);
5564                 graph.eachNode(function(elem) {
5565                     elem.eachAdjacency(function(adj) {
5566                         viz.graph.addAdjacence(adj.nodeFrom, adj.nodeTo, adj.data);
5567                     });
5568                 });
5569                 break;
5570             
5571             case 'replot':
5572                 viz.refresh(true);
5573                 this.sum(json, { type: 'nothing' });
5574                 viz.refresh(true);
5575                 break;
5576             
5577             case 'fade:seq': case 'fade': case 'fade:con':
5578                 that = this;
5579                 graph = viz.construct(json);
5580
5581                 //set alpha to 0 for nodes to add.
5582                 var fadeEdges = this.preprocessSum(graph);
5583                 var modes = !fadeEdges? ['node-property:alpha'] : ['node-property:alpha', 'edge-property:alpha'];
5584                 viz.reposition();
5585                 if(options.type != 'fade:con') {
5586                     viz.fx.animate($.merge(options, {
5587                         modes: ['linear'],
5588                         onComplete: function() {
5589                             viz.fx.animate($.merge(options, {
5590                                 modes: modes,
5591                                 onComplete: function() {
5592                                     options.onComplete();
5593                                 }
5594                             }));
5595                         }
5596                     }));
5597                 } else {
5598                     viz.graph.eachNode(function(elem) {
5599                         if (elem.id != root && elem.pos.isZero()) {
5600                           elem.pos.set(elem.endPos); 
5601                           elem.startPos.set(elem.endPos);
5602                         }
5603                     });
5604                     viz.fx.animate($.merge(options, {
5605                         modes: ['linear'].concat(modes)
5606                     }));
5607                 }
5608                 break;
5609
5610             default: this.doError();
5611         }
5612     },
5613     
5614     /*
5615        Method: morph
5616     
5617        This method will transform the current visualized graph into the new JSON representation passed in the method. 
5618        The JSON object must at least have the root node in common with the current visualized graph.
5619
5620        Parameters:
5621     
5622        json - (object) A json tree or graph structure. See also <Loader.loadJSON>.
5623        opt - (object) Animation options. It's an object with optional properties described below
5624        type - (string) Default's *nothing*. Type of the animation. Can be "nothing", "replot", "fade:con".
5625        duration - Described in <Options.Fx>.
5626        fps - Described in <Options.Fx>.
5627        transition - Described in <Options.Fx>.
5628        hideLabels - (boolean) Default's *true*. Hide labels during the animation.
5629        id - (string) The shared <Graph.Node> id between both graphs.
5630        
5631        extraModes - (optional|object) When morphing with an animation, dollar prefixed data parameters are added to 
5632                     *endData* and not *data* itself. This way you can animate dollar prefixed parameters during your morphing operation. 
5633                     For animating these extra-parameters you have to specify an object that has animation groups as keys and animation 
5634                     properties as values, just like specified in <Graph.Plot.animate>.
5635    
5636       Example:
5637       (start code js)
5638         //...json contains a tree or graph structure...
5639
5640         var viz = new $jit.Viz(options);
5641         viz.op.morph(json, {
5642           type: 'fade',
5643           duration: 1000,
5644           hideLabels: false,
5645           transition: $jit.Trans.Quart.easeOut
5646         });
5647         //or also
5648         viz.op.morph(json, {
5649           type: 'fade',
5650           duration: 1500
5651         });
5652         //if the json data contains dollar prefixed params
5653         //like $width or $height these too can be animated
5654         viz.op.morph(json, {
5655           type: 'fade',
5656           duration: 1500
5657         }, {
5658           'node-property': ['width', 'height']
5659         });
5660       (end code)
5661     
5662     */
5663     morph: function(json, opt, extraModes) {
5664         extraModes = extraModes || {};
5665         var viz = this.viz;
5666         var options = $.merge(this.options, viz.controller, opt), root = viz.root;
5667         var graph;
5668         //TODO(nico) this hack makes morphing work with the Hypertree. 
5669         //Need to check if it has been solved and this can be removed.
5670         viz.root = opt.id || viz.root;
5671         switch(options.type) {
5672             case 'nothing':
5673                 graph = viz.construct(json);
5674                 graph.eachNode(function(elem) {
5675                   var nodeExists = viz.graph.hasNode(elem.id);  
5676                   elem.eachAdjacency(function(adj) {
5677                     var adjExists = !!viz.graph.getAdjacence(adj.nodeFrom.id, adj.nodeTo.id);
5678                     viz.graph.addAdjacence(adj.nodeFrom, adj.nodeTo, adj.data);
5679                     //Update data properties if the node existed
5680                     if(adjExists) {
5681                       var addedAdj = viz.graph.getAdjacence(adj.nodeFrom.id, adj.nodeTo.id);
5682                       for(var prop in (adj.data || {})) {
5683                         addedAdj.data[prop] = adj.data[prop];
5684                       }
5685                     }
5686                   });
5687                   //Update data properties if the node existed
5688                   if(nodeExists) {
5689                     var addedNode = viz.graph.getNode(elem.id);
5690                     for(var prop in (elem.data || {})) {
5691                       addedNode.data[prop] = elem.data[prop];
5692                     }
5693                   }
5694                 });
5695                 viz.graph.eachNode(function(elem) {
5696                     elem.eachAdjacency(function(adj) {
5697                         if(!graph.getAdjacence(adj.nodeFrom.id, adj.nodeTo.id)) {
5698                             viz.graph.removeAdjacence(adj.nodeFrom.id, adj.nodeTo.id);
5699                         }
5700                     });
5701                     if(!graph.hasNode(elem.id)) viz.graph.removeNode(elem.id);
5702                 });
5703                 
5704                 break;
5705             
5706             case 'replot':
5707                 viz.labels.clearLabels(true);
5708                 this.morph(json, { type: 'nothing' });
5709                 viz.refresh(true);
5710                 viz.refresh(true);
5711                 break;
5712                 
5713             case 'fade:seq': case 'fade': case 'fade:con':
5714                 that = this;
5715                 graph = viz.construct(json);
5716                 //preprocessing for nodes to delete.
5717                 //get node property modes to interpolate
5718                 var nodeModes = ('node-property' in extraModes) 
5719                   && $.map($.splat(extraModes['node-property']), 
5720                       function(n) { return '$' + n; });
5721                 viz.graph.eachNode(function(elem) {
5722                   var graphNode = graph.getNode(elem.id);   
5723                   if(!graphNode) {
5724                       elem.setData('alpha', 1);
5725                       elem.setData('alpha', 1, 'start');
5726                       elem.setData('alpha', 0, 'end');
5727                       elem.ignore = true;
5728                     } else {
5729                       //Update node data information
5730                       var graphNodeData = graphNode.data;
5731                       for(var prop in graphNodeData) {
5732                         if(nodeModes && ($.indexOf(nodeModes, prop) > -1)) {
5733                           elem.endData[prop] = graphNodeData[prop];
5734                         } else {
5735                           elem.data[prop] = graphNodeData[prop];
5736                         }
5737                       }
5738                     }
5739                 }); 
5740                 viz.graph.eachNode(function(elem) {
5741                     if(elem.ignore) return;
5742                     elem.eachAdjacency(function(adj) {
5743                         if(adj.nodeFrom.ignore || adj.nodeTo.ignore) return;
5744                         var nodeFrom = graph.getNode(adj.nodeFrom.id);
5745                         var nodeTo = graph.getNode(adj.nodeTo.id);
5746                         if(!nodeFrom.adjacentTo(nodeTo)) {
5747                             var adj = viz.graph.getAdjacence(nodeFrom.id, nodeTo.id);
5748                             fadeEdges = true;
5749                             adj.setData('alpha', 1);
5750                             adj.setData('alpha', 1, 'start');
5751                             adj.setData('alpha', 0, 'end');
5752                         }
5753                     });
5754                 }); 
5755                 //preprocessing for adding nodes.
5756                 var fadeEdges = this.preprocessSum(graph);
5757
5758                 var modes = !fadeEdges? ['node-property:alpha'] : 
5759                                         ['node-property:alpha', 
5760                                          'edge-property:alpha'];
5761                 //Append extra node-property animations (if any)
5762                 modes[0] = modes[0] + (('node-property' in extraModes)? 
5763                     (':' + $.splat(extraModes['node-property']).join(':')) : '');
5764                 //Append extra edge-property animations (if any)
5765                 modes[1] = (modes[1] || 'edge-property:alpha') + (('edge-property' in extraModes)? 
5766                     (':' + $.splat(extraModes['edge-property']).join(':')) : '');
5767                 //Add label-property animations (if any)
5768                 if('label-property' in extraModes) {
5769                   modes.push('label-property:' + $.splat(extraModes['label-property']).join(':'))
5770                 }
5771                 //only use reposition if its implemented.
5772                 if (viz.reposition) {
5773                   viz.reposition();
5774                 } else {
5775                   viz.compute('end');
5776                 }
5777                 viz.graph.eachNode(function(elem) {
5778                     if (elem.id != root && elem.pos.getp().equals(Polar.KER)) {
5779                       elem.pos.set(elem.endPos); elem.startPos.set(elem.endPos);
5780                     }
5781                 });
5782                 viz.fx.animate($.merge(options, {
5783                     modes: [extraModes.position || 'polar'].concat(modes),
5784                     onComplete: function() {
5785                         viz.graph.eachNode(function(elem) {
5786                             if(elem.ignore) viz.graph.removeNode(elem.id);
5787                         });
5788                         viz.graph.eachNode(function(elem) {
5789                             elem.eachAdjacency(function(adj) {
5790                                 if(adj.ignore) viz.graph.removeAdjacence(adj.nodeFrom.id, adj.nodeTo.id);
5791                             });
5792                         });
5793                         options.onComplete();
5794                     }
5795                 }));
5796                 break;
5797
5798             default:;
5799         }
5800     },
5801
5802     
5803   /*
5804     Method: contract
5805  
5806     Collapses the subtree of the given node. The node will have a _collapsed=true_ property.
5807     
5808     Parameters:
5809  
5810     node - (object) A <Graph.Node>.
5811     opt - (object) An object containing options described below
5812     type - (string) Whether to 'replot' or 'animate' the contraction.
5813    
5814     There are also a number of Animation options. For more information see <Options.Fx>.
5815
5816     Example:
5817     (start code js)
5818      var viz = new $jit.Viz(options);
5819      viz.op.contract(node, {
5820        type: 'animate',
5821        duration: 1000,
5822        hideLabels: true,
5823        transition: $jit.Trans.Quart.easeOut
5824      });
5825    (end code)
5826  
5827    */
5828     contract: function(node, opt) {
5829       var viz = this.viz;
5830       if(node.collapsed || !node.anySubnode($.lambda(true))) return;
5831       opt = $.merge(this.options, viz.config, opt || {}, {
5832         'modes': ['node-property:alpha:span', 'linear']
5833       });
5834       node.collapsed = true;
5835       (function subn(n) {
5836         n.eachSubnode(function(ch) {
5837           ch.ignore = true;
5838           ch.setData('alpha', 0, opt.type == 'animate'? 'end' : 'current');
5839           subn(ch);
5840         });
5841       })(node);
5842       if(opt.type == 'animate') {
5843         viz.compute('end');
5844         if(viz.rotated) {
5845           viz.rotate(viz.rotated, 'none', {
5846             'property':'end'
5847           });
5848         }
5849         (function subn(n) {
5850           n.eachSubnode(function(ch) {
5851             ch.setPos(node.getPos('end'), 'end');
5852             subn(ch);
5853           });
5854         })(node);
5855         viz.fx.animate(opt);
5856       } else if(opt.type == 'replot'){
5857         viz.refresh();
5858       }
5859     },
5860     
5861     /*
5862     Method: expand
5863  
5864     Expands the previously contracted subtree. The given node must have the _collapsed=true_ property.
5865     
5866     Parameters:
5867  
5868     node - (object) A <Graph.Node>.
5869     opt - (object) An object containing options described below
5870     type - (string) Whether to 'replot' or 'animate'.
5871      
5872     There are also a number of Animation options. For more information see <Options.Fx>.
5873
5874     Example:
5875     (start code js)
5876       var viz = new $jit.Viz(options);
5877       viz.op.expand(node, {
5878         type: 'animate',
5879         duration: 1000,
5880         hideLabels: true,
5881         transition: $jit.Trans.Quart.easeOut
5882       });
5883     (end code)
5884  
5885    */
5886     expand: function(node, opt) {
5887       if(!('collapsed' in node)) return;
5888       var viz = this.viz;
5889       opt = $.merge(this.options, viz.config, opt || {}, {
5890         'modes': ['node-property:alpha:span', 'linear']
5891       });
5892       delete node.collapsed;
5893       (function subn(n) {
5894         n.eachSubnode(function(ch) {
5895           delete ch.ignore;
5896           ch.setData('alpha', 1, opt.type == 'animate'? 'end' : 'current');
5897           subn(ch);
5898         });
5899       })(node);
5900       if(opt.type == 'animate') {
5901         viz.compute('end');
5902         if(viz.rotated) {
5903           viz.rotate(viz.rotated, 'none', {
5904             'property':'end'
5905           });
5906         }
5907         viz.fx.animate(opt);
5908       } else if(opt.type == 'replot'){
5909         viz.refresh();
5910       }
5911     },
5912
5913     preprocessSum: function(graph) {
5914         var viz = this.viz;
5915         graph.eachNode(function(elem) {
5916             if(!viz.graph.hasNode(elem.id)) {
5917                 viz.graph.addNode(elem);
5918                 var n = viz.graph.getNode(elem.id);
5919                 n.setData('alpha', 0);
5920                 n.setData('alpha', 0, 'start');
5921                 n.setData('alpha', 1, 'end');
5922             }
5923         }); 
5924         var fadeEdges = false;
5925         graph.eachNode(function(elem) {
5926             elem.eachAdjacency(function(adj) {
5927                 var nodeFrom = viz.graph.getNode(adj.nodeFrom.id);
5928                 var nodeTo = viz.graph.getNode(adj.nodeTo.id);
5929                 if(!nodeFrom.adjacentTo(nodeTo)) {
5930                     var adj = viz.graph.addAdjacence(nodeFrom, nodeTo, adj.data);
5931                     if(nodeFrom.startAlpha == nodeFrom.endAlpha 
5932                     && nodeTo.startAlpha == nodeTo.endAlpha) {
5933                         fadeEdges = true;
5934                         adj.setData('alpha', 0);
5935                         adj.setData('alpha', 0, 'start');
5936                         adj.setData('alpha', 1, 'end');
5937                     } 
5938                 }
5939             });
5940         }); 
5941         return fadeEdges;
5942     }
5943 };
5944
5945
5946
5947 /*
5948    File: Helpers.js
5949  
5950    Helpers are objects that contain rendering primitives (like rectangles, ellipses, etc), for plotting nodes and edges.
5951    Helpers also contain implementations of the *contains* method, a method returning a boolean indicating whether the mouse
5952    position is over the rendered shape.
5953    
5954    Helpers are very useful when implementing new NodeTypes, since you can access them through *this.nodeHelper* and 
5955    *this.edgeHelper* <Graph.Plot> properties, providing you with simple primitives and mouse-position check functions.
5956    
5957    Example:
5958    (start code js)
5959    //implement a new node type
5960    $jit.Viz.Plot.NodeTypes.implement({
5961      'customNodeType': {
5962        'render': function(node, canvas) {
5963          this.nodeHelper.circle.render ...
5964        },
5965        'contains': function(node, pos) {
5966          this.nodeHelper.circle.contains ...
5967        }
5968      }
5969    });
5970    //implement an edge type
5971    $jit.Viz.Plot.EdgeTypes.implement({
5972      'customNodeType': {
5973        'render': function(node, canvas) {
5974          this.edgeHelper.circle.render ...
5975        },
5976        //optional
5977        'contains': function(node, pos) {
5978          this.edgeHelper.circle.contains ...
5979        }
5980      }
5981    });
5982    (end code)
5983
5984 */
5985
5986 /*
5987    Object: NodeHelper
5988    
5989    Contains rendering and other type of primitives for simple shapes.
5990  */
5991 var NodeHelper = {
5992   'none': {
5993     'render': $.empty,
5994     'contains': $.lambda(false)
5995   },
5996   /*
5997    Object: NodeHelper.circle
5998    */
5999   'circle': {
6000     /*
6001      Method: render
6002      
6003      Renders a circle into the canvas.
6004      
6005      Parameters:
6006      
6007      type - (string) Possible options are 'fill' or 'stroke'.
6008      pos - (object) An *x*, *y* object with the position of the center of the circle.
6009      radius - (number) The radius of the circle to be rendered.
6010      canvas - (object) A <Canvas> instance.
6011      
6012      Example:
6013      (start code js)
6014      NodeHelper.circle.render('fill', { x: 10, y: 30 }, 30, viz.canvas);
6015      (end code)
6016      */
6017     'render': function(type, pos, radius, canvas){
6018       var ctx = canvas.getCtx();
6019       ctx.beginPath();
6020       ctx.arc(pos.x, pos.y, radius, 0, Math.PI * 2, true);
6021       ctx.closePath();
6022       ctx[type]();
6023     },
6024     /*
6025     Method: contains
6026     
6027     Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.
6028     
6029     Parameters:
6030     
6031     npos - (object) An *x*, *y* object with the <Graph.Node> position.
6032     pos - (object) An *x*, *y* object with the position to check.
6033     radius - (number) The radius of the rendered circle.
6034     
6035     Example:
6036     (start code js)
6037     NodeHelper.circle.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, 30); //true
6038     (end code)
6039     */
6040     'contains': function(npos, pos, radius){
6041       var diffx = npos.x - pos.x, 
6042           diffy = npos.y - pos.y, 
6043           diff = diffx * diffx + diffy * diffy;
6044       return diff <= radius * radius;
6045     }
6046   },
6047   /*
6048   Object: NodeHelper.ellipse
6049   */
6050   'ellipse': {
6051     /*
6052     Method: render
6053     
6054     Renders an ellipse into the canvas.
6055     
6056     Parameters:
6057     
6058     type - (string) Possible options are 'fill' or 'stroke'.
6059     pos - (object) An *x*, *y* object with the position of the center of the ellipse.
6060     width - (number) The width of the ellipse.
6061     height - (number) The height of the ellipse.
6062     canvas - (object) A <Canvas> instance.
6063     
6064     Example:
6065     (start code js)
6066     NodeHelper.ellipse.render('fill', { x: 10, y: 30 }, 30, 40, viz.canvas);
6067     (end code)
6068     */
6069     'render': function(type, pos, width, height, canvas){
6070       var ctx = canvas.getCtx(),
6071           scalex = 1,
6072           scaley = 1,
6073           scaleposx = 1,
6074           scaleposy = 1,
6075           radius = 0;
6076
6077       if (width > height) {
6078           radius = width / 2;
6079           scaley = height / width;
6080           scaleposy = width / height;
6081       } else {
6082           radius = height / 2;
6083           scalex = width / height;
6084           scaleposx = height / width;
6085       }
6086
6087       ctx.save();
6088       ctx.scale(scalex, scaley);
6089       ctx.beginPath();
6090       ctx.arc(pos.x * scaleposx, pos.y * scaleposy, radius, 0, Math.PI * 2, true);
6091       ctx.closePath();
6092       ctx[type]();
6093       ctx.restore();
6094     },
6095     /*
6096     Method: contains
6097     
6098     Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.
6099     
6100     Parameters:
6101     
6102     npos - (object) An *x*, *y* object with the <Graph.Node> position.
6103     pos - (object) An *x*, *y* object with the position to check.
6104     width - (number) The width of the rendered ellipse.
6105     height - (number) The height of the rendered ellipse.
6106     
6107     Example:
6108     (start code js)
6109     NodeHelper.ellipse.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, 30, 40);
6110     (end code)
6111     */
6112     'contains': function(npos, pos, width, height){
6113       var radius = 0,
6114           scalex = 1,
6115           scaley = 1,
6116           diffx = 0,
6117           diffy = 0,
6118           diff = 0;
6119
6120       if (width > height) {
6121               radius = width / 2;
6122               scaley = height / width;
6123       } else {
6124           radius = height / 2;
6125           scalex = width / height;
6126       }
6127
6128       diffx = (npos.x - pos.x) * (1 / scalex);
6129       diffy = (npos.y - pos.y) * (1 / scaley);
6130       diff = diffx * diffx + diffy * diffy;
6131       return diff <= radius * radius;
6132     }
6133   },
6134   /*
6135   Object: NodeHelper.square
6136   */
6137   'square': {
6138     /*
6139     Method: render
6140     
6141     Renders a square into the canvas.
6142     
6143     Parameters:
6144     
6145     type - (string) Possible options are 'fill' or 'stroke'.
6146     pos - (object) An *x*, *y* object with the position of the center of the square.
6147     dim - (number) The radius (or half-diameter) of the square.
6148     canvas - (object) A <Canvas> instance.
6149     
6150     Example:
6151     (start code js)
6152     NodeHelper.square.render('stroke', { x: 10, y: 30 }, 40, viz.canvas);
6153     (end code)
6154     */
6155     'render': function(type, pos, dim, canvas){
6156       canvas.getCtx()[type + "Rect"](pos.x - dim, pos.y - dim, 2*dim, 2*dim);
6157     },
6158     /*
6159     Method: contains
6160     
6161     Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.
6162     
6163     Parameters:
6164     
6165     npos - (object) An *x*, *y* object with the <Graph.Node> position.
6166     pos - (object) An *x*, *y* object with the position to check.
6167     dim - (number) The radius (or half-diameter) of the square.
6168     
6169     Example:
6170     (start code js)
6171     NodeHelper.square.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, 30);
6172     (end code)
6173     */
6174     'contains': function(npos, pos, dim){
6175       return Math.abs(pos.x - npos.x) <= dim && Math.abs(pos.y - npos.y) <= dim;
6176     }
6177   },
6178   /*
6179   Object: NodeHelper.rectangle
6180   */
6181   'rectangle': {
6182     /*
6183     Method: render
6184     
6185     Renders a rectangle into the canvas.
6186     
6187     Parameters:
6188     
6189     type - (string) Possible options are 'fill' or 'stroke'.
6190     pos - (object) An *x*, *y* object with the position of the center of the rectangle.
6191     width - (number) The width of the rectangle.
6192     height - (number) The height of the rectangle.
6193     canvas - (object) A <Canvas> instance.
6194     
6195     Example:
6196     (start code js)
6197     NodeHelper.rectangle.render('fill', { x: 10, y: 30 }, 30, 40, viz.canvas);
6198     (end code)
6199     */
6200     'render': function(type, pos, width, height, canvas){
6201       canvas.getCtx()[type + "Rect"](pos.x - width / 2, pos.y - height / 2, 
6202                                       width, height);
6203     },
6204     /*
6205     Method: contains
6206     
6207     Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.
6208     
6209     Parameters:
6210     
6211     npos - (object) An *x*, *y* object with the <Graph.Node> position.
6212     pos - (object) An *x*, *y* object with the position to check.
6213     width - (number) The width of the rendered rectangle.
6214     height - (number) The height of the rendered rectangle.
6215     
6216     Example:
6217     (start code js)
6218     NodeHelper.rectangle.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, 30, 40);
6219     (end code)
6220     */
6221     'contains': function(npos, pos, width, height){
6222       return Math.abs(pos.x - npos.x) <= width / 2
6223           && Math.abs(pos.y - npos.y) <= height / 2;
6224     }
6225   },
6226   /*
6227   Object: NodeHelper.triangle
6228   */
6229   'triangle': {
6230     /*
6231     Method: render
6232     
6233     Renders a triangle into the canvas.
6234     
6235     Parameters:
6236     
6237     type - (string) Possible options are 'fill' or 'stroke'.
6238     pos - (object) An *x*, *y* object with the position of the center of the triangle.
6239     dim - (number) Half the base and half the height of the triangle.
6240     canvas - (object) A <Canvas> instance.
6241     
6242     Example:
6243     (start code js)
6244     NodeHelper.triangle.render('stroke', { x: 10, y: 30 }, 40, viz.canvas);
6245     (end code)
6246     */
6247     'render': function(type, pos, dim, canvas){
6248       var ctx = canvas.getCtx(), 
6249           c1x = pos.x, 
6250           c1y = pos.y - dim, 
6251           c2x = c1x - dim, 
6252           c2y = pos.y + dim, 
6253           c3x = c1x + dim, 
6254           c3y = c2y;
6255       ctx.beginPath();
6256       ctx.moveTo(c1x, c1y);
6257       ctx.lineTo(c2x, c2y);
6258       ctx.lineTo(c3x, c3y);
6259       ctx.closePath();
6260       ctx[type]();
6261     },
6262     /*
6263     Method: contains
6264     
6265     Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.
6266     
6267     Parameters:
6268     
6269     npos - (object) An *x*, *y* object with the <Graph.Node> position.
6270     pos - (object) An *x*, *y* object with the position to check.
6271     dim - (number) Half the base and half the height of the triangle.
6272     
6273     Example:
6274     (start code js)
6275     NodeHelper.triangle.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, 30);
6276     (end code)
6277     */
6278     'contains': function(npos, pos, dim) {
6279       return NodeHelper.circle.contains(npos, pos, dim);
6280     }
6281   },
6282   /*
6283   Object: NodeHelper.star
6284   */
6285   'star': {
6286     /*
6287     Method: render
6288     
6289     Renders a star (concave decagon) into the canvas.
6290     
6291     Parameters:
6292     
6293     type - (string) Possible options are 'fill' or 'stroke'.
6294     pos - (object) An *x*, *y* object with the position of the center of the star.
6295     dim - (number) The length of a side of a concave decagon.
6296     canvas - (object) A <Canvas> instance.
6297     
6298     Example:
6299     (start code js)
6300     NodeHelper.star.render('stroke', { x: 10, y: 30 }, 40, viz.canvas);
6301     (end code)
6302     */
6303     'render': function(type, pos, dim, canvas){
6304       var ctx = canvas.getCtx(), 
6305           pi5 = Math.PI / 5;
6306       ctx.save();
6307       ctx.translate(pos.x, pos.y);
6308       ctx.beginPath();
6309       ctx.moveTo(dim, 0);
6310       for (var i = 0; i < 9; i++) {
6311         ctx.rotate(pi5);
6312         if (i % 2 == 0) {
6313           ctx.lineTo((dim / 0.525731) * 0.200811, 0);
6314         } else {
6315           ctx.lineTo(dim, 0);
6316         }
6317       }
6318       ctx.closePath();
6319       ctx[type]();
6320       ctx.restore();
6321     },
6322     /*
6323     Method: contains
6324     
6325     Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.
6326     
6327     Parameters:
6328     
6329     npos - (object) An *x*, *y* object with the <Graph.Node> position.
6330     pos - (object) An *x*, *y* object with the position to check.
6331     dim - (number) The length of a side of a concave decagon.
6332     
6333     Example:
6334     (start code js)
6335     NodeHelper.star.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, 30);
6336     (end code)
6337     */
6338     'contains': function(npos, pos, dim) {
6339       return NodeHelper.circle.contains(npos, pos, dim);
6340     }
6341   }
6342 };
6343
6344 /*
6345   Object: EdgeHelper
6346   
6347   Contains rendering primitives for simple edge shapes.
6348 */
6349 var EdgeHelper = {
6350   /*
6351     Object: EdgeHelper.line
6352   */
6353   'line': {
6354       /*
6355       Method: render
6356       
6357       Renders a line into the canvas.
6358       
6359       Parameters:
6360       
6361       from - (object) An *x*, *y* object with the starting position of the line.
6362       to - (object) An *x*, *y* object with the ending position of the line.
6363       canvas - (object) A <Canvas> instance.
6364       
6365       Example:
6366       (start code js)
6367       EdgeHelper.line.render({ x: 10, y: 30 }, { x: 10, y: 50 }, viz.canvas);
6368       (end code)
6369       */
6370       'render': function(from, to, canvas){
6371         var ctx = canvas.getCtx();
6372         ctx.beginPath();
6373         ctx.moveTo(from.x, from.y);
6374         ctx.lineTo(to.x, to.y);
6375         ctx.stroke();
6376       },
6377       /*
6378       Method: contains
6379       
6380       Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.
6381       
6382       Parameters:
6383       
6384       posFrom - (object) An *x*, *y* object with a <Graph.Node> position.
6385       posTo - (object) An *x*, *y* object with a <Graph.Node> position.
6386       pos - (object) An *x*, *y* object with the position to check.
6387       epsilon - (number) The dimension of the shape.
6388       
6389       Example:
6390       (start code js)
6391       EdgeHelper.line.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, { x: 15, y: 35 }, 30);
6392       (end code)
6393       */
6394       'contains': function(posFrom, posTo, pos, epsilon) {
6395         var min = Math.min, 
6396             max = Math.max,
6397             minPosX = min(posFrom.x, posTo.x),
6398             maxPosX = max(posFrom.x, posTo.x),
6399             minPosY = min(posFrom.y, posTo.y),
6400             maxPosY = max(posFrom.y, posTo.y);
6401         
6402         if(pos.x >= minPosX && pos.x <= maxPosX 
6403             && pos.y >= minPosY && pos.y <= maxPosY) {
6404           if(Math.abs(posTo.x - posFrom.x) <= epsilon) {
6405             return true;
6406           }
6407           var dist = (posTo.y - posFrom.y) / (posTo.x - posFrom.x) * (pos.x - posFrom.x) + posFrom.y;
6408           return Math.abs(dist - pos.y) <= epsilon;
6409         }
6410         return false;
6411       }
6412     },
6413   /*
6414     Object: EdgeHelper.arrow
6415   */
6416   'arrow': {
6417       /*
6418       Method: render
6419       
6420       Renders an arrow into the canvas.
6421       
6422       Parameters:
6423       
6424       from - (object) An *x*, *y* object with the starting position of the arrow.
6425       to - (object) An *x*, *y* object with the ending position of the arrow.
6426       dim - (number) The dimension of the arrow.
6427       swap - (boolean) Whether to set the arrow pointing to the starting position or the ending position.
6428       canvas - (object) A <Canvas> instance.
6429       
6430       Example:
6431       (start code js)
6432       EdgeHelper.arrow.render({ x: 10, y: 30 }, { x: 10, y: 50 }, 13, false, viz.canvas);
6433       (end code)
6434       */
6435     'render': function(from, to, dim, swap, canvas){
6436         var ctx = canvas.getCtx();
6437         // invert edge direction
6438         if (swap) {
6439           var tmp = from;
6440           from = to;
6441           to = tmp;
6442         }
6443         var vect = new Complex(to.x - from.x, to.y - from.y);
6444         vect.$scale(dim / vect.norm());
6445         var intermediatePoint = new Complex(to.x - vect.x, to.y - vect.y),
6446             normal = new Complex(-vect.y / 2, vect.x / 2),
6447             v1 = intermediatePoint.add(normal), 
6448             v2 = intermediatePoint.$add(normal.$scale(-1));
6449         
6450         ctx.beginPath();
6451         ctx.moveTo(from.x, from.y);
6452         ctx.lineTo(to.x, to.y);
6453         ctx.stroke();
6454         ctx.beginPath();
6455         ctx.moveTo(v1.x, v1.y);
6456         ctx.lineTo(v2.x, v2.y);
6457         ctx.lineTo(to.x, to.y);
6458         ctx.closePath();
6459         ctx.fill();
6460     },
6461     /*
6462     Method: contains
6463     
6464     Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.
6465     
6466     Parameters:
6467     
6468     posFrom - (object) An *x*, *y* object with a <Graph.Node> position.
6469     posTo - (object) An *x*, *y* object with a <Graph.Node> position.
6470     pos - (object) An *x*, *y* object with the position to check.
6471     epsilon - (number) The dimension of the shape.
6472     
6473     Example:
6474     (start code js)
6475     EdgeHelper.arrow.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, { x: 15, y: 35 }, 30);
6476     (end code)
6477     */
6478     'contains': function(posFrom, posTo, pos, epsilon) {
6479       return EdgeHelper.line.contains(posFrom, posTo, pos, epsilon);
6480     }
6481   },
6482   /*
6483     Object: EdgeHelper.hyperline
6484   */
6485   'hyperline': {
6486     /*
6487     Method: render
6488     
6489     Renders a hyperline into the canvas. A hyperline are the lines drawn for the <Hypertree> visualization.
6490     
6491     Parameters:
6492     
6493     from - (object) An *x*, *y* object with the starting position of the hyperline. *x* and *y* must belong to [0, 1).
6494     to - (object) An *x*, *y* object with the ending position of the hyperline. *x* and *y* must belong to [0, 1).
6495     r - (number) The scaling factor.
6496     canvas - (object) A <Canvas> instance.
6497     
6498     Example:
6499     (start code js)
6500     EdgeHelper.hyperline.render({ x: 10, y: 30 }, { x: 10, y: 50 }, 100, viz.canvas);
6501     (end code)
6502     */
6503     'render': function(from, to, r, canvas){
6504       var ctx = canvas.getCtx();  
6505       var centerOfCircle = computeArcThroughTwoPoints(from, to);
6506       if (centerOfCircle.a > 1000 || centerOfCircle.b > 1000
6507           || centerOfCircle.ratio < 0) {
6508         ctx.beginPath();
6509         ctx.moveTo(from.x * r, from.y * r);
6510         ctx.lineTo(to.x * r, to.y * r);
6511         ctx.stroke();
6512       } else {
6513         var angleBegin = Math.atan2(to.y - centerOfCircle.y, to.x
6514             - centerOfCircle.x);
6515         var angleEnd = Math.atan2(from.y - centerOfCircle.y, from.x
6516             - centerOfCircle.x);
6517         var sense = sense(angleBegin, angleEnd);
6518         ctx.beginPath();
6519         ctx.arc(centerOfCircle.x * r, centerOfCircle.y * r, centerOfCircle.ratio
6520             * r, angleBegin, angleEnd, sense);
6521         ctx.stroke();
6522       }
6523       /*      
6524         Calculates the arc parameters through two points.
6525         
6526         More information in <http://en.wikipedia.org/wiki/Poincar%C3%A9_disc_model#Analytic_geometry_constructions_in_the_hyperbolic_plane> 
6527       
6528         Parameters:
6529       
6530         p1 - A <Complex> instance.
6531         p2 - A <Complex> instance.
6532         scale - The Disk's diameter.
6533       
6534         Returns:
6535       
6536         An object containing some arc properties.
6537       */
6538       function computeArcThroughTwoPoints(p1, p2){
6539         var aDen = (p1.x * p2.y - p1.y * p2.x), bDen = aDen;
6540         var sq1 = p1.squaredNorm(), sq2 = p2.squaredNorm();
6541         // Fall back to a straight line
6542         if (aDen == 0)
6543           return {
6544             x: 0,
6545             y: 0,
6546             ratio: -1
6547           };
6548     
6549         var a = (p1.y * sq2 - p2.y * sq1 + p1.y - p2.y) / aDen;
6550         var b = (p2.x * sq1 - p1.x * sq2 + p2.x - p1.x) / bDen;
6551         var x = -a / 2;
6552         var y = -b / 2;
6553         var squaredRatio = (a * a + b * b) / 4 - 1;
6554         // Fall back to a straight line
6555         if (squaredRatio < 0)
6556           return {
6557             x: 0,
6558             y: 0,
6559             ratio: -1
6560           };
6561         var ratio = Math.sqrt(squaredRatio);
6562         var out = {
6563           x: x,
6564           y: y,
6565           ratio: ratio > 1000? -1 : ratio,
6566           a: a,
6567           b: b
6568         };
6569     
6570         return out;
6571       }
6572       /*      
6573         Sets angle direction to clockwise (true) or counterclockwise (false). 
6574          
6575         Parameters: 
6576       
6577            angleBegin - Starting angle for drawing the arc. 
6578            angleEnd - The HyperLine will be drawn from angleBegin to angleEnd. 
6579       
6580         Returns: 
6581       
6582            A Boolean instance describing the sense for drawing the HyperLine. 
6583       */
6584       function sense(angleBegin, angleEnd){
6585         return (angleBegin < angleEnd)? ((angleBegin + Math.PI > angleEnd)? false
6586             : true) : ((angleEnd + Math.PI > angleBegin)? true : false);
6587       }
6588     },
6589     /*
6590     Method: contains
6591     
6592     Not Implemented
6593     
6594     Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.
6595     
6596     Parameters:
6597     
6598     posFrom - (object) An *x*, *y* object with a <Graph.Node> position.
6599     posTo - (object) An *x*, *y* object with a <Graph.Node> position.
6600     pos - (object) An *x*, *y* object with the position to check.
6601     epsilon - (number) The dimension of the shape.
6602     
6603     Example:
6604     (start code js)
6605     EdgeHelper.hyperline.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, { x: 15, y: 35 }, 30);
6606     (end code)
6607     */
6608     'contains': $.lambda(false)
6609   }
6610 };
6611
6612
6613 /*
6614  * File: Graph.Plot.js
6615  */
6616
6617 /*
6618    Object: Graph.Plot
6619
6620    <Graph> rendering and animation methods.
6621    
6622    Properties:
6623    
6624    nodeHelper - <NodeHelper> object.
6625    edgeHelper - <EdgeHelper> object.
6626 */
6627 Graph.Plot = {
6628     //Default initializer
6629     initialize: function(viz, klass){
6630       this.viz = viz;
6631       this.config = viz.config;
6632       this.node = viz.config.Node;
6633       this.edge = viz.config.Edge;
6634       this.animation = new Animation;
6635       this.nodeTypes = new klass.Plot.NodeTypes;
6636       this.edgeTypes = new klass.Plot.EdgeTypes;
6637       this.labels = viz.labels;
6638    },
6639
6640     //Add helpers
6641     nodeHelper: NodeHelper,
6642     edgeHelper: EdgeHelper,
6643     
6644     Interpolator: {
6645         //node/edge property parsers
6646         'map': {
6647           'border': 'color',
6648           'color': 'color',
6649           'width': 'number',
6650           'height': 'number',
6651           'dim': 'number',
6652           'alpha': 'number',
6653           'lineWidth': 'number',
6654           'angularWidth':'number',
6655           'span':'number',
6656           'valueArray':'array-number',
6657           'dimArray':'array-number'
6658           //'colorArray':'array-color'
6659         },
6660         
6661         //canvas specific parsers
6662         'canvas': {
6663           'globalAlpha': 'number',
6664           'fillStyle': 'color',
6665           'strokeStyle': 'color',
6666           'lineWidth': 'number',
6667           'shadowBlur': 'number',
6668           'shadowColor': 'color',
6669           'shadowOffsetX': 'number',
6670           'shadowOffsetY': 'number',
6671           'miterLimit': 'number'
6672         },
6673   
6674         //label parsers
6675         'label': {
6676           'size': 'number',
6677           'color': 'color'
6678         },
6679   
6680         //Number interpolator
6681         'compute': function(from, to, delta) {
6682           return from + (to - from) * delta;
6683         },
6684         
6685         //Position interpolators
6686         'moebius': function(elem, props, delta, vector) {
6687           var v = vector.scale(-delta);  
6688           if(v.norm() < 1) {
6689               var x = v.x, y = v.y;
6690               var ans = elem.startPos
6691                 .getc().moebiusTransformation(v);
6692               elem.pos.setc(ans.x, ans.y);
6693               v.x = x; v.y = y;
6694             }           
6695         },
6696
6697         'linear': function(elem, props, delta) {
6698             var from = elem.startPos.getc(true);
6699             var to = elem.endPos.getc(true);
6700             elem.pos.setc(this.compute(from.x, to.x, delta), 
6701                           this.compute(from.y, to.y, delta));
6702         },
6703
6704         'polar': function(elem, props, delta) {
6705           var from = elem.startPos.getp(true);
6706           var to = elem.endPos.getp();
6707           var ans = to.interpolate(from, delta);
6708           elem.pos.setp(ans.theta, ans.rho);
6709         },
6710         
6711         //Graph's Node/Edge interpolators
6712         'number': function(elem, prop, delta, getter, setter) {
6713           var from = elem[getter](prop, 'start');
6714           var to = elem[getter](prop, 'end');
6715           elem[setter](prop, this.compute(from, to, delta));
6716         },
6717
6718         'color': function(elem, prop, delta, getter, setter) {
6719           var from = $.hexToRgb(elem[getter](prop, 'start'));
6720           var to = $.hexToRgb(elem[getter](prop, 'end'));
6721           var comp = this.compute;
6722           var val = $.rgbToHex([parseInt(comp(from[0], to[0], delta)),
6723                                 parseInt(comp(from[1], to[1], delta)),
6724                                 parseInt(comp(from[2], to[2], delta))]);
6725           
6726           elem[setter](prop, val);
6727         },
6728         
6729         'array-number': function(elem, prop, delta, getter, setter) {
6730           var from = elem[getter](prop, 'start'),
6731               to = elem[getter](prop, 'end'),
6732               cur = [];
6733           for(var i=0, l=from.length; i<l; i++) {
6734             var fromi = from[i], toi = to[i];
6735             if(fromi.length) {
6736               for(var j=0, len=fromi.length, curi=[]; j<len; j++) {
6737                 curi.push(this.compute(fromi[j], toi[j], delta));
6738               }
6739               cur.push(curi);
6740             } else {
6741               cur.push(this.compute(fromi, toi, delta));
6742             }
6743           }
6744           elem[setter](prop, cur);
6745         },
6746         
6747         'node': function(elem, props, delta, map, getter, setter) {
6748           map = this[map];
6749           if(props) {
6750             var len = props.length;
6751             for(var i=0; i<len; i++) {
6752               var pi = props[i];
6753               this[map[pi]](elem, pi, delta, getter, setter);
6754             }
6755           } else {
6756             for(var pi in map) {
6757               this[map[pi]](elem, pi, delta, getter, setter);
6758             }
6759           }
6760         },
6761         
6762         'edge': function(elem, props, delta, mapKey, getter, setter) {
6763             var adjs = elem.adjacencies;
6764             for(var id in adjs) this['node'](adjs[id], props, delta, mapKey, getter, setter);
6765         },
6766         
6767         'node-property': function(elem, props, delta) {
6768           this['node'](elem, props, delta, 'map', 'getData', 'setData');
6769         },
6770         
6771         'edge-property': function(elem, props, delta) {
6772           this['edge'](elem, props, delta, 'map', 'getData', 'setData');  
6773         },
6774
6775         'label-property': function(elem, props, delta) {
6776           this['node'](elem, props, delta, 'label', 'getLabelData', 'setLabelData');
6777         },
6778         
6779         'node-style': function(elem, props, delta) {
6780           this['node'](elem, props, delta, 'canvas', 'getCanvasStyle', 'setCanvasStyle');
6781         },
6782         
6783         'edge-style': function(elem, props, delta) {
6784           this['edge'](elem, props, delta, 'canvas', 'getCanvasStyle', 'setCanvasStyle');  
6785         }
6786     },
6787     
6788   
6789     /*
6790        sequence
6791     
6792        Iteratively performs an action while refreshing the state of the visualization.
6793
6794        Parameters:
6795
6796        options - (object) An object containing some sequence options described below
6797        condition - (function) A function returning a boolean instance in order to stop iterations.
6798        step - (function) A function to execute on each step of the iteration.
6799        onComplete - (function) A function to execute when the sequence finishes.
6800        duration - (number) Duration (in milliseconds) of each step.
6801
6802       Example:
6803        (start code js)
6804         var rg = new $jit.RGraph(options);
6805         var i = 0;
6806         rg.fx.sequence({
6807           condition: function() {
6808            return i == 10;
6809           },
6810           step: function() {
6811             alert(i++);
6812           },
6813           onComplete: function() {
6814            alert('done!');
6815           }
6816         });
6817        (end code)
6818
6819     */
6820     sequence: function(options) {
6821         var that = this;
6822         options = $.merge({
6823           condition: $.lambda(false),
6824           step: $.empty,
6825           onComplete: $.empty,
6826           duration: 200
6827         }, options || {});
6828
6829         var interval = setInterval(function() {
6830           if(options.condition()) {
6831             options.step();
6832           } else {
6833             clearInterval(interval);
6834             options.onComplete();
6835           }
6836           that.viz.refresh(true);
6837         }, options.duration);
6838     },
6839     
6840     /*
6841       prepare
6842  
6843       Prepare graph position and other attribute values before performing an Animation. 
6844       This method is used internally by the Toolkit.
6845       
6846       See also:
6847        
6848        <Animation>, <Graph.Plot.animate>
6849
6850     */
6851     prepare: function(modes) {
6852       var graph = this.viz.graph,
6853           accessors = {
6854             'node-property': {
6855               'getter': 'getData',
6856               'setter': 'setData'
6857             },
6858             'edge-property': {
6859               'getter': 'getData',
6860               'setter': 'setData'
6861             },
6862             'node-style': {
6863               'getter': 'getCanvasStyle',
6864               'setter': 'setCanvasStyle'
6865             },
6866             'edge-style': {
6867               'getter': 'getCanvasStyle',
6868               'setter': 'setCanvasStyle'
6869             }
6870           };
6871
6872       //parse modes
6873       var m = {};
6874       if($.type(modes) == 'array') {
6875         for(var i=0, len=modes.length; i < len; i++) {
6876           var elems = modes[i].split(':');
6877           m[elems.shift()] = elems;
6878         }
6879       } else {
6880         for(var p in modes) {
6881           if(p == 'position') {
6882             m[modes.position] = [];
6883           } else {
6884             m[p] = $.splat(modes[p]);
6885           }
6886         }
6887       }
6888       
6889       graph.eachNode(function(node) { 
6890         node.startPos.set(node.pos);
6891         $.each(['node-property', 'node-style'], function(p) {
6892           if(p in m) {
6893             var prop = m[p];
6894             for(var i=0, l=prop.length; i < l; i++) {
6895               node[accessors[p].setter](prop[i], node[accessors[p].getter](prop[i]), 'start');
6896             }
6897           }
6898         });
6899         $.each(['edge-property', 'edge-style'], function(p) {
6900           if(p in m) {
6901             var prop = m[p];
6902             node.eachAdjacency(function(adj) {
6903               for(var i=0, l=prop.length; i < l; i++) {
6904                 adj[accessors[p].setter](prop[i], adj[accessors[p].getter](prop[i]), 'start');
6905               }
6906             });
6907           }
6908         });
6909       });
6910       return m;
6911     },
6912     
6913     /*
6914        Method: animate
6915     
6916        Animates a <Graph> by interpolating some <Graph.Node>, <Graph.Adjacence> or <Graph.Label> properties.
6917
6918        Parameters:
6919
6920        opt - (object) Animation options. The object properties are described below
6921        duration - (optional) Described in <Options.Fx>.
6922        fps - (optional) Described in <Options.Fx>.
6923        hideLabels - (optional|boolean) Whether to hide labels during the animation.
6924        modes - (required|object) An object with animation modes (described below).
6925
6926        Animation modes:
6927        
6928        Animation modes are strings representing different node/edge and graph properties that you'd like to animate. 
6929        They are represented by an object that has as keys main categories of properties to animate and as values a list 
6930        of these specific properties. The properties are described below
6931        
6932        position - Describes the way nodes' positions must be interpolated. Possible values are 'linear', 'polar' or 'moebius'.
6933        node-property - Describes which Node properties will be interpolated. These properties can be any of the ones defined in <Options.Node>.
6934        edge-property - Describes which Edge properties will be interpolated. These properties can be any the ones defined in <Options.Edge>.
6935        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.
6936        node-style - Describes which Node Canvas Styles will be interpolated. These are specific canvas properties like fillStyle, strokeStyle, lineWidth, shadowBlur, shadowColor, shadowOffsetX, shadowOffsetY, etc.
6937        edge-style - Describes which Edge Canvas Styles will be interpolated. These are specific canvas properties like fillStyle, strokeStyle, lineWidth, shadowBlur, shadowColor, shadowOffsetX, shadowOffsetY, etc.
6938
6939        Example:
6940        (start code js)
6941        var viz = new $jit.Viz(options);
6942        //...tweak some Data, CanvasStyles or LabelData properties...
6943        viz.fx.animate({
6944          modes: {
6945            'position': 'linear',
6946            'node-property': ['width', 'height'],
6947            'node-style': 'shadowColor',
6948            'label-property': 'size'
6949          },
6950          hideLabels: false
6951        });
6952        //...can also be written like this...
6953        viz.fx.animate({
6954          modes: ['linear',
6955                  'node-property:width:height',
6956                  'node-style:shadowColor',
6957                  'label-property:size'],
6958          hideLabels: false
6959        });
6960        (end code)
6961     */
6962     animate: function(opt, versor) {
6963       opt = $.merge(this.viz.config, opt || {});
6964       var that = this,
6965           viz = this.viz,
6966           graph  = viz.graph,
6967           interp = this.Interpolator,
6968           animation =  opt.type === 'nodefx'? this.nodeFxAnimation : this.animation;
6969       //prepare graph values
6970       var m = this.prepare(opt.modes);
6971       
6972       //animate
6973       if(opt.hideLabels) this.labels.hideLabels(true);
6974       animation.setOptions($.extend(opt, {
6975         $animating: false,
6976         compute: function(delta) {
6977           graph.eachNode(function(node) { 
6978             for(var p in m) {
6979               interp[p](node, m[p], delta, versor);
6980             }
6981           });
6982           that.plot(opt, this.$animating, delta);
6983           this.$animating = true;
6984         },
6985         complete: function() {
6986           if(opt.hideLabels) that.labels.hideLabels(false);
6987           that.plot(opt);
6988           opt.onComplete();
6989           //TODO(nico): This shouldn't be here!
6990           //opt.onAfterCompute();
6991         }       
6992       })).start();
6993     },
6994     
6995     /*
6996       nodeFx
6997    
6998       Apply animation to node properties like color, width, height, dim, etc.
6999   
7000       Parameters:
7001   
7002       options - Animation options. This object properties is described below
7003       elements - The Elements to be transformed. This is an object that has a properties
7004       
7005       (start code js)
7006       'elements': {
7007         //can also be an array of ids
7008         'id': 'id-of-node-to-transform',
7009         //properties to be modified. All properties are optional.
7010         'properties': {
7011           'color': '#ccc', //some color
7012           'width': 10, //some width
7013           'height': 10, //some height
7014           'dim': 20, //some dim
7015           'lineWidth': 10 //some line width
7016         } 
7017       }
7018       (end code)
7019       
7020       - _reposition_ Whether to recalculate positions and add a motion animation. 
7021       This might be used when changing _width_ or _height_ properties in a <Layouts.Tree> like layout. Default's *false*.
7022       
7023       - _onComplete_ A method that is called when the animation completes.
7024       
7025       ...and all other <Graph.Plot.animate> options like _duration_, _fps_, _transition_, etc.
7026   
7027       Example:
7028       (start code js)
7029        var rg = new RGraph(canvas, config); //can be also Hypertree or ST
7030        rg.fx.nodeFx({
7031          'elements': {
7032            'id':'mynodeid',
7033            'properties': {
7034              'color':'#ccf'
7035            },
7036            'transition': Trans.Quart.easeOut
7037          }
7038        });
7039       (end code)    
7040    */
7041    nodeFx: function(opt) {
7042      var viz = this.viz,
7043          graph  = viz.graph,
7044          animation = this.nodeFxAnimation,
7045          options = $.merge(this.viz.config, {
7046            'elements': {
7047              'id': false,
7048              'properties': {}
7049            },
7050            'reposition': false
7051          });
7052      opt = $.merge(options, opt || {}, {
7053        onBeforeCompute: $.empty,
7054        onAfterCompute: $.empty
7055      });
7056      //check if an animation is running
7057      animation.stopTimer();
7058      var props = opt.elements.properties;
7059      //set end values for nodes
7060      if(!opt.elements.id) {
7061        graph.eachNode(function(n) {
7062          for(var prop in props) {
7063            n.setData(prop, props[prop], 'end');
7064          }
7065        });
7066      } else {
7067        var ids = $.splat(opt.elements.id);
7068        $.each(ids, function(id) {
7069          var n = graph.getNode(id);
7070          if(n) {
7071            for(var prop in props) {
7072              n.setData(prop, props[prop], 'end');
7073            }
7074          }
7075        });
7076      }
7077      //get keys
7078      var propnames = [];
7079      for(var prop in props) propnames.push(prop);
7080      //add node properties modes
7081      var modes = ['node-property:' + propnames.join(':')];
7082      //set new node positions
7083      if(opt.reposition) {
7084        modes.push('linear');
7085        viz.compute('end');
7086      }
7087      //animate
7088      this.animate($.merge(opt, {
7089        modes: modes,
7090        type: 'nodefx'
7091      }));
7092    },
7093
7094     
7095     /*
7096        Method: plot
7097     
7098        Plots a <Graph>.
7099
7100        Parameters:
7101
7102        opt - (optional) Plotting options. Most of them are described in <Options.Fx>.
7103
7104        Example:
7105
7106        (start code js)
7107        var viz = new $jit.Viz(options);
7108        viz.fx.plot(); 
7109        (end code)
7110
7111     */
7112    plot: function(opt, animating) {
7113      var viz = this.viz, 
7114          aGraph = viz.graph, 
7115          canvas = viz.canvas, 
7116          id = viz.root, 
7117          that = this, 
7118          ctx = canvas.getCtx(), 
7119          min = Math.min,
7120          opt = opt || this.viz.controller;
7121      
7122      opt.clearCanvas && canvas.clear();
7123        
7124      var root = aGraph.getNode(id);
7125      if(!root) return;
7126      
7127      var T = !!root.visited;
7128      aGraph.eachNode(function(node) {
7129        var nodeAlpha = node.getData('alpha');
7130        node.eachAdjacency(function(adj) {
7131          var nodeTo = adj.nodeTo;
7132          if(!!nodeTo.visited === T && node.drawn && nodeTo.drawn) {
7133            !animating && opt.onBeforePlotLine(adj);
7134            that.plotLine(adj, canvas, animating);
7135            !animating && opt.onAfterPlotLine(adj);
7136          }
7137        });
7138        if(node.drawn) {
7139          !animating && opt.onBeforePlotNode(node);
7140          that.plotNode(node, canvas, animating);
7141          !animating && opt.onAfterPlotNode(node);
7142        }
7143        if(!that.labelsHidden && opt.withLabels) {
7144          if(node.drawn && nodeAlpha >= 0.95) {
7145            that.labels.plotLabel(canvas, node, opt);
7146          } else {
7147            that.labels.hideLabel(node, false);
7148          }
7149        }
7150        node.visited = !T;
7151      });
7152     },
7153
7154   /*
7155       Plots a Subtree.
7156    */
7157    plotTree: function(node, opt, animating) {
7158        var that = this, 
7159        viz = this.viz, 
7160        canvas = viz.canvas,
7161        config = this.config,
7162        ctx = canvas.getCtx();
7163        var nodeAlpha = node.getData('alpha');
7164        node.eachSubnode(function(elem) {
7165          if(opt.plotSubtree(node, elem) && elem.exist && elem.drawn) {
7166              var adj = node.getAdjacency(elem.id);
7167              !animating && opt.onBeforePlotLine(adj);
7168              that.plotLine(adj, canvas, animating);
7169              !animating && opt.onAfterPlotLine(adj);
7170              that.plotTree(elem, opt, animating);
7171          }
7172        });
7173        if(node.drawn) {
7174            !animating && opt.onBeforePlotNode(node);
7175            this.plotNode(node, canvas, animating);
7176            !animating && opt.onAfterPlotNode(node);
7177            if(!opt.hideLabels && opt.withLabels && nodeAlpha >= 0.95) 
7178                this.labels.plotLabel(canvas, node, opt);
7179            else 
7180                this.labels.hideLabel(node, false);
7181        } else {
7182            this.labels.hideLabel(node, true);
7183        }
7184    },
7185
7186   /*
7187        Method: plotNode
7188     
7189        Plots a <Graph.Node>.
7190
7191        Parameters:
7192        
7193        node - (object) A <Graph.Node>.
7194        canvas - (object) A <Canvas> element.
7195
7196     */
7197     plotNode: function(node, canvas, animating) {
7198         var f = node.getData('type'), 
7199             ctxObj = this.node.CanvasStyles;
7200         if(f != 'none') {
7201           var width = node.getData('lineWidth'),
7202               color = node.getData('color'),
7203               alpha = node.getData('alpha'),
7204               ctx = canvas.getCtx();
7205           ctx.save();
7206           ctx.lineWidth = width;
7207           ctx.fillStyle = ctx.strokeStyle = color;
7208           ctx.globalAlpha = alpha;
7209           
7210           for(var s in ctxObj) {
7211             ctx[s] = node.getCanvasStyle(s);
7212           }
7213
7214           this.nodeTypes[f].render.call(this, node, canvas, animating);
7215           ctx.restore();
7216         }
7217     },
7218     
7219     /*
7220        Method: plotLine
7221     
7222        Plots a <Graph.Adjacence>.
7223
7224        Parameters:
7225
7226        adj - (object) A <Graph.Adjacence>.
7227        canvas - (object) A <Canvas> instance.
7228
7229     */
7230     plotLine: function(adj, canvas, animating) {
7231       var f = adj.getData('type'),
7232           ctxObj = this.edge.CanvasStyles;
7233       if(f != 'none') {
7234         var width = adj.getData('lineWidth'),
7235             color = adj.getData('color'),
7236             ctx = canvas.getCtx(),
7237             nodeFrom = adj.nodeFrom,
7238             nodeTo = adj.nodeTo;
7239         
7240         ctx.save();
7241         ctx.lineWidth = width;
7242         ctx.fillStyle = ctx.strokeStyle = color;
7243         ctx.globalAlpha = Math.min(nodeFrom.getData('alpha'), 
7244             nodeTo.getData('alpha'), 
7245             adj.getData('alpha'));
7246         
7247         for(var s in ctxObj) {
7248           ctx[s] = adj.getCanvasStyle(s);
7249         }
7250
7251         this.edgeTypes[f].render.call(this, adj, canvas, animating);
7252         ctx.restore();
7253       }
7254     }    
7255   
7256 };
7257
7258 /*
7259   Object: Graph.Plot3D
7260   
7261   <Graph> 3D rendering and animation methods.
7262   
7263   Properties:
7264   
7265   nodeHelper - <NodeHelper> object.
7266   edgeHelper - <EdgeHelper> object.
7267
7268 */
7269 Graph.Plot3D = $.merge(Graph.Plot, {
7270   Interpolator: {
7271     'linear': function(elem, props, delta) {
7272       var from = elem.startPos.getc(true);
7273       var to = elem.endPos.getc(true);
7274       elem.pos.setc(this.compute(from.x, to.x, delta), 
7275                     this.compute(from.y, to.y, delta),
7276                     this.compute(from.z, to.z, delta));
7277     }
7278   },
7279   
7280   plotNode: function(node, canvas) {
7281     if(node.getData('type') == 'none') return;
7282     this.plotElement(node, canvas, {
7283       getAlpha: function() {
7284         return node.getData('alpha');
7285       }
7286     });
7287   },
7288   
7289   plotLine: function(adj, canvas) {
7290     if(adj.getData('type') == 'none') return;
7291     this.plotElement(adj, canvas, {
7292       getAlpha: function() {
7293         return Math.min(adj.nodeFrom.getData('alpha'),
7294                         adj.nodeTo.getData('alpha'),
7295                         adj.getData('alpha'));
7296       }
7297     });
7298   },
7299   
7300   plotElement: function(elem, canvas, opt) {
7301     var gl = canvas.getCtx(),
7302         viewMatrix = new Matrix4,
7303         lighting = canvas.config.Scene.Lighting,
7304         wcanvas = canvas.canvases[0],
7305         program = wcanvas.program,
7306         camera = wcanvas.camera;
7307     
7308     if(!elem.geometry) {
7309       elem.geometry = new O3D[elem.getData('type')];
7310     }
7311     elem.geometry.update(elem);
7312     if(!elem.webGLVertexBuffer) {
7313       var vertices = [],
7314           faces = [],
7315           normals = [],
7316           vertexIndex = 0,
7317           geom = elem.geometry;
7318       
7319       for(var i=0, vs=geom.vertices, fs=geom.faces, fsl=fs.length; i<fsl; i++) {
7320         var face = fs[i],
7321             v1 = vs[face.a],
7322             v2 = vs[face.b],
7323             v3 = vs[face.c],
7324             v4 = face.d? vs[face.d] : false,
7325             n = face.normal;
7326         
7327         vertices.push(v1.x, v1.y, v1.z);
7328         vertices.push(v2.x, v2.y, v2.z);
7329         vertices.push(v3.x, v3.y, v3.z);
7330         if(v4) vertices.push(v4.x, v4.y, v4.z);
7331             
7332         normals.push(n.x, n.y, n.z);
7333         normals.push(n.x, n.y, n.z);
7334         normals.push(n.x, n.y, n.z);
7335         if(v4) normals.push(n.x, n.y, n.z);
7336             
7337         faces.push(vertexIndex, vertexIndex +1, vertexIndex +2);
7338         if(v4) {
7339           faces.push(vertexIndex, vertexIndex +2, vertexIndex +3);
7340           vertexIndex += 4;
7341         } else {
7342           vertexIndex += 3;
7343         }
7344       }
7345       //create and store vertex data
7346       elem.webGLVertexBuffer = gl.createBuffer();
7347       gl.bindBuffer(gl.ARRAY_BUFFER, elem.webGLVertexBuffer);
7348       gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
7349       //create and store faces index data
7350       elem.webGLFaceBuffer = gl.createBuffer();
7351       gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elem.webGLFaceBuffer);
7352       gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(faces), gl.STATIC_DRAW);
7353       elem.webGLFaceCount = faces.length;
7354       //calculate vertex normals and store them
7355       elem.webGLNormalBuffer = gl.createBuffer();
7356       gl.bindBuffer(gl.ARRAY_BUFFER, elem.webGLNormalBuffer);
7357       gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(normals), gl.STATIC_DRAW);
7358     }
7359     viewMatrix.multiply(camera.matrix, elem.geometry.matrix);
7360     //send matrix data
7361     gl.uniformMatrix4fv(program.viewMatrix, false, viewMatrix.flatten());
7362     gl.uniformMatrix4fv(program.projectionMatrix, false, camera.projectionMatrix.flatten());
7363     //send normal matrix for lighting
7364     var normalMatrix = Matrix4.makeInvert(viewMatrix);
7365     normalMatrix.$transpose();
7366     gl.uniformMatrix4fv(program.normalMatrix, false, normalMatrix.flatten());
7367     //send color data
7368     var color = $.hexToRgb(elem.getData('color'));
7369     color.push(opt.getAlpha());
7370     gl.uniform4f(program.color, color[0] / 255, color[1] / 255, color[2] / 255, color[3]);
7371     //send lighting data
7372     gl.uniform1i(program.enableLighting, lighting.enable);
7373     if(lighting.enable) {
7374       //set ambient light color
7375       if(lighting.ambient) {
7376         var acolor = lighting.ambient;
7377         gl.uniform3f(program.ambientColor, acolor[0], acolor[1], acolor[2]);
7378       }
7379       //set directional light
7380       if(lighting.directional) {
7381         var dir = lighting.directional,
7382             color = dir.color,
7383             pos = dir.direction,
7384             vd = new Vector3(pos.x, pos.y, pos.z).normalize().$scale(-1);
7385         gl.uniform3f(program.lightingDirection, vd.x, vd.y, vd.z);
7386         gl.uniform3f(program.directionalColor, color[0], color[1], color[2]);
7387       }
7388     }
7389     //send vertices data
7390     gl.bindBuffer(gl.ARRAY_BUFFER, elem.webGLVertexBuffer);
7391     gl.vertexAttribPointer(program.position, 3, gl.FLOAT, false, 0, 0);
7392     //send normals data
7393     gl.bindBuffer(gl.ARRAY_BUFFER, elem.webGLNormalBuffer);
7394     gl.vertexAttribPointer(program.normal, 3, gl.FLOAT, false, 0, 0);
7395     //draw!
7396     gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elem.webGLFaceBuffer );
7397     gl.drawElements(gl.TRIANGLES, elem.webGLFaceCount, gl.UNSIGNED_SHORT, 0);
7398   }
7399 });
7400
7401
7402 /*
7403  * File: Graph.Label.js
7404  *
7405 */
7406
7407 /*
7408    Object: Graph.Label
7409
7410    An interface for plotting/hiding/showing labels.
7411
7412    Description:
7413
7414    This is a generic interface for plotting/hiding/showing labels.
7415    The <Graph.Label> interface is implemented in multiple ways to provide
7416    different label types.
7417
7418    For example, the Graph.Label interface is implemented as <Graph.Label.HTML> to provide
7419    HTML label elements. Also we provide the <Graph.Label.SVG> interface for SVG type labels. 
7420    The <Graph.Label.Native> interface implements these methods with the native Canvas text rendering functions.
7421    
7422    All subclasses (<Graph.Label.HTML>, <Graph.Label.SVG> and <Graph.Label.Native>) implement the method plotLabel.
7423 */
7424
7425 Graph.Label = {};
7426
7427 /*
7428    Class: Graph.Label.Native
7429
7430    Implements labels natively, using the Canvas text API.
7431 */
7432 Graph.Label.Native = new Class({
7433     initialize: function(viz) {
7434       this.viz = viz;
7435     },
7436
7437     /*
7438        Method: plotLabel
7439
7440        Plots a label for a given node.
7441
7442        Parameters:
7443
7444        canvas - (object) A <Canvas> instance.
7445        node - (object) A <Graph.Node>.
7446        controller - (object) A configuration object.
7447        
7448        Example:
7449        
7450        (start code js)
7451        var viz = new $jit.Viz(options);
7452        var node = viz.graph.getNode('nodeId');
7453        viz.labels.plotLabel(viz.canvas, node, viz.config);
7454        (end code)
7455     */
7456     plotLabel: function(canvas, node, controller) {
7457       var ctx = canvas.getCtx();
7458       var pos = node.pos.getc(true);
7459
7460       ctx.font = node.getLabelData('style') + ' ' + node.getLabelData('size') + 'px ' + node.getLabelData('family');
7461       ctx.textAlign = node.getLabelData('textAlign');
7462       ctx.fillStyle = ctx.strokeStyle = node.getLabelData('color');
7463       ctx.textBaseline = node.getLabelData('textBaseline');
7464
7465       this.renderLabel(canvas, node, controller);
7466     },
7467
7468     /*
7469        renderLabel
7470
7471        Does the actual rendering of the label in the canvas. The default
7472        implementation renders the label close to the position of the node, this
7473        method should be overriden to position the labels differently.
7474
7475        Parameters:
7476
7477        canvas - A <Canvas> instance.
7478        node - A <Graph.Node>.
7479        controller - A configuration object. See also <Hypertree>, <RGraph>, <ST>.
7480     */
7481     renderLabel: function(canvas, node, controller) {
7482       var ctx = canvas.getCtx();
7483       var pos = node.pos.getc(true);
7484       ctx.fillText(node.name, pos.x, pos.y + node.getData("height") / 2);
7485     },
7486
7487     hideLabel: $.empty,
7488     hideLabels: $.empty
7489 });
7490
7491 /*
7492    Class: Graph.Label.DOM
7493
7494    Abstract Class implementing some DOM label methods.
7495
7496    Implemented by:
7497
7498    <Graph.Label.HTML> and <Graph.Label.SVG>.
7499
7500 */
7501 Graph.Label.DOM = new Class({
7502     //A flag value indicating if node labels are being displayed or not.
7503     labelsHidden: false,
7504     //Label container
7505     labelContainer: false,
7506     //Label elements hash.
7507     labels: {},
7508
7509     /*
7510        Method: getLabelContainer
7511
7512        Lazy fetcher for the label container.
7513
7514        Returns:
7515
7516        The label container DOM element.
7517
7518        Example:
7519
7520       (start code js)
7521         var viz = new $jit.Viz(options);
7522         var labelContainer = viz.labels.getLabelContainer();
7523         alert(labelContainer.innerHTML);
7524       (end code)
7525     */
7526     getLabelContainer: function() {
7527       return this.labelContainer ?
7528         this.labelContainer :
7529         this.labelContainer = document.getElementById(this.viz.config.labelContainer);
7530     },
7531
7532     /*
7533        Method: getLabel
7534
7535        Lazy fetcher for the label element.
7536
7537        Parameters:
7538
7539        id - (string) The label id (which is also a <Graph.Node> id).
7540
7541        Returns:
7542
7543        The label element.
7544
7545        Example:
7546
7547       (start code js)
7548         var viz = new $jit.Viz(options);
7549         var label = viz.labels.getLabel('someid');
7550         alert(label.innerHTML);
7551       (end code)
7552
7553     */
7554     getLabel: function(id) {
7555       return (id in this.labels && this.labels[id] != null) ?
7556         this.labels[id] :
7557         this.labels[id] = document.getElementById(id);
7558     },
7559
7560     /*
7561        Method: hideLabels
7562
7563        Hides all labels (by hiding the label container).
7564
7565        Parameters:
7566
7567        hide - (boolean) A boolean value indicating if the label container must be hidden or not.
7568
7569        Example:
7570        (start code js)
7571         var viz = new $jit.Viz(options);
7572         rg.labels.hideLabels(true);
7573        (end code)
7574
7575     */
7576     hideLabels: function (hide) {
7577       var container = this.getLabelContainer();
7578       if(hide)
7579         container.style.display = 'none';
7580       else
7581         container.style.display = '';
7582       this.labelsHidden = hide;
7583     },
7584
7585     /*
7586        Method: clearLabels
7587
7588        Clears the label container.
7589
7590        Useful when using a new visualization with the same canvas element/widget.
7591
7592        Parameters:
7593
7594        force - (boolean) Forces deletion of all labels.
7595
7596        Example:
7597        (start code js)
7598         var viz = new $jit.Viz(options);
7599         viz.labels.clearLabels();
7600         (end code)
7601     */
7602     clearLabels: function(force) {
7603       for(var id in this.labels) {
7604         if (force || !this.viz.graph.hasNode(id)) {
7605           this.disposeLabel(id);
7606           delete this.labels[id];
7607         }
7608       }
7609     },
7610
7611     /*
7612        Method: disposeLabel
7613
7614        Removes a label.
7615
7616        Parameters:
7617
7618        id - (string) A label id (which generally is also a <Graph.Node> id).
7619
7620        Example:
7621        (start code js)
7622         var viz = new $jit.Viz(options);
7623         viz.labels.disposeLabel('labelid');
7624        (end code)
7625     */
7626     disposeLabel: function(id) {
7627       var elem = this.getLabel(id);
7628       if(elem && elem.parentNode) {
7629         elem.parentNode.removeChild(elem);
7630       }
7631     },
7632
7633     /*
7634        Method: hideLabel
7635
7636        Hides the corresponding <Graph.Node> label.
7637
7638        Parameters:
7639
7640        node - (object) A <Graph.Node>. Can also be an array of <Graph.Nodes>.
7641        show - (boolean) If *true*, nodes will be shown. Otherwise nodes will be hidden.
7642
7643        Example:
7644        (start code js)
7645         var rg = new $jit.Viz(options);
7646         viz.labels.hideLabel(viz.graph.getNode('someid'), false);
7647        (end code)
7648     */
7649     hideLabel: function(node, show) {
7650       node = $.splat(node);
7651       var st = show ? "" : "none", lab, that = this;
7652       $.each(node, function(n) {
7653         var lab = that.getLabel(n.id);
7654         if (lab) {
7655           lab.style.display = st;
7656         }
7657       });
7658     },
7659
7660     /*
7661        fitsInCanvas
7662
7663        Returns _true_ or _false_ if the label for the node is contained in the canvas dom element or not.
7664
7665        Parameters:
7666
7667        pos - A <Complex> instance (I'm doing duck typing here so any object with _x_ and _y_ parameters will do).
7668        canvas - A <Canvas> instance.
7669
7670        Returns:
7671
7672        A boolean value specifying if the label is contained in the <Canvas> DOM element or not.
7673
7674     */
7675     fitsInCanvas: function(pos, canvas) {
7676       var size = canvas.getSize();
7677       if(pos.x >= size.width || pos.x < 0
7678          || pos.y >= size.height || pos.y < 0) return false;
7679        return true;
7680     }
7681 });
7682
7683 /*
7684    Class: Graph.Label.HTML
7685
7686    Implements HTML labels.
7687
7688    Extends:
7689
7690    All <Graph.Label.DOM> methods.
7691
7692 */
7693 Graph.Label.HTML = new Class({
7694     Implements: Graph.Label.DOM,
7695
7696     /*
7697        Method: plotLabel
7698
7699        Plots a label for a given node.
7700
7701        Parameters:
7702
7703        canvas - (object) A <Canvas> instance.
7704        node - (object) A <Graph.Node>.
7705        controller - (object) A configuration object.
7706        
7707       Example:
7708        
7709        (start code js)
7710        var viz = new $jit.Viz(options);
7711        var node = viz.graph.getNode('nodeId');
7712        viz.labels.plotLabel(viz.canvas, node, viz.config);
7713        (end code)
7714
7715
7716     */
7717     plotLabel: function(canvas, node, controller) {
7718       var id = node.id, tag = this.getLabel(id);
7719
7720       if(!tag && !(tag = document.getElementById(id))) {
7721         tag = document.createElement('div');
7722         var container = this.getLabelContainer();
7723         tag.id = id;
7724         tag.className = 'node';
7725         tag.style.position = 'absolute';
7726         controller.onCreateLabel(tag, node);
7727         container.appendChild(tag);
7728         this.labels[node.id] = tag;
7729       }
7730
7731       this.placeLabel(tag, node, controller);
7732     }
7733 });
7734
7735 /*
7736    Class: Graph.Label.SVG
7737
7738    Implements SVG labels.
7739
7740    Extends:
7741
7742    All <Graph.Label.DOM> methods.
7743 */
7744 Graph.Label.SVG = new Class({
7745     Implements: Graph.Label.DOM,
7746
7747     /*
7748        Method: plotLabel
7749
7750        Plots a label for a given node.
7751
7752        Parameters:
7753
7754        canvas - (object) A <Canvas> instance.
7755        node - (object) A <Graph.Node>.
7756        controller - (object) A configuration object.
7757        
7758        Example:
7759        
7760        (start code js)
7761        var viz = new $jit.Viz(options);
7762        var node = viz.graph.getNode('nodeId');
7763        viz.labels.plotLabel(viz.canvas, node, viz.config);
7764        (end code)
7765
7766
7767     */
7768     plotLabel: function(canvas, node, controller) {
7769       var id = node.id, tag = this.getLabel(id);
7770       if(!tag && !(tag = document.getElementById(id))) {
7771         var ns = 'http://www.w3.org/2000/svg';
7772           tag = document.createElementNS(ns, 'svg:text');
7773         var tspan = document.createElementNS(ns, 'svg:tspan');
7774         tag.appendChild(tspan);
7775         var container = this.getLabelContainer();
7776         tag.setAttribute('id', id);
7777         tag.setAttribute('class', 'node');
7778         container.appendChild(tag);
7779         controller.onCreateLabel(tag, node);
7780         this.labels[node.id] = tag;
7781       }
7782       this.placeLabel(tag, node, controller);
7783     }
7784 });
7785
7786
7787
7788 Graph.Geom = new Class({
7789
7790   initialize: function(viz) {
7791     this.viz = viz;
7792     this.config = viz.config;
7793     this.node = viz.config.Node;
7794     this.edge = viz.config.Edge;
7795   },
7796   /*
7797     Applies a translation to the tree.
7798   
7799     Parameters:
7800   
7801     pos - A <Complex> number specifying translation vector.
7802     prop - A <Graph.Node> position property ('pos', 'start' or 'end').
7803   
7804     Example:
7805   
7806     (start code js)
7807       st.geom.translate(new Complex(300, 100), 'end');
7808     (end code)
7809   */  
7810   translate: function(pos, prop) {
7811      prop = $.splat(prop);
7812      this.viz.graph.eachNode(function(elem) {
7813          $.each(prop, function(p) { elem.getPos(p).$add(pos); });
7814      });
7815   },
7816   /*
7817     Hides levels of the tree until it properly fits in canvas.
7818   */  
7819   setRightLevelToShow: function(node, canvas, callback) {
7820      var level = this.getRightLevelToShow(node, canvas), 
7821          fx = this.viz.labels,
7822          opt = $.merge({
7823            execShow:true,
7824            execHide:true,
7825            onHide: $.empty,
7826            onShow: $.empty
7827          }, callback || {});
7828      node.eachLevel(0, this.config.levelsToShow, function(n) {
7829          var d = n._depth - node._depth;
7830          if(d > level) {
7831              opt.onHide(n);
7832              if(opt.execHide) {
7833                n.drawn = false; 
7834                n.exist = false;
7835                fx.hideLabel(n, false);
7836              }
7837          } else {
7838              opt.onShow(n);
7839              if(opt.execShow) {
7840                n.exist = true;
7841              }
7842          }
7843      });
7844      node.drawn= true;
7845   },
7846   /*
7847     Returns the right level to show for the current tree in order to fit in canvas.
7848   */  
7849   getRightLevelToShow: function(node, canvas) {
7850      var config = this.config;
7851      var level = config.levelsToShow;
7852      var constrained = config.constrained;
7853      if(!constrained) return level;
7854      while(!this.treeFitsInCanvas(node, canvas, level) && level > 1) { level-- ; }
7855      return level;
7856   }
7857 });
7858
7859 /*
7860  * File: Loader.js
7861  * 
7862  */
7863
7864 /*
7865    Object: Loader
7866
7867    Provides methods for loading and serving JSON data.
7868 */
7869 var Loader = {
7870      construct: function(json) {
7871         var isGraph = ($.type(json) == 'array');
7872         var ans = new Graph(this.graphOptions, this.config.Node, this.config.Edge, this.config.Label);
7873         if(!isGraph) 
7874             //make tree
7875             (function (ans, json) {
7876                 ans.addNode(json);
7877                 if(json.children) {
7878                   for(var i=0, ch = json.children; i<ch.length; i++) {
7879                     ans.addAdjacence(json, ch[i]);
7880                     arguments.callee(ans, ch[i]);
7881                   }
7882                 }
7883             })(ans, json);
7884         else
7885             //make graph
7886             (function (ans, json) {
7887                 var getNode = function(id) {
7888                   for(var i=0, l=json.length; i<l; i++) {
7889                     if(json[i].id == id) {
7890                       return json[i];
7891                     }
7892                   }
7893                   // The node was not defined in the JSON
7894                   // Let's create it
7895                   var newNode = {
7896                                 "id" : id,
7897                                 "name" : id
7898                         };
7899                   return ans.addNode(newNode);
7900                 };
7901
7902                 for(var i=0, l=json.length; i<l; i++) {
7903                   ans.addNode(json[i]);
7904                   var adj = json[i].adjacencies;
7905                   if (adj) {
7906                     for(var j=0, lj=adj.length; j<lj; j++) {
7907                       var node = adj[j], data = {};
7908                       if(typeof adj[j] != 'string') {
7909                         data = $.merge(node.data, {});
7910                         node = node.nodeTo;
7911                       }
7912                       ans.addAdjacence(json[i], getNode(node), data);
7913                     }
7914                   }
7915                 }
7916             })(ans, json);
7917
7918         return ans;
7919     },
7920
7921     /*
7922      Method: loadJSON
7923     
7924      Loads a JSON structure to the visualization. The JSON structure can be a JSON *tree* or *graph* structure.
7925      
7926       A JSON tree or graph structure consists of nodes, each having as properties
7927        
7928        id - (string) A unique identifier for the node
7929        name - (string) A node's name
7930        data - (object) The data optional property contains a hash (i.e {}) 
7931        where you can store all the information you want about this node.
7932         
7933       For JSON *Tree* structures, there's an extra optional property *children* of type Array which contains the node's children.
7934       
7935       Example:
7936
7937       (start code js)
7938         var json = {  
7939           "id": "aUniqueIdentifier",  
7940           "name": "usually a nodes name",  
7941           "data": {
7942             "some key": "some value",
7943             "some other key": "some other value"
7944            },  
7945           "children": [ *other nodes or empty* ]  
7946         };  
7947       (end code)
7948         
7949         JSON *Graph* structures consist of an array of nodes, each specifying the nodes to which the current node is connected. 
7950         For JSON *Graph* structures, the *children* property is replaced by the *adjacencies* property.
7951         
7952         There are two types of *Graph* structures, *simple* and *extended* graph structures.
7953         
7954         For *simple* Graph structures, the adjacencies property contains an array of strings, each specifying the 
7955         id of the node connected to the main node.
7956         
7957         Example:
7958         
7959         (start code js)
7960         var json = [  
7961           {  
7962             "id": "aUniqueIdentifier",  
7963             "name": "usually a nodes name",  
7964             "data": {
7965               "some key": "some value",
7966               "some other key": "some other value"
7967              },  
7968             "adjacencies": ["anotherUniqueIdentifier", "yetAnotherUniqueIdentifier", 'etc']  
7969           },
7970
7971           'other nodes go here...' 
7972         ];          
7973         (end code)
7974         
7975         For *extended Graph structures*, the adjacencies property contains an array of Adjacency objects that have as properties
7976         
7977         nodeTo - (string) The other node connected by this adjacency.
7978         data - (object) A data property, where we can store custom key/value information.
7979         
7980         Example:
7981         
7982         (start code js)
7983         var json = [  
7984           {  
7985             "id": "aUniqueIdentifier",  
7986             "name": "usually a nodes name",  
7987             "data": {
7988               "some key": "some value",
7989               "some other key": "some other value"
7990              },  
7991             "adjacencies": [  
7992             {  
7993               nodeTo:"aNodeId",  
7994               data: {} //put whatever you want here  
7995             },
7996             'other adjacencies go here...'  
7997           },
7998
7999           'other nodes go here...' 
8000         ];          
8001         (end code)
8002        
8003        About the data property:
8004        
8005        As described before, you can store custom data in the *data* property of JSON *nodes* and *adjacencies*. 
8006        You can use almost any string as key for the data object. Some keys though are reserved by the toolkit, and 
8007        have special meanings. This is the case for keys starting with a dollar sign, for example, *$width*.
8008        
8009        For JSON *node* objects, adding dollar prefixed properties that match the names of the options defined in 
8010        <Options.Node> will override the general value for that option with that particular value. For this to work 
8011        however, you do have to set *overridable = true* in <Options.Node>.
8012        
8013        The same thing is true for JSON adjacencies. Dollar prefixed data properties will alter values set in <Options.Edge> 
8014        if <Options.Edge> has *overridable = true*.
8015        
8016        When loading JSON data into TreeMaps, the *data* property must contain a value for the *$area* key, 
8017        since this is the value which will be taken into account when creating the layout. 
8018        The same thing goes for the *$color* parameter.
8019        
8020        In JSON Nodes you can use also *$label-* prefixed properties to refer to <Options.Label> properties. For example, 
8021        *$label-size* will refer to <Options.Label> size property. Also, in JSON nodes and adjacencies you can set 
8022        canvas specific properties individually by using the *$canvas-* prefix. For example, *$canvas-shadowBlur* will refer 
8023        to the *shadowBlur* property.
8024        
8025        These properties can also be accessed after loading the JSON data from <Graph.Nodes> and <Graph.Adjacences> 
8026        by using <Accessors>. For more information take a look at the <Graph> and <Accessors> documentation.
8027        
8028        Finally, these properties can also be used to create advanced animations like with <Options.NodeStyles>. For more 
8029        information about creating animations please take a look at the <Graph.Plot> and <Graph.Plot.animate> documentation.
8030        
8031        loadJSON Parameters:
8032     
8033         json - A JSON Tree or Graph structure.
8034         i - For Graph structures only. Sets the indexed node as root for the visualization.
8035
8036     */
8037     loadJSON: function(json, i) {
8038       this.json = json;
8039       //if they're canvas labels erase them.
8040       if(this.labels && this.labels.clearLabels) {
8041         this.labels.clearLabels(true);
8042       }
8043       this.graph = this.construct(json);
8044       if($.type(json) != 'array'){
8045         this.root = json.id;
8046       } else {
8047         this.root = json[i? i : 0].id;
8048       }
8049     },
8050     
8051     /*
8052       Method: toJSON
8053    
8054       Returns a JSON tree/graph structure from the visualization's <Graph>. 
8055       See <Loader.loadJSON> for the graph formats available.
8056       
8057       See also:
8058       
8059       <Loader.loadJSON>
8060       
8061       Parameters:
8062       
8063       type - (string) Default's "tree". The type of the JSON structure to be returned. 
8064       Possible options are "tree" or "graph".
8065     */    
8066     toJSON: function(type) {
8067       type = type || "tree";
8068       if(type == 'tree') {
8069         var ans = {};
8070         var rootNode = this.graph.getNode(this.root);
8071         var ans = (function recTree(node) {
8072           var ans = {};
8073           ans.id = node.id;
8074           ans.name = node.name;
8075           ans.data = node.data;
8076           var ch =[];
8077           node.eachSubnode(function(n) {
8078             ch.push(recTree(n));
8079           });
8080           ans.children = ch;
8081           return ans;
8082         })(rootNode);
8083         return ans;
8084       } else {
8085         var ans = [];
8086         var T = !!this.graph.getNode(this.root).visited;
8087         this.graph.eachNode(function(node) {
8088           var ansNode = {};
8089           ansNode.id = node.id;
8090           ansNode.name = node.name;
8091           ansNode.data = node.data;
8092           var adjs = [];
8093           node.eachAdjacency(function(adj) {
8094             var nodeTo = adj.nodeTo;
8095             if(!!nodeTo.visited === T) {
8096               var ansAdj = {};
8097               ansAdj.nodeTo = nodeTo.id;
8098               ansAdj.data = adj.data;
8099               adjs.push(ansAdj);
8100             }
8101           });
8102           ansNode.adjacencies = adjs;
8103           ans.push(ansNode);
8104           node.visited = !T;
8105         });
8106         return ans;
8107       }
8108     }
8109 };
8110
8111
8112
8113 /*
8114  * File: Layouts.js
8115  * 
8116  * Implements base Tree and Graph layouts.
8117  *
8118  * Description:
8119  *
8120  * Implements base Tree and Graph layouts like Radial, Tree, etc.
8121  * 
8122  */
8123
8124 /*
8125  * Object: Layouts
8126  * 
8127  * Parent object for common layouts.
8128  *
8129  */
8130 var Layouts = $jit.Layouts = {};
8131
8132
8133 //Some util shared layout functions are defined here.
8134 var NodeDim = {
8135   label: null,
8136   
8137   compute: function(graph, prop, opt) {
8138     this.initializeLabel(opt);
8139     var label = this.label, style = label.style;
8140     graph.eachNode(function(n) {
8141       var autoWidth  = n.getData('autoWidth'),
8142           autoHeight = n.getData('autoHeight');
8143       if(autoWidth || autoHeight) {
8144         //delete dimensions since these are
8145         //going to be overridden now.
8146         delete n.data.$width;
8147         delete n.data.$height;
8148         delete n.data.$dim;
8149         
8150         var width  = n.getData('width'),
8151             height = n.getData('height');
8152         //reset label dimensions
8153         style.width  = autoWidth? 'auto' : width + 'px';
8154         style.height = autoHeight? 'auto' : height + 'px';
8155         
8156         //TODO(nico) should let the user choose what to insert here.
8157         label.innerHTML = n.name;
8158         
8159         var offsetWidth  = label.offsetWidth,
8160             offsetHeight = label.offsetHeight;
8161         var type = n.getData('type');
8162         if($.indexOf(['circle', 'square', 'triangle', 'star'], type) === -1) {
8163           n.setData('width', offsetWidth);
8164           n.setData('height', offsetHeight);
8165         } else {
8166           var dim = offsetWidth > offsetHeight? offsetWidth : offsetHeight;
8167           n.setData('width', dim);
8168           n.setData('height', dim);
8169           n.setData('dim', dim); 
8170         }
8171       }
8172     });
8173   },
8174   
8175   initializeLabel: function(opt) {
8176     if(!this.label) {
8177       this.label = document.createElement('div');
8178       document.body.appendChild(this.label);
8179     }
8180     this.setLabelStyles(opt);
8181   },
8182   
8183   setLabelStyles: function(opt) {
8184     $.extend(this.label.style, {
8185       'visibility': 'hidden',
8186       'position': 'absolute',
8187       'width': 'auto',
8188       'height': 'auto'
8189     });
8190     this.label.className = 'jit-autoadjust-label';
8191   }
8192 };
8193
8194
8195 /*
8196  * Class: Layouts.Tree
8197  * 
8198  * Implements a Tree Layout.
8199  * 
8200  * Implemented By:
8201  * 
8202  * <ST>
8203  * 
8204  * Inspired by:
8205  * 
8206  * Drawing Trees (Andrew J. Kennedy) <http://research.microsoft.com/en-us/um/people/akenn/fun/drawingtrees.pdf>
8207  * 
8208  */
8209 Layouts.Tree = (function() {
8210   //Layout functions
8211   var slice = Array.prototype.slice;
8212
8213   /*
8214      Calculates the max width and height nodes for a tree level
8215   */  
8216   function getBoundaries(graph, config, level, orn, prop) {
8217     var dim = config.Node;
8218     var multitree = config.multitree;
8219     if (dim.overridable) {
8220       var w = -1, h = -1;
8221       graph.eachNode(function(n) {
8222         if (n._depth == level
8223             && (!multitree || ('$orn' in n.data) && n.data.$orn == orn)) {
8224           var dw = n.getData('width', prop);
8225           var dh = n.getData('height', prop);
8226           w = (w < dw) ? dw : w;
8227           h = (h < dh) ? dh : h;
8228         }
8229       });
8230       return {
8231         'width' : w < 0 ? dim.width : w,
8232         'height' : h < 0 ? dim.height : h
8233       };
8234     } else {
8235       return dim;
8236     }
8237   }
8238
8239
8240   function movetree(node, prop, val, orn) {
8241     var p = (orn == "left" || orn == "right") ? "y" : "x";
8242     node.getPos(prop)[p] += val;
8243   }
8244
8245
8246   function moveextent(extent, val) {
8247     var ans = [];
8248     $.each(extent, function(elem) {
8249       elem = slice.call(elem);
8250       elem[0] += val;
8251       elem[1] += val;
8252       ans.push(elem);
8253     });
8254     return ans;
8255   }
8256
8257
8258   function merge(ps, qs) {
8259     if (ps.length == 0)
8260       return qs;
8261     if (qs.length == 0)
8262       return ps;
8263     var p = ps.shift(), q = qs.shift();
8264     return [ [ p[0], q[1] ] ].concat(merge(ps, qs));
8265   }
8266
8267
8268   function mergelist(ls, def) {
8269     def = def || [];
8270     if (ls.length == 0)
8271       return def;
8272     var ps = ls.pop();
8273     return mergelist(ls, merge(ps, def));
8274   }
8275
8276
8277   function fit(ext1, ext2, subtreeOffset, siblingOffset, i) {
8278     if (ext1.length <= i || ext2.length <= i)
8279       return 0;
8280
8281     var p = ext1[i][1], q = ext2[i][0];
8282     return Math.max(fit(ext1, ext2, subtreeOffset, siblingOffset, ++i)
8283         + subtreeOffset, p - q + siblingOffset);
8284   }
8285
8286
8287   function fitlistl(es, subtreeOffset, siblingOffset) {
8288     function $fitlistl(acc, es, i) {
8289       if (es.length <= i)
8290         return [];
8291       var e = es[i], ans = fit(acc, e, subtreeOffset, siblingOffset, 0);
8292       return [ ans ].concat($fitlistl(merge(acc, moveextent(e, ans)), es, ++i));
8293     }
8294     ;
8295     return $fitlistl( [], es, 0);
8296   }
8297
8298
8299   function fitlistr(es, subtreeOffset, siblingOffset) {
8300     function $fitlistr(acc, es, i) {
8301       if (es.length <= i)
8302         return [];
8303       var e = es[i], ans = -fit(e, acc, subtreeOffset, siblingOffset, 0);
8304       return [ ans ].concat($fitlistr(merge(moveextent(e, ans), acc), es, ++i));
8305     }
8306     ;
8307     es = slice.call(es);
8308     var ans = $fitlistr( [], es.reverse(), 0);
8309     return ans.reverse();
8310   }
8311
8312
8313   function fitlist(es, subtreeOffset, siblingOffset, align) {
8314     var esl = fitlistl(es, subtreeOffset, siblingOffset), esr = fitlistr(es,
8315         subtreeOffset, siblingOffset);
8316
8317     if (align == "left")
8318       esr = esl;
8319     else if (align == "right")
8320       esl = esr;
8321
8322     for ( var i = 0, ans = []; i < esl.length; i++) {
8323       ans[i] = (esl[i] + esr[i]) / 2;
8324     }
8325     return ans;
8326   }
8327
8328
8329   function design(graph, node, prop, config, orn) {
8330     var multitree = config.multitree;
8331     var auxp = [ 'x', 'y' ], auxs = [ 'width', 'height' ];
8332     var ind = +(orn == "left" || orn == "right");
8333     var p = auxp[ind], notp = auxp[1 - ind];
8334
8335     var cnode = config.Node;
8336     var s = auxs[ind], nots = auxs[1 - ind];
8337
8338     var siblingOffset = config.siblingOffset;
8339     var subtreeOffset = config.subtreeOffset;
8340     var align = config.align;
8341
8342     function $design(node, maxsize, acum) {
8343       var sval = node.getData(s, prop);
8344       var notsval = maxsize
8345           || (node.getData(nots, prop));
8346
8347       var trees = [], extents = [], chmaxsize = false;
8348       var chacum = notsval + config.levelDistance;
8349       node.eachSubnode(function(n) {
8350             if (n.exist
8351                 && (!multitree || ('$orn' in n.data) && n.data.$orn == orn)) {
8352
8353               if (!chmaxsize)
8354                 chmaxsize = getBoundaries(graph, config, n._depth, orn, prop);
8355
8356               var s = $design(n, chmaxsize[nots], acum + chacum);
8357               trees.push(s.tree);
8358               extents.push(s.extent);
8359             }
8360           });
8361       var positions = fitlist(extents, subtreeOffset, siblingOffset, align);
8362       for ( var i = 0, ptrees = [], pextents = []; i < trees.length; i++) {
8363         movetree(trees[i], prop, positions[i], orn);
8364         pextents.push(moveextent(extents[i], positions[i]));
8365       }
8366       var resultextent = [ [ -sval / 2, sval / 2 ] ]
8367           .concat(mergelist(pextents));
8368       node.getPos(prop)[p] = 0;
8369
8370       if (orn == "top" || orn == "left") {
8371         node.getPos(prop)[notp] = acum;
8372       } else {
8373         node.getPos(prop)[notp] = -acum;
8374       }
8375
8376       return {
8377         tree : node,
8378         extent : resultextent
8379       };
8380     }
8381
8382     $design(node, false, 0);
8383   }
8384
8385
8386   return new Class({
8387     /*
8388     Method: compute
8389     
8390     Computes nodes' positions.
8391
8392      */
8393     compute : function(property, computeLevels) {
8394       var prop = property || 'start';
8395       var node = this.graph.getNode(this.root);
8396       $.extend(node, {
8397         'drawn' : true,
8398         'exist' : true,
8399         'selected' : true
8400       });
8401       NodeDim.compute(this.graph, prop, this.config);
8402       if (!!computeLevels || !("_depth" in node)) {
8403         this.graph.computeLevels(this.root, 0, "ignore");
8404       }
8405       
8406       this.computePositions(node, prop);
8407     },
8408
8409     computePositions : function(node, prop) {
8410       var config = this.config;
8411       var multitree = config.multitree;
8412       var align = config.align;
8413       var indent = align !== 'center' && config.indent;
8414       var orn = config.orientation;
8415       var orns = multitree ? [ 'top', 'right', 'bottom', 'left' ] : [ orn ];
8416       var that = this;
8417       $.each(orns, function(orn) {
8418         //calculate layout
8419           design(that.graph, node, prop, that.config, orn, prop);
8420           var i = [ 'x', 'y' ][+(orn == "left" || orn == "right")];
8421           //absolutize
8422           (function red(node) {
8423             node.eachSubnode(function(n) {
8424               if (n.exist
8425                   && (!multitree || ('$orn' in n.data) && n.data.$orn == orn)) {
8426
8427                 n.getPos(prop)[i] += node.getPos(prop)[i];
8428                 if (indent) {
8429                   n.getPos(prop)[i] += align == 'left' ? indent : -indent;
8430                 }
8431                 red(n);
8432               }
8433             });
8434           })(node);
8435         });
8436     }
8437   });
8438   
8439 })();
8440
8441 /*
8442  * File: Spacetree.js
8443  */
8444
8445 /*
8446    Class: ST
8447    
8448   A Tree layout with advanced contraction and expansion animations.
8449      
8450   Inspired by:
8451  
8452   SpaceTree: Supporting Exploration in Large Node Link Tree, Design Evolution and Empirical Evaluation (Catherine Plaisant, Jesse Grosjean, Benjamin B. Bederson) 
8453   <http://hcil.cs.umd.edu/trs/2002-05/2002-05.pdf>
8454   
8455   Drawing Trees (Andrew J. Kennedy) <http://research.microsoft.com/en-us/um/people/akenn/fun/drawingtrees.pdf>
8456   
8457   Note:
8458  
8459   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.
8460  
8461   Implements:
8462   
8463   All <Loader> methods
8464   
8465   Constructor Options:
8466   
8467   Inherits options from
8468   
8469   - <Options.Canvas>
8470   - <Options.Controller>
8471   - <Options.Tree>
8472   - <Options.Node>
8473   - <Options.Edge>
8474   - <Options.Label>
8475   - <Options.Events>
8476   - <Options.Tips>
8477   - <Options.NodeStyles>
8478   - <Options.Navigation>
8479   
8480   Additionally, there are other parameters and some default values changed
8481   
8482   constrained - (boolean) Default's *true*. Whether to show the entire tree when loaded or just the number of levels specified by _levelsToShow_.
8483   levelsToShow - (number) Default's *2*. The number of levels to show for a subtree. This number is relative to the selected node.
8484   levelDistance - (number) Default's *30*. The distance between two consecutive levels of the tree.
8485   Node.type - Described in <Options.Node>. Default's set to *rectangle*.
8486   offsetX - (number) Default's *0*. The x-offset distance from the selected node to the center of the canvas.
8487   offsetY - (number) Default's *0*. The y-offset distance from the selected node to the center of the canvas.
8488   duration - Described in <Options.Fx>. It's default value has been changed to *700*.
8489   
8490   Instance Properties:
8491   
8492   canvas - Access a <Canvas> instance.
8493   graph - Access a <Graph> instance.
8494   op - Access a <ST.Op> instance.
8495   fx - Access a <ST.Plot> instance.
8496   labels - Access a <ST.Label> interface implementation.
8497
8498  */
8499
8500 $jit.ST= (function() {
8501     // Define some private methods first...
8502     // Nodes in path
8503     var nodesInPath = [];
8504     // Nodes to contract
8505     function getNodesToHide(node) {
8506       node = node || this.clickedNode;
8507       if(!this.config.constrained) {
8508         return [];
8509       }
8510       var Geom = this.geom;
8511       var graph = this.graph;
8512       var canvas = this.canvas;
8513       var level = node._depth, nodeArray = [];
8514           graph.eachNode(function(n) {
8515           if(n.exist && !n.selected) {
8516               if(n.isDescendantOf(node.id)) {
8517                 if(n._depth <= level) nodeArray.push(n);
8518               } else {
8519                 nodeArray.push(n);
8520               }
8521           }
8522           });
8523           var leafLevel = Geom.getRightLevelToShow(node, canvas);
8524           node.eachLevel(leafLevel, leafLevel, function(n) {
8525           if(n.exist && !n.selected) nodeArray.push(n);
8526           });
8527             
8528           for (var i = 0; i < nodesInPath.length; i++) {
8529             var n = this.graph.getNode(nodesInPath[i]);
8530             if(!n.isDescendantOf(node.id)) {
8531               nodeArray.push(n);
8532             }
8533           } 
8534           return nodeArray;       
8535     };
8536     // Nodes to expand
8537      function getNodesToShow(node) {
8538         var nodeArray = [], config = this.config;
8539         node = node || this.clickedNode;
8540         this.clickedNode.eachLevel(0, config.levelsToShow, function(n) {
8541             if(config.multitree && !('$orn' in n.data) 
8542                         && n.anySubnode(function(ch){ return ch.exist && !ch.drawn; })) {
8543                 nodeArray.push(n);
8544             } else if(n.drawn && !n.anySubnode("drawn")) {
8545               nodeArray.push(n);
8546             }
8547         });
8548         return nodeArray;
8549      };
8550     // Now define the actual class.
8551     return new Class({
8552     
8553         Implements: [Loader, Extras, Layouts.Tree],
8554         
8555         initialize: function(controller) {            
8556           var $ST = $jit.ST;
8557           
8558           var config= {
8559                 levelsToShow: 2,
8560                 levelDistance: 30,
8561                 constrained: true,                
8562                 Node: {
8563                   type: 'rectangle'
8564                 },
8565                 duration: 700,
8566                 offsetX: 0,
8567                 offsetY: 0
8568             };
8569             
8570             this.controller = this.config = $.merge(
8571                 Options("Canvas", "Fx", "Tree", "Node", "Edge", "Controller", 
8572                     "Tips", "NodeStyles", "Events", "Navigation", "Label"), config, controller);
8573
8574             var canvasConfig = this.config;
8575             if(canvasConfig.useCanvas) {
8576               this.canvas = canvasConfig.useCanvas;
8577               this.config.labelContainer = this.canvas.id + '-label';
8578             } else {
8579               if(canvasConfig.background) {
8580                 canvasConfig.background = $.merge({
8581                   type: 'Circles'
8582                 }, canvasConfig.background);
8583               }
8584               this.canvas = new Canvas(this, canvasConfig);
8585               this.config.labelContainer = (typeof canvasConfig.injectInto == 'string'? canvasConfig.injectInto : canvasConfig.injectInto.id) + '-label';
8586             }
8587
8588             this.graphOptions = {
8589                 'klass': Complex
8590             };
8591             this.graph = new Graph(this.graphOptions, this.config.Node, this.config.Edge);
8592             this.labels = new $ST.Label[canvasConfig.Label.type](this);
8593             this.fx = new $ST.Plot(this, $ST);
8594             this.op = new $ST.Op(this);
8595             this.group = new $ST.Group(this);
8596             this.geom = new $ST.Geom(this);
8597             this.clickedNode=  null;
8598             // initialize extras
8599             this.initializeExtras();
8600         },
8601     
8602         /*
8603          Method: plot
8604         
8605          Plots the <ST>. This is a shortcut to *fx.plot*.
8606
8607         */  
8608         plot: function() { this.fx.plot(this.controller); },
8609     
8610       
8611         /*
8612          Method: switchPosition
8613         
8614          Switches the tree orientation.
8615
8616          Parameters:
8617
8618         pos - (string) The new tree orientation. Possible values are "top", "left", "right" and "bottom".
8619         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.
8620         onComplete - (optional|object) This callback is called once the "switching" animation is complete.
8621
8622          Example:
8623
8624          (start code js)
8625            st.switchPosition("right", "animate", {
8626             onComplete: function() {
8627               alert('completed!');
8628             } 
8629            });
8630          (end code)
8631         */  
8632         switchPosition: function(pos, method, onComplete) {
8633           var Geom = this.geom, Plot = this.fx, that = this;
8634           if(!Plot.busy) {
8635               Plot.busy = true;
8636               this.contract({
8637                   onComplete: function() {
8638                       Geom.switchOrientation(pos);
8639                       that.compute('end', false);
8640                       Plot.busy = false;
8641                       if(method == 'animate') {
8642                           that.onClick(that.clickedNode.id, onComplete);  
8643                       } else if(method == 'replot') {
8644                           that.select(that.clickedNode.id, onComplete);
8645                       }
8646                   }
8647               }, pos);
8648           }
8649         },
8650
8651         /*
8652         Method: switchAlignment
8653        
8654         Switches the tree alignment.
8655
8656         Parameters:
8657
8658        align - (string) The new tree alignment. Possible values are "left", "center" and "right".
8659        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.
8660        onComplete - (optional|object) This callback is called once the "switching" animation is complete.
8661
8662         Example:
8663
8664         (start code js)
8665           st.switchAlignment("right", "animate", {
8666            onComplete: function() {
8667              alert('completed!');
8668            } 
8669           });
8670         (end code)
8671        */  
8672        switchAlignment: function(align, method, onComplete) {
8673         this.config.align = align;
8674         if(method == 'animate') {
8675                 this.select(this.clickedNode.id, onComplete);
8676         } else if(method == 'replot') {
8677                 this.onClick(this.clickedNode.id, onComplete);  
8678         }
8679        },
8680
8681        /*
8682         Method: addNodeInPath
8683        
8684         Adds a node to the current path as selected node. The selected node will be visible (as in non-collapsed) at all times.
8685         
8686
8687         Parameters:
8688
8689        id - (string) A <Graph.Node> id.
8690
8691         Example:
8692
8693         (start code js)
8694           st.addNodeInPath("nodeId");
8695         (end code)
8696        */  
8697        addNodeInPath: function(id) {
8698            nodesInPath.push(id);
8699            this.select((this.clickedNode && this.clickedNode.id) || this.root);
8700        },       
8701
8702        /*
8703        Method: clearNodesInPath
8704       
8705        Removes all nodes tagged as selected by the <ST.addNodeInPath> method.
8706        
8707        See also:
8708        
8709        <ST.addNodeInPath>
8710      
8711        Example:
8712
8713        (start code js)
8714          st.clearNodesInPath();
8715        (end code)
8716       */  
8717        clearNodesInPath: function(id) {
8718            nodesInPath.length = 0;
8719            this.select((this.clickedNode && this.clickedNode.id) || this.root);
8720        },
8721         
8722        /*
8723          Method: refresh
8724         
8725          Computes positions and plots the tree.
8726          
8727        */
8728        refresh: function() {
8729            this.reposition();
8730            this.select((this.clickedNode && this.clickedNode.id) || this.root);
8731        },    
8732
8733        reposition: function() {
8734             this.graph.computeLevels(this.root, 0, "ignore");
8735              this.geom.setRightLevelToShow(this.clickedNode, this.canvas);
8736             this.graph.eachNode(function(n) {
8737                 if(n.exist) n.drawn = true;
8738             });
8739             this.compute('end');
8740         },
8741         
8742         requestNodes: function(node, onComplete) {
8743           var handler = $.merge(this.controller, onComplete), 
8744           lev = this.config.levelsToShow;
8745           if(handler.request) {
8746               var leaves = [], d = node._depth;
8747               node.eachLevel(0, lev, function(n) {
8748                   if(n.drawn && 
8749                    !n.anySubnode()) {
8750                    leaves.push(n);
8751                    n._level = lev - (n._depth - d);
8752                   }
8753               });
8754               this.group.requestNodes(leaves, handler);
8755           }
8756             else
8757               handler.onComplete();
8758         },
8759      
8760         contract: function(onComplete, switched) {
8761           var orn  = this.config.orientation;
8762           var Geom = this.geom, Group = this.group;
8763           if(switched) Geom.switchOrientation(switched);
8764           var nodes = getNodesToHide.call(this);
8765           if(switched) Geom.switchOrientation(orn);
8766           Group.contract(nodes, $.merge(this.controller, onComplete));
8767         },
8768       
8769          move: function(node, onComplete) {
8770             this.compute('end', false);
8771             var move = onComplete.Move, offset = {
8772                 'x': move.offsetX,
8773                 'y': move.offsetY 
8774             };
8775             if(move.enable) {
8776                 this.geom.translate(node.endPos.add(offset).$scale(-1), "end");
8777             }
8778             this.fx.animate($.merge(this.controller, { modes: ['linear'] }, onComplete));
8779          },
8780       
8781         expand: function (node, onComplete) {
8782             var nodeArray = getNodesToShow.call(this, node);
8783             this.group.expand(nodeArray, $.merge(this.controller, onComplete));
8784         },
8785     
8786         selectPath: function(node) {
8787           var that = this;
8788           this.graph.eachNode(function(n) { n.selected = false; }); 
8789           function path(node) {
8790               if(node == null || node.selected) return;
8791               node.selected = true;
8792               $.each(that.group.getSiblings([node])[node.id], 
8793               function(n) { 
8794                    n.exist = true; 
8795                    n.drawn = true; 
8796               });    
8797               var parents = node.getParents();
8798               parents = (parents.length > 0)? parents[0] : null;
8799               path(parents);
8800           };
8801           for(var i=0, ns = [node.id].concat(nodesInPath); i < ns.length; i++) {
8802               path(this.graph.getNode(ns[i]));
8803           }
8804         },
8805       
8806         /*
8807         Method: setRoot
8808      
8809          Switches the current root node. Changes the topology of the Tree.
8810      
8811         Parameters:
8812            id - (string) The id of the node to be set as root.
8813            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.
8814            onComplete - (optional|object) An action to perform after the animation (if any).
8815  
8816         Example:
8817
8818         (start code js)
8819           st.setRoot('nodeId', 'animate', {
8820              onComplete: function() {
8821                alert('complete!');
8822              }
8823           });
8824         (end code)
8825      */
8826      setRoot: function(id, method, onComplete) {
8827                 if(this.busy) return;
8828                 this.busy = true;
8829           var that = this, canvas = this.canvas;
8830                 var rootNode = this.graph.getNode(this.root);
8831                 var clickedNode = this.graph.getNode(id);
8832                 function $setRoot() {
8833                 if(this.config.multitree && clickedNode.data.$orn) {
8834                         var orn = clickedNode.data.$orn;
8835                         var opp = {
8836                                         'left': 'right',
8837                                         'right': 'left',
8838                                         'top': 'bottom',
8839                                         'bottom': 'top'
8840                         }[orn];
8841                         rootNode.data.$orn = opp;
8842                         (function tag(rootNode) {
8843                                 rootNode.eachSubnode(function(n) {
8844                                         if(n.id != id) {
8845                                                 n.data.$orn = opp;
8846                                                 tag(n);
8847                                         }
8848                                 });
8849                         })(rootNode);
8850                         delete clickedNode.data.$orn;
8851                 }
8852                 this.root = id;
8853                 this.clickedNode = clickedNode;
8854                 this.graph.computeLevels(this.root, 0, "ignore");
8855                 this.geom.setRightLevelToShow(clickedNode, canvas, {
8856                   execHide: false,
8857                   onShow: function(node) {
8858                     if(!node.drawn) {
8859                     node.drawn = true;
8860                     node.setData('alpha', 1, 'end');
8861                     node.setData('alpha', 0);
8862                     node.pos.setc(clickedNode.pos.x, clickedNode.pos.y);
8863                     }
8864                   }
8865                 });
8866               this.compute('end');
8867               this.busy = true;
8868               this.fx.animate({
8869                 modes: ['linear', 'node-property:alpha'],
8870                 onComplete: function() {
8871                   that.busy = false;
8872                   that.onClick(id, {
8873                     onComplete: function() {
8874                       onComplete && onComplete.onComplete();
8875                     }
8876                   });
8877                 }
8878               });
8879                 }
8880
8881                 // delete previous orientations (if any)
8882                 delete rootNode.data.$orns;
8883
8884                 if(method == 'animate') {
8885                   $setRoot.call(this);
8886                   that.selectPath(clickedNode);
8887                 } else if(method == 'replot') {
8888                         $setRoot.call(this);
8889                         this.select(this.root);
8890                 }
8891      },
8892
8893      /*
8894            Method: addSubtree
8895         
8896             Adds a subtree.
8897         
8898            Parameters:
8899               subtree - (object) A JSON Tree object. See also <Loader.loadJSON>.
8900               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.
8901               onComplete - (optional|object) An action to perform after the animation (if any).
8902     
8903            Example:
8904
8905            (start code js)
8906              st.addSubtree(json, 'animate', {
8907                 onComplete: function() {
8908                   alert('complete!');
8909                 }
8910              });
8911            (end code)
8912         */
8913         addSubtree: function(subtree, method, onComplete) {
8914             if(method == 'replot') {
8915                 this.op.sum(subtree, $.extend({ type: 'replot' }, onComplete || {}));
8916             } else if (method == 'animate') {
8917                 this.op.sum(subtree, $.extend({ type: 'fade:seq' }, onComplete || {}));
8918             }
8919         },
8920     
8921         /*
8922            Method: removeSubtree
8923         
8924             Removes a subtree.
8925         
8926            Parameters:
8927               id - (string) The _id_ of the subtree to be removed.
8928               removeRoot - (boolean) Default's *false*. Remove the root of the subtree or only its subnodes.
8929               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.
8930               onComplete - (optional|object) An action to perform after the animation (if any).
8931
8932           Example:
8933
8934           (start code js)
8935             st.removeSubtree('idOfSubtreeToBeRemoved', false, 'animate', {
8936               onComplete: function() {
8937                 alert('complete!');
8938               }
8939             });
8940           (end code)
8941     
8942         */
8943         removeSubtree: function(id, removeRoot, method, onComplete) {
8944             var node = this.graph.getNode(id), subids = [];
8945             node.eachLevel(+!removeRoot, false, function(n) {
8946                 subids.push(n.id);
8947             });
8948             if(method == 'replot') {
8949                 this.op.removeNode(subids, $.extend({ type: 'replot' }, onComplete || {}));
8950             } else if (method == 'animate') {
8951                 this.op.removeNode(subids, $.extend({ type: 'fade:seq'}, onComplete || {}));
8952             }
8953         },
8954     
8955         /*
8956            Method: select
8957         
8958             Selects a node in the <ST> without performing an animation. Useful when selecting 
8959             nodes which are currently hidden or deep inside the tree.
8960
8961           Parameters:
8962             id - (string) The id of the node to select.
8963             onComplete - (optional|object) an onComplete callback.
8964
8965           Example:
8966           (start code js)
8967             st.select('mynodeid', {
8968               onComplete: function() {
8969                 alert('complete!');
8970               }
8971             });
8972           (end code)
8973         */
8974         select: function(id, onComplete) {
8975             var group = this.group, geom = this.geom;
8976             var node=  this.graph.getNode(id), canvas = this.canvas;
8977             var root  = this.graph.getNode(this.root);
8978             var complete = $.merge(this.controller, onComplete);
8979             var that = this;
8980     
8981             complete.onBeforeCompute(node);
8982             this.selectPath(node);
8983             this.clickedNode= node;
8984             this.requestNodes(node, {
8985                 onComplete: function(){
8986                     group.hide(group.prepare(getNodesToHide.call(that)), complete);
8987                     geom.setRightLevelToShow(node, canvas);
8988                     that.compute("current");
8989                     that.graph.eachNode(function(n) { 
8990                         var pos = n.pos.getc(true);
8991                         n.startPos.setc(pos.x, pos.y);
8992                         n.endPos.setc(pos.x, pos.y);
8993                         n.visited = false; 
8994                     });
8995                     var offset = { x: complete.offsetX, y: complete.offsetY };
8996                     that.geom.translate(node.endPos.add(offset).$scale(-1), ["start", "current", "end"]);
8997                     group.show(getNodesToShow.call(that));              
8998                     that.plot();
8999                     complete.onAfterCompute(that.clickedNode);
9000                     complete.onComplete();
9001                 }
9002             });     
9003         },
9004     
9005       /*
9006          Method: onClick
9007     
9008         Animates the <ST> to center the node specified by *id*.
9009             
9010         Parameters:
9011         
9012         id - (string) A node id.
9013         options - (optional|object) A group of options and callbacks described below.
9014         onComplete - (object) An object callback called when the animation finishes.
9015         Move - (object) An object that has as properties _offsetX_ or _offsetY_ for adding some offset position to the centered node.
9016
9017         Example:
9018
9019         (start code js)
9020           st.onClick('mynodeid', {
9021                   Move: {
9022                         enable: true,
9023                     offsetX: 30,
9024                     offsetY: 5
9025                   },
9026                   onComplete: function() {
9027                       alert('yay!');
9028                   }
9029           });
9030         (end code)
9031     
9032         */    
9033       onClick: function (id, options) {
9034         var canvas = this.canvas, that = this, Geom = this.geom, config = this.config;
9035         var innerController = {
9036             Move: {
9037                     enable: true,
9038               offsetX: config.offsetX || 0,
9039               offsetY: config.offsetY || 0  
9040             },
9041             setRightLevelToShowConfig: false,
9042             onBeforeRequest: $.empty,
9043             onBeforeContract: $.empty,
9044             onBeforeMove: $.empty,
9045             onBeforeExpand: $.empty
9046         };
9047         var complete = $.merge(this.controller, innerController, options);
9048         
9049         if(!this.busy) {
9050             this.busy = true;
9051             var node = this.graph.getNode(id);
9052             this.selectPath(node, this.clickedNode);
9053                 this.clickedNode = node;
9054             complete.onBeforeCompute(node);
9055             complete.onBeforeRequest(node);
9056             this.requestNodes(node, {
9057                 onComplete: function() {
9058                     complete.onBeforeContract(node);
9059                     that.contract({
9060                         onComplete: function() {
9061                             Geom.setRightLevelToShow(node, canvas, complete.setRightLevelToShowConfig);
9062                             complete.onBeforeMove(node);
9063                             that.move(node, {
9064                                 Move: complete.Move,
9065                                 onComplete: function() {
9066                                     complete.onBeforeExpand(node);
9067                                     that.expand(node, {
9068                                         onComplete: function() {
9069                                             that.busy = false;
9070                                             complete.onAfterCompute(id);
9071                                             complete.onComplete();
9072                                         }
9073                                     }); // expand
9074                                 }
9075                             }); // move
9076                         }
9077                     });// contract
9078                 }
9079             });// request
9080         }
9081       }
9082     });
9083
9084 })();
9085
9086 $jit.ST.$extend = true;
9087
9088 /*
9089    Class: ST.Op
9090     
9091    Custom extension of <Graph.Op>.
9092
9093    Extends:
9094
9095    All <Graph.Op> methods
9096    
9097    See also:
9098    
9099    <Graph.Op>
9100
9101 */
9102 $jit.ST.Op = new Class({
9103
9104   Implements: Graph.Op
9105     
9106 });
9107
9108 /*
9109     
9110      Performs operations on group of nodes.
9111
9112 */
9113 $jit.ST.Group = new Class({
9114     
9115     initialize: function(viz) {
9116         this.viz = viz;
9117         this.canvas = viz.canvas;
9118         this.config = viz.config;
9119         this.animation = new Animation;
9120         this.nodes = null;
9121     },
9122     
9123     /*
9124     
9125        Calls the request method on the controller to request a subtree for each node. 
9126     */
9127     requestNodes: function(nodes, controller) {
9128         var counter = 0, len = nodes.length, nodeSelected = {};
9129         var complete = function() { controller.onComplete(); };
9130         var viz = this.viz;
9131         if(len == 0) complete();
9132         for(var i=0; i<len; i++) {
9133             nodeSelected[nodes[i].id] = nodes[i];
9134             controller.request(nodes[i].id, nodes[i]._level, {
9135                 onComplete: function(nodeId, data) {
9136                     if(data && data.children) {
9137                         data.id = nodeId;
9138                         viz.op.sum(data, { type: 'nothing' });
9139                     }
9140                     if(++counter == len) {
9141                         viz.graph.computeLevels(viz.root, 0);
9142                         complete();
9143                     }
9144                 }
9145             });
9146         }
9147     },
9148     
9149     /*
9150     
9151        Collapses group of nodes. 
9152     */
9153     contract: function(nodes, controller) {
9154         var viz = this.viz;
9155         var that = this;
9156
9157         nodes = this.prepare(nodes);
9158         this.animation.setOptions($.merge(controller, {
9159             $animating: false,
9160             compute: function(delta) {
9161               if(delta == 1) delta = 0.99;
9162               that.plotStep(1 - delta, controller, this.$animating);
9163               this.$animating = 'contract';
9164             },
9165             
9166             complete: function() {
9167                 that.hide(nodes, controller);
9168             }       
9169         })).start();
9170     },
9171     
9172     hide: function(nodes, controller) {
9173         var viz = this.viz;
9174         for(var i=0; i<nodes.length; i++) {
9175             // TODO nodes are requested on demand, but not
9176             // deleted when hidden. Would that be a good feature?
9177             // Currently that feature is buggy, so I'll turn it off
9178             // Actually this feature is buggy because trimming should take
9179             // place onAfterCompute and not right after collapsing nodes.
9180             if (true || !controller || !controller.request) {
9181                 nodes[i].eachLevel(1, false, function(elem){
9182                     if (elem.exist) {
9183                         $.extend(elem, {
9184                             'drawn': false,
9185                             'exist': false
9186                         });
9187                     }
9188                 });
9189             } else {
9190                 var ids = [];
9191                 nodes[i].eachLevel(1, false, function(n) {
9192                     ids.push(n.id);
9193                 });
9194                 viz.op.removeNode(ids, { 'type': 'nothing' });
9195                 viz.labels.clearLabels();
9196             }
9197         }
9198         controller.onComplete();
9199     },    
9200     
9201
9202     /*
9203        Expands group of nodes. 
9204     */
9205     expand: function(nodes, controller) {
9206         var that = this;
9207         this.show(nodes);
9208         this.animation.setOptions($.merge(controller, {
9209             $animating: false,
9210             compute: function(delta) {
9211                 that.plotStep(delta, controller, this.$animating);
9212                 this.$animating = 'expand';
9213             },
9214             
9215             complete: function() {
9216                 that.plotStep(undefined, controller, false);
9217                 controller.onComplete();
9218             }       
9219         })).start();
9220         
9221     },
9222     
9223     show: function(nodes) {
9224         var config = this.config;
9225         this.prepare(nodes);
9226         $.each(nodes, function(n) {
9227                 // check for root nodes if multitree
9228                 if(config.multitree && !('$orn' in n.data)) {
9229                         delete n.data.$orns;
9230                         var orns = ' ';
9231                         n.eachSubnode(function(ch) {
9232                                 if(('$orn' in ch.data) 
9233                                                 && orns.indexOf(ch.data.$orn) < 0 
9234                                                 && ch.exist && !ch.drawn) {
9235                                         orns += ch.data.$orn + ' ';
9236                                 }
9237                         });
9238                         n.data.$orns = orns;
9239                 }
9240             n.eachLevel(0, config.levelsToShow, function(n) {
9241                 if(n.exist) n.drawn = true;
9242             });     
9243         });
9244     },
9245     
9246     prepare: function(nodes) {
9247         this.nodes = this.getNodesWithChildren(nodes);
9248         return this.nodes;
9249     },
9250     
9251     /*
9252        Filters an array of nodes leaving only nodes with children.
9253     */
9254     getNodesWithChildren: function(nodes) {
9255         var ans = [], config = this.config, root = this.viz.root;
9256         nodes.sort(function(a, b) { return (a._depth <= b._depth) - (a._depth >= b._depth); });
9257         for(var i=0; i<nodes.length; i++) {
9258             if(nodes[i].anySubnode("exist")) {
9259                 for (var j = i+1, desc = false; !desc && j < nodes.length; j++) {
9260                     if(!config.multitree || '$orn' in nodes[j].data) {
9261                                 desc = desc || nodes[i].isDescendantOf(nodes[j].id);                            
9262                     }
9263                 }
9264                 if(!desc) ans.push(nodes[i]);
9265             }
9266         }
9267         return ans;
9268     },
9269     
9270     plotStep: function(delta, controller, animating) {
9271         var viz = this.viz,
9272         config = this.config,
9273         canvas = viz.canvas, 
9274         ctx = canvas.getCtx(),
9275         nodes = this.nodes;
9276         var i, node;
9277         // hide nodes that are meant to be collapsed/expanded
9278         var nds = {};
9279         for(i=0; i<nodes.length; i++) {
9280           node = nodes[i];
9281           nds[node.id] = [];
9282           var root = config.multitree && !('$orn' in node.data);
9283           var orns = root && node.data.$orns;
9284           node.eachSubgraph(function(n) { 
9285             // TODO(nico): Cleanup
9286                   // special check for root node subnodes when
9287                   // multitree is checked.
9288                   if(root && orns && orns.indexOf(n.data.$orn) > 0 
9289                                   && n.drawn) {
9290                           n.drawn = false;
9291                   nds[node.id].push(n);
9292               } else if((!root || !orns) && n.drawn) {
9293                 n.drawn = false;
9294                 nds[node.id].push(n);
9295               }
9296             }); 
9297             node.drawn = true;
9298         }
9299         // plot the whole (non-scaled) tree
9300         if(nodes.length > 0) viz.fx.plot();
9301         // show nodes that were previously hidden
9302         for(i in nds) {
9303           $.each(nds[i], function(n) { n.drawn = true; });
9304         }
9305         // plot each scaled subtree
9306         for(i=0; i<nodes.length; i++) {
9307           node = nodes[i];
9308           ctx.save();
9309           viz.fx.plotSubtree(node, controller, delta, animating);                
9310           ctx.restore();
9311         }
9312       },
9313
9314       getSiblings: function(nodes) {
9315         var siblings = {};
9316         $.each(nodes, function(n) {
9317             var par = n.getParents();
9318             if (par.length == 0) {
9319                 siblings[n.id] = [n];
9320             } else {
9321                 var ans = [];
9322                 par[0].eachSubnode(function(sn) {
9323                     ans.push(sn);
9324                 });
9325                 siblings[n.id] = ans;
9326             }
9327         });
9328         return siblings;
9329     }
9330 });
9331
9332 /*
9333    ST.Geom
9334
9335    Performs low level geometrical computations.
9336
9337    Access:
9338
9339    This instance can be accessed with the _geom_ parameter of the st instance created.
9340
9341    Example:
9342
9343    (start code js)
9344     var st = new ST(canvas, config);
9345     st.geom.translate //or can also call any other <ST.Geom> method
9346    (end code)
9347
9348 */
9349
9350 $jit.ST.Geom = new Class({
9351     Implements: Graph.Geom,
9352     /*
9353        Changes the tree current orientation to the one specified.
9354
9355        You should usually use <ST.switchPosition> instead.
9356     */  
9357     switchOrientation: function(orn) {
9358         this.config.orientation = orn;
9359     },
9360
9361     /*
9362        Makes a value dispatch according to the current layout
9363        Works like a CSS property, either _top-right-bottom-left_ or _top|bottom - left|right_.
9364      */
9365     dispatch: function() {
9366           // TODO(nico) should store Array.prototype.slice.call somewhere.
9367         var args = Array.prototype.slice.call(arguments);
9368         var s = args.shift(), len = args.length;
9369         var val = function(a) { return typeof a == 'function'? a() : a; };
9370         if(len == 2) {
9371             return (s == "top" || s == "bottom")? val(args[0]) : val(args[1]);
9372         } else if(len == 4) {
9373             switch(s) {
9374                 case "top": return val(args[0]);
9375                 case "right": return val(args[1]);
9376                 case "bottom": return val(args[2]);
9377                 case "left": return val(args[3]);
9378             }
9379         }
9380         return undefined;
9381     },
9382
9383     /*
9384        Returns label height or with, depending on the tree current orientation.
9385     */  
9386     getSize: function(n, invert) {
9387         var data = n.data, config = this.config;
9388         var siblingOffset = config.siblingOffset;
9389         var s = (config.multitree 
9390                         && ('$orn' in data) 
9391                         && data.$orn) || config.orientation;
9392         var w = n.getData('width') + siblingOffset;
9393         var h = n.getData('height') + siblingOffset;
9394         if(!invert)
9395             return this.dispatch(s, h, w);
9396         else
9397             return this.dispatch(s, w, h);
9398     },
9399     
9400     /*
9401        Calculates a subtree base size. This is an utility function used by _getBaseSize_
9402     */  
9403     getTreeBaseSize: function(node, level, leaf) {
9404         var size = this.getSize(node, true), baseHeight = 0, that = this;
9405         if(leaf(level, node)) return size;
9406         if(level === 0) return 0;
9407         node.eachSubnode(function(elem) {
9408             baseHeight += that.getTreeBaseSize(elem, level -1, leaf);
9409         });
9410         return (size > baseHeight? size : baseHeight) + this.config.subtreeOffset;
9411     },
9412
9413
9414     /*
9415        getEdge
9416        
9417        Returns a Complex instance with the begin or end position of the edge to be plotted.
9418
9419        Parameters:
9420
9421        node - A <Graph.Node> that is connected to this edge.
9422        type - Returns the begin or end edge position. Possible values are 'begin' or 'end'.
9423
9424        Returns:
9425
9426        A <Complex> number specifying the begin or end position.
9427     */  
9428     getEdge: function(node, type, s) {
9429         var $C = function(a, b) { 
9430           return function(){
9431             return node.pos.add(new Complex(a, b));
9432           }; 
9433         };
9434         var dim = this.node;
9435         var w = node.getData('width');
9436         var h = node.getData('height');
9437
9438         if(type == 'begin') {
9439             if(dim.align == "center") {
9440                 return this.dispatch(s, $C(0, h/2), $C(-w/2, 0),
9441                                      $C(0, -h/2),$C(w/2, 0));
9442             } else if(dim.align == "left") {
9443                 return this.dispatch(s, $C(0, h), $C(0, 0),
9444                                      $C(0, 0), $C(w, 0));
9445             } else if(dim.align == "right") {
9446                 return this.dispatch(s, $C(0, 0), $C(-w, 0),
9447                                      $C(0, -h),$C(0, 0));
9448             } else throw "align: not implemented";
9449             
9450             
9451         } else if(type == 'end') {
9452             if(dim.align == "center") {
9453                 return this.dispatch(s, $C(0, -h/2), $C(w/2, 0),
9454                                      $C(0, h/2),  $C(-w/2, 0));
9455             } else if(dim.align == "left") {
9456                 return this.dispatch(s, $C(0, 0), $C(w, 0),
9457                                      $C(0, h), $C(0, 0));
9458             } else if(dim.align == "right") {
9459                 return this.dispatch(s, $C(0, -h),$C(0, 0),
9460                                      $C(0, 0), $C(-w, 0));
9461             } else throw "align: not implemented";
9462         }
9463     },
9464
9465     /*
9466        Adjusts the tree position due to canvas scaling or translation.
9467     */  
9468     getScaledTreePosition: function(node, scale) {
9469         var dim = this.node;
9470         var w = node.getData('width');
9471         var h = node.getData('height');
9472         var s = (this.config.multitree 
9473                         && ('$orn' in node.data) 
9474                         && node.data.$orn) || this.config.orientation;
9475
9476         var $C = function(a, b) { 
9477           return function(){
9478             return node.pos.add(new Complex(a, b)).$scale(1 - scale);
9479           }; 
9480         };
9481         if(dim.align == "left") {
9482             return this.dispatch(s, $C(0, h), $C(0, 0),
9483                                  $C(0, 0), $C(w, 0));
9484         } else if(dim.align == "center") {
9485             return this.dispatch(s, $C(0, h / 2), $C(-w / 2, 0),
9486                                  $C(0, -h / 2),$C(w / 2, 0));
9487         } else if(dim.align == "right") {
9488             return this.dispatch(s, $C(0, 0), $C(-w, 0),
9489                                  $C(0, -h),$C(0, 0));
9490         } else throw "align: not implemented";
9491     },
9492
9493     /*
9494        treeFitsInCanvas
9495        
9496        Returns a Boolean if the current subtree fits in canvas.
9497
9498        Parameters:
9499
9500        node - A <Graph.Node> which is the current root of the subtree.
9501        canvas - The <Canvas> object.
9502        level - The depth of the subtree to be considered.
9503     */  
9504     treeFitsInCanvas: function(node, canvas, level) {
9505         var csize = canvas.getSize();
9506         var s = (this.config.multitree 
9507                         && ('$orn' in node.data) 
9508                         && node.data.$orn) || this.config.orientation;
9509
9510         var size = this.dispatch(s, csize.width, csize.height);
9511         var baseSize = this.getTreeBaseSize(node, level, function(level, node) { 
9512           return level === 0 || !node.anySubnode();
9513         });
9514         return (baseSize < size);
9515     }
9516 });
9517
9518 /*
9519   Class: ST.Plot
9520   
9521   Custom extension of <Graph.Plot>.
9522
9523   Extends:
9524
9525   All <Graph.Plot> methods
9526   
9527   See also:
9528   
9529   <Graph.Plot>
9530
9531 */
9532 $jit.ST.Plot = new Class({
9533     
9534     Implements: Graph.Plot,
9535     
9536     /*
9537        Plots a subtree from the spacetree.
9538     */
9539     plotSubtree: function(node, opt, scale, animating) {
9540         var viz = this.viz, canvas = viz.canvas, config = viz.config;
9541         scale = Math.min(Math.max(0.001, scale), 1);
9542         if(scale >= 0) {
9543             node.drawn = false;     
9544             var ctx = canvas.getCtx();
9545             var diff = viz.geom.getScaledTreePosition(node, scale);
9546             ctx.translate(diff.x, diff.y);
9547             ctx.scale(scale, scale);
9548         }
9549         this.plotTree(node, $.merge(opt, {
9550           'withLabels': true,
9551           'hideLabels': !!scale,
9552           'plotSubtree': function(n, ch) {
9553             var root = config.multitree && !('$orn' in node.data);
9554             var orns = root && node.getData('orns');
9555             return !root || orns.indexOf(node.getData('orn')) > -1;
9556           }
9557         }), animating);
9558         if(scale >= 0) node.drawn = true;
9559     },   
9560    
9561     /*
9562         Method: getAlignedPos
9563         
9564         Returns a *x, y* object with the position of the top/left corner of a <ST> node.
9565         
9566         Parameters:
9567         
9568         pos - (object) A <Graph.Node> position.
9569         width - (number) The width of the node.
9570         height - (number) The height of the node.
9571         
9572      */
9573     getAlignedPos: function(pos, width, height) {
9574         var nconfig = this.node;
9575         var square, orn;
9576         if(nconfig.align == "center") {
9577             square = {
9578                 x: pos.x - width / 2,
9579                 y: pos.y - height / 2
9580             };
9581         } else if (nconfig.align == "left") {
9582             orn = this.config.orientation;
9583             if(orn == "bottom" || orn == "top") {
9584                 square = {
9585                     x: pos.x - width / 2,
9586                     y: pos.y
9587                 };
9588             } else {
9589                 square = {
9590                     x: pos.x,
9591                     y: pos.y - height / 2
9592                 };
9593             }
9594         } else if(nconfig.align == "right") {
9595             orn = this.config.orientation;
9596             if(orn == "bottom" || orn == "top") {
9597                 square = {
9598                     x: pos.x - width / 2,
9599                     y: pos.y - height
9600                 };
9601             } else {
9602                 square = {
9603                     x: pos.x - width,
9604                     y: pos.y - height / 2
9605                 };
9606             }
9607         } else throw "align: not implemented";
9608         
9609         return square;
9610     },
9611     
9612     getOrientation: function(adj) {
9613         var config = this.config;
9614         var orn = config.orientation;
9615
9616         if(config.multitree) {
9617                 var nodeFrom = adj.nodeFrom;
9618                 var nodeTo = adj.nodeTo;
9619                 orn = (('$orn' in nodeFrom.data) 
9620                         && nodeFrom.data.$orn) 
9621                         || (('$orn' in nodeTo.data) 
9622                         && nodeTo.data.$orn);
9623         }
9624
9625         return orn; 
9626     }
9627 });
9628
9629 /*
9630   Class: ST.Label
9631
9632   Custom extension of <Graph.Label>. 
9633   Contains custom <Graph.Label.SVG>, <Graph.Label.HTML> and <Graph.Label.Native> extensions.
9634
9635   Extends:
9636
9637   All <Graph.Label> methods and subclasses.
9638
9639   See also:
9640
9641   <Graph.Label>, <Graph.Label.Native>, <Graph.Label.HTML>, <Graph.Label.SVG>.
9642  */ 
9643 $jit.ST.Label = {};
9644
9645 /*
9646    ST.Label.Native
9647
9648    Custom extension of <Graph.Label.Native>.
9649
9650    Extends:
9651
9652    All <Graph.Label.Native> methods
9653
9654    See also:
9655
9656    <Graph.Label.Native>
9657 */
9658 $jit.ST.Label.Native = new Class({
9659   Implements: Graph.Label.Native,
9660
9661   renderLabel: function(canvas, node, controller) {
9662     var ctx = canvas.getCtx(),
9663         coord = node.pos.getc(true),
9664         width = node.getData('width'),
9665         height = node.getData('height'),
9666         pos = this.viz.fx.getAlignedPos(coord, width, height);
9667     ctx.fillText(node.name, pos.x + width / 2, pos.y + height / 2);
9668   }
9669 });
9670
9671 $jit.ST.Label.DOM = new Class({
9672   Implements: Graph.Label.DOM,
9673
9674   /* 
9675       placeLabel
9676
9677       Overrides abstract method placeLabel in <Graph.Plot>.
9678
9679       Parameters:
9680
9681       tag - A DOM label element.
9682       node - A <Graph.Node>.
9683       controller - A configuration/controller object passed to the visualization.
9684      
9685     */
9686     placeLabel: function(tag, node, controller) {
9687         var pos = node.pos.getc(true), 
9688             config = this.viz.config, 
9689             dim = config.Node, 
9690             canvas = this.viz.canvas,
9691             w = node.getData('width'),
9692             h = node.getData('height'),
9693             radius = canvas.getSize(),
9694             labelPos, orn;
9695         
9696         var ox = canvas.translateOffsetX,
9697             oy = canvas.translateOffsetY,
9698             sx = canvas.scaleOffsetX,
9699             sy = canvas.scaleOffsetY,
9700             posx = pos.x * sx + ox,
9701             posy = pos.y * sy + oy;
9702
9703         if(dim.align == "center") {
9704             labelPos= {
9705                 x: Math.round(posx - w / 2 + radius.width/2),
9706                 y: Math.round(posy - h / 2 + radius.height/2)
9707             };
9708         } else if (dim.align == "left") {
9709             orn = config.orientation;
9710             if(orn == "bottom" || orn == "top") {
9711                 labelPos= {
9712                     x: Math.round(posx - w / 2 + radius.width/2),
9713                     y: Math.round(posy + radius.height/2)
9714                 };
9715             } else {
9716                 labelPos= {
9717                     x: Math.round(posx + radius.width/2),
9718                     y: Math.round(posy - h / 2 + radius.height/2)
9719                 };
9720             }
9721         } else if(dim.align == "right") {
9722             orn = config.orientation;
9723             if(orn == "bottom" || orn == "top") {
9724                 labelPos= {
9725                     x: Math.round(posx - w / 2 + radius.width/2),
9726                     y: Math.round(posy - h + radius.height/2)
9727                 };
9728             } else {
9729                 labelPos= {
9730                     x: Math.round(posx - w + radius.width/2),
9731                     y: Math.round(posy - h / 2 + radius.height/2)
9732                 };
9733             }
9734         } else throw "align: not implemented";
9735
9736         var style = tag.style;
9737         style.left = labelPos.x + 'px';
9738         style.top  = labelPos.y + 'px';
9739         style.display = this.fitsInCanvas(labelPos, canvas)? '' : 'none';
9740         controller.onPlaceLabel(tag, node);
9741     }
9742 });
9743
9744 /*
9745   ST.Label.SVG
9746
9747   Custom extension of <Graph.Label.SVG>.
9748
9749   Extends:
9750
9751   All <Graph.Label.SVG> methods
9752
9753   See also:
9754
9755   <Graph.Label.SVG>
9756 */
9757 $jit.ST.Label.SVG = new Class({
9758   Implements: [$jit.ST.Label.DOM, Graph.Label.SVG],
9759
9760   initialize: function(viz) {
9761     this.viz = viz;
9762   }
9763 });
9764
9765 /*
9766    ST.Label.HTML
9767
9768    Custom extension of <Graph.Label.HTML>.
9769
9770    Extends:
9771
9772    All <Graph.Label.HTML> methods.
9773
9774    See also:
9775
9776    <Graph.Label.HTML>
9777
9778 */
9779 $jit.ST.Label.HTML = new Class({
9780   Implements: [$jit.ST.Label.DOM, Graph.Label.HTML],
9781
9782   initialize: function(viz) {
9783     this.viz = viz;
9784   }
9785 });
9786
9787
9788 /*
9789   Class: ST.Plot.NodeTypes
9790
9791   This class contains a list of <Graph.Node> built-in types. 
9792   Node types implemented are 'none', 'circle', 'rectangle', 'ellipse' and 'square'.
9793
9794   You can add your custom node types, customizing your visualization to the extreme.
9795
9796   Example:
9797
9798   (start code js)
9799     ST.Plot.NodeTypes.implement({
9800       'mySpecialType': {
9801         'render': function(node, canvas) {
9802           //print your custom node to canvas
9803         },
9804         //optional
9805         'contains': function(node, pos) {
9806           //return true if pos is inside the node or false otherwise
9807         }
9808       }
9809     });
9810   (end code)
9811
9812 */
9813 $jit.ST.Plot.NodeTypes = new Class({
9814   'none': {
9815     'render': $.empty,
9816     'contains': $.lambda(false)
9817   },
9818   'circle': {
9819     'render': function(node, canvas) {
9820       var dim  = node.getData('dim'),
9821           pos = this.getAlignedPos(node.pos.getc(true), dim, dim),
9822           dim2 = dim/2;
9823       this.nodeHelper.circle.render('fill', {x:pos.x+dim2, y:pos.y+dim2}, dim2, canvas);
9824     },
9825     'contains': function(node, pos) {
9826       var dim  = node.getData('dim'),
9827           npos = this.getAlignedPos(node.pos.getc(true), dim, dim),
9828           dim2 = dim/2;
9829       this.nodeHelper.circle.contains({x:npos.x+dim2, y:npos.y+dim2}, pos, dim2);
9830     }
9831   },
9832   'square': {
9833     'render': function(node, canvas) {
9834       var dim  = node.getData('dim'),
9835           dim2 = dim/2,
9836           pos = this.getAlignedPos(node.pos.getc(true), dim, dim);
9837       this.nodeHelper.square.render('fill', {x:pos.x+dim2, y:pos.y+dim2}, dim2, canvas);
9838     },
9839     'contains': function(node, pos) {
9840       var dim  = node.getData('dim'),
9841           npos = this.getAlignedPos(node.pos.getc(true), dim, dim),
9842           dim2 = dim/2;
9843       this.nodeHelper.square.contains({x:npos.x+dim2, y:npos.y+dim2}, pos, dim2);
9844     }
9845   },
9846   'ellipse': {
9847     'render': function(node, canvas) {
9848       var width = node.getData('width'),
9849           height = node.getData('height'),
9850           pos = this.getAlignedPos(node.pos.getc(true), width, height);
9851       this.nodeHelper.ellipse.render('fill', {x:pos.x+width/2, y:pos.y+height/2}, width, height, canvas);
9852     },
9853     'contains': function(node, pos) {
9854       var width = node.getData('width'),
9855           height = node.getData('height'),
9856           npos = this.getAlignedPos(node.pos.getc(true), width, height);
9857       this.nodeHelper.ellipse.contains({x:npos.x+width/2, y:npos.y+height/2}, pos, width, height);
9858     }
9859   },
9860   'rectangle': {
9861     'render': function(node, canvas) {
9862       var width = node.getData('width'),
9863           height = node.getData('height'),
9864           pos = this.getAlignedPos(node.pos.getc(true), width, height);
9865       this.nodeHelper.rectangle.render('fill', {x:pos.x+width/2, y:pos.y+height/2}, width, height, canvas);
9866     },
9867     'contains': function(node, pos) {
9868       var width = node.getData('width'),
9869           height = node.getData('height'),
9870           npos = this.getAlignedPos(node.pos.getc(true), width, height);
9871       this.nodeHelper.rectangle.contains({x:npos.x+width/2, y:npos.y+height/2}, pos, width, height);
9872     }
9873   }
9874 });
9875
9876 /*
9877   Class: ST.Plot.EdgeTypes
9878
9879   This class contains a list of <Graph.Adjacence> built-in types. 
9880   Edge types implemented are 'none', 'line', 'arrow', 'quadratic:begin', 'quadratic:end', 'bezier'.
9881
9882   You can add your custom edge types, customizing your visualization to the extreme.
9883
9884   Example:
9885
9886   (start code js)
9887     ST.Plot.EdgeTypes.implement({
9888       'mySpecialType': {
9889         'render': function(adj, canvas) {
9890           //print your custom edge to canvas
9891         },
9892         //optional
9893         'contains': function(adj, pos) {
9894           //return true if pos is inside the arc or false otherwise
9895         }
9896       }
9897     });
9898   (end code)
9899
9900 */
9901 $jit.ST.Plot.EdgeTypes = new Class({
9902     'none': $.empty,
9903     'line': {
9904       'render': function(adj, canvas) {
9905         var orn = this.getOrientation(adj),
9906             nodeFrom = adj.nodeFrom, 
9907             nodeTo = adj.nodeTo,
9908             rel = nodeFrom._depth < nodeTo._depth,
9909             from = this.viz.geom.getEdge(rel? nodeFrom:nodeTo, 'begin', orn),
9910             to =  this.viz.geom.getEdge(rel? nodeTo:nodeFrom, 'end', orn);
9911         this.edgeHelper.line.render(from, to, canvas);
9912       },
9913       'contains': function(adj, pos) {
9914         var orn = this.getOrientation(adj),
9915             nodeFrom = adj.nodeFrom, 
9916             nodeTo = adj.nodeTo,
9917             rel = nodeFrom._depth < nodeTo._depth,
9918             from = this.viz.geom.getEdge(rel? nodeFrom:nodeTo, 'begin', orn),
9919             to =  this.viz.geom.getEdge(rel? nodeTo:nodeFrom, 'end', orn);
9920         return this.edgeHelper.line.contains(from, to, pos, this.edge.epsilon);
9921       }
9922     },
9923      'arrow': {
9924        'render': function(adj, canvas) {
9925          var orn = this.getOrientation(adj),
9926              node = adj.nodeFrom, 
9927              child = adj.nodeTo,
9928              dim = adj.getData('dim'),
9929              from = this.viz.geom.getEdge(node, 'begin', orn),
9930              to = this.viz.geom.getEdge(child, 'end', orn),
9931              direction = adj.data.$direction,
9932              inv = (direction && direction.length>1 && direction[0] != node.id);
9933          this.edgeHelper.arrow.render(from, to, dim, inv, canvas);
9934        },
9935        'contains': function(adj, pos) {
9936          var orn = this.getOrientation(adj),
9937              nodeFrom = adj.nodeFrom, 
9938              nodeTo = adj.nodeTo,
9939              rel = nodeFrom._depth < nodeTo._depth,
9940              from = this.viz.geom.getEdge(rel? nodeFrom:nodeTo, 'begin', orn),
9941              to =  this.viz.geom.getEdge(rel? nodeTo:nodeFrom, 'end', orn);
9942          return this.edgeHelper.arrow.contains(from, to, pos, this.edge.epsilon);
9943        }
9944      },
9945     'quadratic:begin': {
9946        'render': function(adj, canvas) {
9947           var orn = this.getOrientation(adj);
9948           var nodeFrom = adj.nodeFrom, 
9949               nodeTo = adj.nodeTo,
9950               rel = nodeFrom._depth < nodeTo._depth,
9951               begin = this.viz.geom.getEdge(rel? nodeFrom:nodeTo, 'begin', orn),
9952               end =  this.viz.geom.getEdge(rel? nodeTo:nodeFrom, 'end', orn),
9953               dim = adj.getData('dim'),
9954               ctx = canvas.getCtx();
9955           ctx.beginPath();
9956           ctx.moveTo(begin.x, begin.y);
9957           switch(orn) {
9958             case "left":
9959               ctx.quadraticCurveTo(begin.x + dim, begin.y, end.x, end.y);
9960               break;
9961             case "right":
9962               ctx.quadraticCurveTo(begin.x - dim, begin.y, end.x, end.y);
9963               break;
9964             case "top":
9965               ctx.quadraticCurveTo(begin.x, begin.y + dim, end.x, end.y);
9966               break;
9967             case "bottom":
9968               ctx.quadraticCurveTo(begin.x, begin.y - dim, end.x, end.y);
9969               break;
9970           }
9971           ctx.stroke();
9972         }
9973      },
9974     'quadratic:end': {
9975        'render': function(adj, canvas) {
9976           var orn = this.getOrientation(adj);
9977           var nodeFrom = adj.nodeFrom, 
9978               nodeTo = adj.nodeTo,
9979               rel = nodeFrom._depth < nodeTo._depth,
9980               begin = this.viz.geom.getEdge(rel? nodeFrom:nodeTo, 'begin', orn),
9981               end =  this.viz.geom.getEdge(rel? nodeTo:nodeFrom, 'end', orn),
9982               dim = adj.getData('dim'),
9983               ctx = canvas.getCtx();
9984           ctx.beginPath();
9985           ctx.moveTo(begin.x, begin.y);
9986           switch(orn) {
9987             case "left":
9988               ctx.quadraticCurveTo(end.x - dim, end.y, end.x, end.y);
9989               break;
9990             case "right":
9991               ctx.quadraticCurveTo(end.x + dim, end.y, end.x, end.y);
9992               break;
9993             case "top":
9994               ctx.quadraticCurveTo(end.x, end.y - dim, end.x, end.y);
9995               break;
9996             case "bottom":
9997               ctx.quadraticCurveTo(end.x, end.y + dim, end.x, end.y);
9998               break;
9999           }
10000           ctx.stroke();
10001        }
10002      },
10003     'bezier': {
10004        'render': function(adj, canvas) {
10005          var orn = this.getOrientation(adj),
10006              nodeFrom = adj.nodeFrom, 
10007              nodeTo = adj.nodeTo,
10008              rel = nodeFrom._depth < nodeTo._depth,
10009              begin = this.viz.geom.getEdge(rel? nodeFrom:nodeTo, 'begin', orn),
10010              end =  this.viz.geom.getEdge(rel? nodeTo:nodeFrom, 'end', orn),
10011              dim = adj.getData('dim'),
10012              ctx = canvas.getCtx();
10013          ctx.beginPath();
10014          ctx.moveTo(begin.x, begin.y);
10015          switch(orn) {
10016            case "left":
10017              ctx.bezierCurveTo(begin.x + dim, begin.y, end.x - dim, end.y, end.x, end.y);
10018              break;
10019            case "right":
10020              ctx.bezierCurveTo(begin.x - dim, begin.y, end.x + dim, end.y, end.x, end.y);
10021              break;
10022            case "top":
10023              ctx.bezierCurveTo(begin.x, begin.y + dim, end.x, end.y - dim, end.x, end.y);
10024              break;
10025            case "bottom":
10026              ctx.bezierCurveTo(begin.x, begin.y - dim, end.x, end.y + dim, end.x, end.y);
10027              break;
10028          }
10029          ctx.stroke();
10030        }
10031     }
10032 });
10033
10034
10035
10036 /*
10037  * File: AreaChart.js
10038  *
10039 */
10040
10041 $jit.ST.Plot.NodeTypes.implement({
10042   'areachart-stacked' : {
10043     'render' : function(node, canvas) {
10044       var pos = node.pos.getc(true), 
10045           width = node.getData('width'),
10046           height = node.getData('height'),
10047           algnPos = this.getAlignedPos(pos, width, height),
10048           x = algnPos.x, y = algnPos.y,
10049           stringArray = node.getData('stringArray'),
10050           dimArray = node.getData('dimArray'),
10051           valArray = node.getData('valueArray'),
10052           valLeft = $.reduce(valArray, function(x, y) { return x + y[0]; }, 0),
10053           valRight = $.reduce(valArray, function(x, y) { return x + y[1]; }, 0),
10054           colorArray = node.getData('colorArray'),
10055           colorLength = colorArray.length,
10056           config = node.getData('config'),
10057           gradient = node.getData('gradient'),
10058           showLabels = config.showLabels,
10059           aggregates = config.showAggregates,
10060           label = config.Label,
10061           prev = node.getData('prev');
10062
10063       var ctx = canvas.getCtx(), border = node.getData('border');
10064       if (colorArray && dimArray && stringArray) {
10065         for (var i=0, l=dimArray.length, acumLeft=0, acumRight=0, valAcum=0; i<l; i++) {
10066           ctx.fillStyle = ctx.strokeStyle = colorArray[i % colorLength];
10067           ctx.save();
10068           if(gradient && (dimArray[i][0] > 0 || dimArray[i][1] > 0)) {
10069             var h1 = acumLeft + dimArray[i][0],
10070                 h2 = acumRight + dimArray[i][1],
10071                 alpha = Math.atan((h2 - h1) / width),
10072                 delta = 55;
10073             var linear = ctx.createLinearGradient(x + width/2, 
10074                 y - (h1 + h2)/2,
10075                 x + width/2 + delta * Math.sin(alpha),
10076                 y - (h1 + h2)/2 + delta * Math.cos(alpha));
10077             var color = $.rgbToHex($.map($.hexToRgb(colorArray[i % colorLength].slice(1)), 
10078                 function(v) { return (v * 0.85) >> 0; }));
10079             linear.addColorStop(0, colorArray[i % colorLength]);
10080             linear.addColorStop(1, color);
10081             ctx.fillStyle = linear;
10082           }
10083           ctx.beginPath();
10084           ctx.moveTo(x, y - acumLeft);
10085           ctx.lineTo(x + width, y - acumRight);
10086           ctx.lineTo(x + width, y - acumRight - dimArray[i][1]);
10087           ctx.lineTo(x, y - acumLeft - dimArray[i][0]);
10088           ctx.lineTo(x, y - acumLeft);
10089           ctx.fill();
10090           ctx.restore();
10091           if(border) {
10092             var strong = border.name == stringArray[i];
10093             var perc = strong? 0.7 : 0.8;
10094             var color = $.rgbToHex($.map($.hexToRgb(colorArray[i % colorLength].slice(1)), 
10095                 function(v) { return (v * perc) >> 0; }));
10096             ctx.strokeStyle = color;
10097             ctx.lineWidth = strong? 4 : 1;
10098             ctx.save();
10099             ctx.beginPath();
10100             if(border.index === 0) {
10101               ctx.moveTo(x, y - acumLeft);
10102               ctx.lineTo(x, y - acumLeft - dimArray[i][0]);
10103             } else {
10104               ctx.moveTo(x + width, y - acumRight);
10105               ctx.lineTo(x + width, y - acumRight - dimArray[i][1]);
10106             }
10107             ctx.stroke();
10108             ctx.restore();
10109           }
10110           acumLeft += (dimArray[i][0] || 0);
10111           acumRight += (dimArray[i][1] || 0);
10112           
10113           if(dimArray[i][0] > 0)
10114             valAcum += (valArray[i][0] || 0);
10115         }
10116         if(prev && label.type == 'Native') {
10117           ctx.save();
10118           ctx.beginPath();
10119           ctx.fillStyle = ctx.strokeStyle = label.color;
10120           ctx.font = label.style + ' ' + label.size + 'px ' + label.family;
10121           ctx.textAlign = 'center';
10122           ctx.textBaseline = 'middle';
10123           var aggValue = aggregates(node.name, valLeft, valRight, node, valAcum);
10124           if(aggValue !== false) {
10125             ctx.fillText(aggValue !== true? aggValue : valAcum, x, y - acumLeft - config.labelOffset - label.size/2, width);
10126           }
10127           if(showLabels(node.name, valLeft, valRight, node)) {
10128             ctx.fillText(node.name, x, y + label.size/2 + config.labelOffset);
10129           }
10130           ctx.restore();
10131         }
10132       }
10133     },
10134     'contains': function(node, mpos) {
10135       var pos = node.pos.getc(true), 
10136           width = node.getData('width'),
10137           height = node.getData('height'),
10138           algnPos = this.getAlignedPos(pos, width, height),
10139           x = algnPos.x, y = algnPos.y,
10140           dimArray = node.getData('dimArray'),
10141           rx = mpos.x - x;
10142       //bounding box check
10143       if(mpos.x < x || mpos.x > x + width
10144         || mpos.y > y || mpos.y < y - height) {
10145         return false;
10146       }
10147       //deep check
10148       for(var i=0, l=dimArray.length, lAcum=y, rAcum=y; i<l; i++) {
10149         var dimi = dimArray[i];
10150         lAcum -= dimi[0];
10151         rAcum -= dimi[1];
10152         var intersec = lAcum + (rAcum - lAcum) * rx / width;
10153         if(mpos.y >= intersec) {
10154           var index = +(rx > width/2);
10155           return {
10156             'name': node.getData('stringArray')[i],
10157             'color': node.getData('colorArray')[i],
10158             'value': node.getData('valueArray')[i][index],
10159             'index': index
10160           };
10161         }
10162       }
10163       return false;
10164     }
10165   }
10166 });
10167
10168 /*
10169   Class: AreaChart
10170   
10171   A visualization that displays stacked area charts.
10172   
10173   Constructor Options:
10174   
10175   See <Options.AreaChart>.
10176
10177 */
10178 $jit.AreaChart = new Class({
10179   st: null,
10180   colors: ["#416D9C", "#70A35E", "#EBB056", "#C74243", "#83548B", "#909291", "#557EAA"],
10181   selected: {},
10182   busy: false,
10183   
10184   initialize: function(opt) {
10185     this.controller = this.config = 
10186       $.merge(Options("Canvas", "Margin", "Label", "AreaChart"), {
10187         Label: { type: 'Native' }
10188       }, opt);
10189     //set functions for showLabels and showAggregates
10190     var showLabels = this.config.showLabels,
10191         typeLabels = $.type(showLabels),
10192         showAggregates = this.config.showAggregates,
10193         typeAggregates = $.type(showAggregates);
10194     this.config.showLabels = typeLabels == 'function'? showLabels : $.lambda(showLabels);
10195     this.config.showAggregates = typeAggregates == 'function'? showAggregates : $.lambda(showAggregates);
10196     
10197     this.initializeViz();
10198   },
10199   
10200   initializeViz: function() {
10201     var config = this.config,
10202         that = this,
10203         nodeType = config.type.split(":")[0],
10204         nodeLabels = {};
10205
10206     var delegate = new $jit.ST({
10207       injectInto: config.injectInto,
10208       width: config.width,
10209       height: config.height,
10210       orientation: "bottom",
10211       levelDistance: 0,
10212       siblingOffset: 0,
10213       subtreeOffset: 0,
10214       withLabels: config.Label.type != 'Native',
10215       useCanvas: config.useCanvas,
10216       Label: {
10217         type: config.Label.type
10218       },
10219       Node: {
10220         overridable: true,
10221         type: 'areachart-' + nodeType,
10222         align: 'left',
10223         width: 1,
10224         height: 1
10225       },
10226       Edge: {
10227         type: 'none'
10228       },
10229       Tips: {
10230         enable: config.Tips.enable,
10231         type: 'Native',
10232         force: true,
10233         onShow: function(tip, node, contains) {
10234           var elem = contains;
10235           config.Tips.onShow(tip, elem, node);
10236         }
10237       },
10238       Events: {
10239         enable: true,
10240         type: 'Native',
10241         onClick: function(node, eventInfo, evt) {
10242           if(!config.filterOnClick && !config.Events.enable) return;
10243           var elem = eventInfo.getContains();
10244           if(elem) config.filterOnClick && that.filter(elem.name);
10245           config.Events.enable && config.Events.onClick(elem, eventInfo, evt);
10246         },
10247         onRightClick: function(node, eventInfo, evt) {
10248           if(!config.restoreOnRightClick) return;
10249           that.restore();
10250         },
10251         onMouseMove: function(node, eventInfo, evt) {
10252           if(!config.selectOnHover) return;
10253           if(node) {
10254             var elem = eventInfo.getContains();
10255             that.select(node.id, elem.name, elem.index);
10256           } else {
10257             that.select(false, false, false);
10258           }
10259         }
10260       },
10261       onCreateLabel: function(domElement, node) {
10262         var labelConf = config.Label,
10263             valueArray = node.getData('valueArray'),
10264             acumLeft = $.reduce(valueArray, function(x, y) { return x + y[0]; }, 0),
10265             acumRight = $.reduce(valueArray, function(x, y) { return x + y[1]; }, 0);
10266         if(node.getData('prev')) {
10267           var nlbs = {
10268             wrapper: document.createElement('div'),
10269             aggregate: document.createElement('div'),
10270             label: document.createElement('div')
10271           };
10272           var wrapper = nlbs.wrapper,
10273               label = nlbs.label,
10274               aggregate = nlbs.aggregate,
10275               wrapperStyle = wrapper.style,
10276               labelStyle = label.style,
10277               aggregateStyle = aggregate.style;
10278           //store node labels
10279           nodeLabels[node.id] = nlbs;
10280           //append labels
10281           wrapper.appendChild(label);
10282           wrapper.appendChild(aggregate);
10283           if(!config.showLabels(node.name, acumLeft, acumRight, node)) {
10284             label.style.display = 'none';
10285           }
10286           if(!config.showAggregates(node.name, acumLeft, acumRight, node)) {
10287             aggregate.style.display = 'none';
10288           }
10289           wrapperStyle.position = 'relative';
10290           wrapperStyle.overflow = 'visible';
10291           wrapperStyle.fontSize = labelConf.size + 'px';
10292           wrapperStyle.fontFamily = labelConf.family;
10293           wrapperStyle.color = labelConf.color;
10294           wrapperStyle.textAlign = 'center';
10295           aggregateStyle.position = labelStyle.position = 'absolute';
10296           
10297           domElement.style.width = node.getData('width') + 'px';
10298           domElement.style.height = node.getData('height') + 'px';
10299           label.innerHTML = node.name;
10300           
10301           domElement.appendChild(wrapper);
10302         }
10303       },
10304       onPlaceLabel: function(domElement, node) {
10305         if(!node.getData('prev')) return;
10306         var labels = nodeLabels[node.id],
10307             wrapperStyle = labels.wrapper.style,
10308             labelStyle = labels.label.style,
10309             aggregateStyle = labels.aggregate.style,
10310             width = node.getData('width'),
10311             height = node.getData('height'),
10312             dimArray = node.getData('dimArray'),
10313             valArray = node.getData('valueArray'),
10314             acumLeft = $.reduce(valArray, function(x, y) { return x + y[0]; }, 0),
10315             acumRight = $.reduce(valArray, function(x, y) { return x + y[1]; }, 0),
10316             font = parseInt(wrapperStyle.fontSize, 10),
10317             domStyle = domElement.style;
10318         
10319         if(dimArray && valArray) {
10320           if(config.showLabels(node.name, acumLeft, acumRight, node)) {
10321             labelStyle.display = '';
10322           } else {
10323             labelStyle.display = 'none';
10324           }
10325           var aggValue = config.showAggregates(node.name, acumLeft, acumRight, node);
10326           if(aggValue !== false) {
10327             aggregateStyle.display = '';
10328           } else {
10329             aggregateStyle.display = 'none';
10330           }
10331           wrapperStyle.width = aggregateStyle.width = labelStyle.width = domElement.style.width = width + 'px';
10332           aggregateStyle.left = labelStyle.left = -width/2 + 'px';
10333           for(var i=0, l=valArray.length, acum=0, leftAcum=0; i<l; i++) {
10334             if(dimArray[i][0] > 0) {
10335               acum+= valArray[i][0];
10336               leftAcum+= dimArray[i][0];
10337             }
10338           }
10339           aggregateStyle.top = (-font - config.labelOffset) + 'px';
10340           labelStyle.top = (config.labelOffset + leftAcum) + 'px';
10341           domElement.style.top = parseInt(domElement.style.top, 10) - leftAcum + 'px';
10342           domElement.style.height = wrapperStyle.height = leftAcum + 'px';
10343           labels.aggregate.innerHTML = aggValue !== true? aggValue : acum;
10344         }
10345       }
10346     });
10347     
10348     var size = delegate.canvas.getSize(),
10349         margin = config.Margin;
10350     delegate.config.offsetY = -size.height/2 + margin.bottom 
10351       + (config.showLabels && (config.labelOffset + config.Label.size));
10352     delegate.config.offsetX = (margin.right - margin.left)/2;
10353     this.delegate = delegate;
10354     this.canvas = this.delegate.canvas;
10355   },
10356   
10357  /*
10358   Method: loadJSON
10359  
10360   Loads JSON data into the visualization. 
10361   
10362   Parameters:
10363   
10364   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>.
10365   
10366   Example:
10367   (start code js)
10368   var areaChart = new $jit.AreaChart(options);
10369   areaChart.loadJSON(json);
10370   (end code)
10371  */  
10372   loadJSON: function(json) {
10373     var prefix = $.time(), 
10374         ch = [], 
10375         delegate = this.delegate,
10376         name = $.splat(json.label), 
10377         color = $.splat(json.color || this.colors),
10378         config = this.config,
10379         gradient = !!config.type.split(":")[1],
10380         animate = config.animate;
10381     
10382     for(var i=0, values=json.values, l=values.length; i<l-1; i++) {
10383       var val = values[i], prev = values[i-1], next = values[i+1];
10384       var valLeft = $.splat(values[i].values), valRight = $.splat(values[i+1].values);
10385       var valArray = $.zip(valLeft, valRight);
10386       var acumLeft = 0, acumRight = 0;
10387       ch.push({
10388         'id': prefix + val.label,
10389         'name': val.label,
10390         'data': {
10391           'value': valArray,
10392           '$valueArray': valArray,
10393           '$colorArray': color,
10394           '$stringArray': name,
10395           '$next': next.label,
10396           '$prev': prev? prev.label:false,
10397           '$config': config,
10398           '$gradient': gradient
10399         },
10400         'children': []
10401       });
10402     }
10403     var root = {
10404       'id': prefix + '$root',
10405       'name': '',
10406       'data': {
10407         '$type': 'none',
10408         '$width': 1,
10409         '$height': 1
10410       },
10411       'children': ch
10412     };
10413     delegate.loadJSON(root);
10414     
10415     this.normalizeDims();
10416     delegate.compute();
10417     delegate.select(delegate.root);
10418     if(animate) {
10419       delegate.fx.animate({
10420         modes: ['node-property:height:dimArray'],
10421         duration:1500
10422       });
10423     }
10424   },
10425   
10426  /*
10427   Method: updateJSON
10428  
10429   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.
10430   
10431   Parameters:
10432   
10433   json - (object) JSON data to be updated. The JSON format corresponds to the one described in <AreaChart.loadJSON>.
10434   onComplete - (object) A callback object to be called when the animation transition when updating the data end.
10435   
10436   Example:
10437   
10438   (start code js)
10439   areaChart.updateJSON(json, {
10440     onComplete: function() {
10441       alert('update complete!');
10442     }
10443   });
10444   (end code)
10445  */  
10446   updateJSON: function(json, onComplete) {
10447     if(this.busy) return;
10448     this.busy = true;
10449     
10450     var delegate = this.delegate,
10451         graph = delegate.graph,
10452         labels = json.label && $.splat(json.label),
10453         values = json.values,
10454         animate = this.config.animate,
10455         that = this,
10456         hashValues = {};
10457
10458     //convert the whole thing into a hash
10459     for (var i = 0, l = values.length; i < l; i++) {
10460       hashValues[values[i].label] = values[i];
10461     }
10462   
10463     graph.eachNode(function(n) {
10464       var v = hashValues[n.name],
10465           stringArray = n.getData('stringArray'),
10466           valArray = n.getData('valueArray'),
10467           next = n.getData('next');
10468       
10469       if (v) {
10470         v.values = $.splat(v.values);
10471         $.each(valArray, function(a, i) {
10472           a[0] = v.values[i];
10473           if(labels) stringArray[i] = labels[i];
10474         });
10475         n.setData('valueArray', valArray);
10476       }
10477      
10478       if(next) {
10479         v = hashValues[next];
10480         if(v) {
10481           $.each(valArray, function(a, i) {
10482             a[1] = v.values[i];
10483           });
10484         }
10485       }
10486     });
10487     this.normalizeDims();
10488     delegate.compute();
10489     delegate.select(delegate.root);
10490     if(animate) {
10491       delegate.fx.animate({
10492         modes: ['node-property:height:dimArray'],
10493         duration:1500,
10494         onComplete: function() {
10495           that.busy = false;
10496           onComplete && onComplete.onComplete();
10497         }
10498       });
10499     }
10500   },
10501   
10502 /*
10503   Method: filter
10504  
10505   Filter selected stacks, collapsing all other stacks. You can filter multiple stacks at the same time.
10506   
10507   Parameters:
10508   
10509   filters - (array) An array of strings with the name of the stacks to be filtered.
10510   callback - (object) An object with an *onComplete* callback method. 
10511   
10512   Example:
10513   
10514   (start code js)
10515   areaChart.filter(['label A', 'label C'], {
10516       onComplete: function() {
10517           console.log('done!');
10518       }
10519   });
10520   (end code)
10521   
10522   See also:
10523   
10524   <AreaChart.restore>.
10525  */  
10526   filter: function(filters, callback) {
10527     if(this.busy) return;
10528     this.busy = true;
10529     if(this.config.Tips.enable) this.delegate.tips.hide();
10530     this.select(false, false, false);
10531     var args = $.splat(filters);
10532     var rt = this.delegate.graph.getNode(this.delegate.root);
10533     var that = this;
10534     this.normalizeDims();
10535     rt.eachAdjacency(function(adj) {
10536       var n = adj.nodeTo, 
10537           dimArray = n.getData('dimArray', 'end'),
10538           stringArray = n.getData('stringArray');
10539       n.setData('dimArray', $.map(dimArray, function(d, i) {
10540         return ($.indexOf(args, stringArray[i]) > -1)? d:[0, 0];
10541       }), 'end');
10542     });
10543     this.delegate.fx.animate({
10544       modes: ['node-property:dimArray'],
10545       duration:1500,
10546       onComplete: function() {
10547         that.busy = false;
10548         callback && callback.onComplete();
10549       }
10550     });
10551   },
10552   
10553   /*
10554   Method: restore
10555  
10556   Sets all stacks that could have been filtered visible.
10557   
10558   Example:
10559   
10560   (start code js)
10561   areaChart.restore();
10562   (end code)
10563   
10564   See also:
10565   
10566   <AreaChart.filter>.
10567  */  
10568   restore: function(callback) {
10569     if(this.busy) return;
10570     this.busy = true;
10571     if(this.config.Tips.enable) this.delegate.tips.hide();
10572     this.select(false, false, false);
10573     this.normalizeDims();
10574     var that = this;
10575     this.delegate.fx.animate({
10576       modes: ['node-property:height:dimArray'],
10577       duration:1500,
10578       onComplete: function() {
10579         that.busy = false;
10580         callback && callback.onComplete();
10581       }
10582     });
10583   },
10584   //adds the little brown bar when hovering the node
10585   select: function(id, name, index) {
10586     if(!this.config.selectOnHover) return;
10587     var s = this.selected;
10588     if(s.id != id || s.name != name 
10589         || s.index != index) {
10590       s.id = id;
10591       s.name = name;
10592       s.index = index;
10593       this.delegate.graph.eachNode(function(n) {
10594         n.setData('border', false);
10595       });
10596       if(id) {
10597         var n = this.delegate.graph.getNode(id);
10598         n.setData('border', s);
10599         var link = index === 0? 'prev':'next';
10600         link = n.getData(link);
10601         if(link) {
10602           n = this.delegate.graph.getByName(link);
10603           if(n) {
10604             n.setData('border', {
10605               name: name,
10606               index: 1-index
10607             });
10608           }
10609         }
10610       }
10611       this.delegate.plot();
10612     }
10613   },
10614   
10615   /*
10616     Method: getLegend
10617    
10618     Returns an object containing as keys the legend names and as values hex strings with color values.
10619     
10620     Example:
10621     
10622     (start code js)
10623     var legend = areaChart.getLegend();
10624     (end code)
10625  */  
10626   getLegend: function() {
10627     var legend = {};
10628     var n;
10629     this.delegate.graph.getNode(this.delegate.root).eachAdjacency(function(adj) {
10630       n = adj.nodeTo;
10631     });
10632     var colors = n.getData('colorArray'),
10633         len = colors.length;
10634     $.each(n.getData('stringArray'), function(s, i) {
10635       legend[s] = colors[i % len];
10636     });
10637     return legend;
10638   },
10639   
10640   /*
10641     Method: getMaxValue
10642    
10643     Returns the maximum accumulated value for the stacks. This method is used for normalizing the graph heights according to the canvas height.
10644     
10645     Example:
10646     
10647     (start code js)
10648     var ans = areaChart.getMaxValue();
10649     (end code)
10650     
10651     In some cases it could be useful to override this method to normalize heights for a group of AreaCharts, like when doing small multiples.
10652     
10653     Example:
10654     
10655     (start code js)
10656     //will return 100 for all AreaChart instances,
10657     //displaying all of them with the same scale
10658     $jit.AreaChart.implement({
10659       'getMaxValue': function() {
10660         return 100;
10661       }
10662     });
10663     (end code)
10664     
10665 */  
10666   getMaxValue: function() {
10667     var maxValue = 0;
10668     this.delegate.graph.eachNode(function(n) {
10669       var valArray = n.getData('valueArray'),
10670           acumLeft = 0, acumRight = 0;
10671       $.each(valArray, function(v) { 
10672         acumLeft += +v[0];
10673         acumRight += +v[1];
10674       });
10675       var acum = acumRight>acumLeft? acumRight:acumLeft;
10676       maxValue = maxValue>acum? maxValue:acum;
10677     });
10678     return maxValue;
10679   },
10680   
10681   normalizeDims: function() {
10682     //number of elements
10683     var root = this.delegate.graph.getNode(this.delegate.root), l=0;
10684     root.eachAdjacency(function() {
10685       l++;
10686     });
10687     var maxValue = this.getMaxValue() || 1,
10688         size = this.delegate.canvas.getSize(),
10689         config = this.config,
10690         margin = config.Margin,
10691         labelOffset = config.labelOffset + config.Label.size,
10692         fixedDim = (size.width - (margin.left + margin.right)) / l,
10693         animate = config.animate,
10694         height = size.height - (margin.top + margin.bottom) - (config.showAggregates && labelOffset) 
10695           - (config.showLabels && labelOffset);
10696     this.delegate.graph.eachNode(function(n) {
10697       var acumLeft = 0, acumRight = 0, animateValue = [];
10698       $.each(n.getData('valueArray'), function(v) {
10699         acumLeft += +v[0];
10700         acumRight += +v[1];
10701         animateValue.push([0, 0]);
10702       });
10703       var acum = acumRight>acumLeft? acumRight:acumLeft;
10704       n.setData('width', fixedDim);
10705       if(animate) {
10706         n.setData('height', acum * height / maxValue, 'end');
10707         n.setData('dimArray', $.map(n.getData('valueArray'), function(n) { 
10708           return [n[0] * height / maxValue, n[1] * height / maxValue]; 
10709         }), 'end');
10710         var dimArray = n.getData('dimArray');
10711         if(!dimArray) {
10712           n.setData('dimArray', animateValue);
10713         }
10714       } else {
10715         n.setData('height', acum * height / maxValue);
10716         n.setData('dimArray', $.map(n.getData('valueArray'), function(n) { 
10717           return [n[0] * height / maxValue, n[1] * height / maxValue]; 
10718         }));
10719       }
10720     });
10721   }
10722 });
10723
10724
10725 /*
10726  * File: Options.BarChart.js
10727  *
10728 */
10729
10730 /*
10731   Object: Options.BarChart
10732   
10733   <BarChart> options. 
10734   Other options included in the BarChart are <Options.Canvas>, <Options.Label>, <Options.Margin>, <Options.Tips> and <Options.Events>.
10735   
10736   Syntax:
10737   
10738   (start code js)
10739
10740   Options.BarChart = {
10741     animate: true,
10742     labelOffset: 3,
10743     barsOffset: 0,
10744     type: 'stacked',
10745     hoveredColor: '#9fd4ff',
10746     orientation: 'horizontal',
10747     showAggregates: true,
10748     showLabels: true
10749   };
10750   
10751   (end code)
10752   
10753   Example:
10754   
10755   (start code js)
10756
10757   var barChart = new $jit.BarChart({
10758     animate: true,
10759     barsOffset: 10,
10760     type: 'stacked:gradient'
10761   });
10762   
10763   (end code)
10764
10765   Parameters:
10766   
10767   animate - (boolean) Default's *true*. Whether to add animated transitions when filtering/restoring stacks.
10768   offset - (number) Default's *25*. Adds margin between the visualization and the canvas.
10769   labelOffset - (number) Default's *3*. Adds margin between the label and the default place where it should be drawn.
10770   barsOffset - (number) Default's *0*. Separation between bars.
10771   type - (string) Default's *'stacked'*. Stack or grouped styles. Posible values are 'stacked', 'grouped', 'stacked:gradient', 'grouped:gradient' to add gradients.
10772   hoveredColor - (boolean|string) Default's *'#9fd4ff'*. Sets the selected color for a hovered bar stack.
10773   orientation - (string) Default's 'horizontal'. Sets the direction of the bars. Possible options are 'vertical' or 'horizontal'.
10774   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.
10775   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.
10776   
10777 */
10778
10779 Options.BarChart = {
10780   $extend: true,
10781   
10782   animate: true,
10783   type: 'stacked', //stacked, grouped, : gradient
10784   labelOffset: 3, //label offset
10785   barsOffset: 0, //distance between bars
10786   hoveredColor: '#9fd4ff',
10787   orientation: 'horizontal',
10788   showAggregates: true,
10789   showLabels: true,
10790   Tips: {
10791     enable: false,
10792     onShow: $.empty,
10793     onHide: $.empty
10794   },
10795   Events: {
10796     enable: false,
10797     onClick: $.empty
10798   }
10799 };
10800
10801 /*
10802  * File: BarChart.js
10803  *
10804 */
10805
10806 $jit.ST.Plot.NodeTypes.implement({
10807   'barchart-stacked' : {
10808     'render' : function(node, canvas) {
10809       var pos = node.pos.getc(true), 
10810           width = node.getData('width'),
10811           height = node.getData('height'),
10812           algnPos = this.getAlignedPos(pos, width, height),
10813           x = algnPos.x, y = algnPos.y,
10814           dimArray = node.getData('dimArray'),
10815           valueArray = node.getData('valueArray'),
10816           colorArray = node.getData('colorArray'),
10817           colorLength = colorArray.length,
10818           stringArray = node.getData('stringArray');
10819
10820       var ctx = canvas.getCtx(),
10821           opt = {},
10822           border = node.getData('border'),
10823           gradient = node.getData('gradient'),
10824           config = node.getData('config'),
10825           horz = config.orientation == 'horizontal',
10826           aggregates = config.showAggregates,
10827           showLabels = config.showLabels,
10828           label = config.Label;
10829       
10830       if (colorArray && dimArray && stringArray) {
10831         for (var i=0, l=dimArray.length, acum=0, valAcum=0; i<l; i++) {
10832           ctx.fillStyle = ctx.strokeStyle = colorArray[i % colorLength];
10833           if(gradient) {
10834             var linear;
10835             if(horz) {
10836               linear = ctx.createLinearGradient(x + acum + dimArray[i]/2, y, 
10837                   x + acum + dimArray[i]/2, y + height);
10838             } else {
10839               linear = ctx.createLinearGradient(x, y - acum - dimArray[i]/2, 
10840                   x + width, y - acum- dimArray[i]/2);
10841             }
10842             var color = $.rgbToHex($.map($.hexToRgb(colorArray[i % colorLength].slice(1)), 
10843                 function(v) { return (v * 0.5) >> 0; }));
10844             linear.addColorStop(0, color);
10845             linear.addColorStop(0.5, colorArray[i % colorLength]);
10846             linear.addColorStop(1, color);
10847             ctx.fillStyle = linear;
10848           }
10849           if(horz) {
10850             ctx.fillRect(x + acum, y, dimArray[i], height);
10851           } else {
10852             ctx.fillRect(x, y - acum - dimArray[i], width, dimArray[i]);
10853           }
10854           if(border && border.name == stringArray[i]) {
10855             opt.acum = acum;
10856             opt.dimValue = dimArray[i];
10857           }
10858           acum += (dimArray[i] || 0);
10859           valAcum += (valueArray[i] || 0);
10860         }
10861         if(border) {
10862           ctx.save();
10863           ctx.lineWidth = 2;
10864           ctx.strokeStyle = border.color;
10865           if(horz) {
10866             ctx.strokeRect(x + opt.acum + 1, y + 1, opt.dimValue -2, height - 2);
10867           } else {
10868             ctx.strokeRect(x + 1, y - opt.acum - opt.dimValue + 1, width -2, opt.dimValue -2);
10869           }
10870           ctx.restore();
10871         }
10872         if(label.type == 'Native') {
10873           ctx.save();
10874           ctx.fillStyle = ctx.strokeStyle = label.color;
10875           ctx.font = label.style + ' ' + label.size + 'px ' + label.family;
10876           ctx.textBaseline = 'middle';
10877           var aggValue = aggregates(node.name, valAcum, node);
10878           if(aggValue !== false) {
10879             aggValue = aggValue !== true? aggValue : valAcum;
10880             if(horz) {
10881               ctx.textAlign = 'right';
10882               ctx.fillText(aggValue, x + acum - config.labelOffset, y + height/2);
10883             } else {
10884               ctx.textAlign = 'center';
10885               ctx.fillText(aggValue, x + width/2, y - height - label.size/2 - config.labelOffset);
10886             }
10887           }
10888           if(showLabels(node.name, valAcum, node)) {
10889             if(horz) {
10890               ctx.textAlign = 'center';
10891               ctx.translate(x - config.labelOffset - label.size/2, y + height/2);
10892               ctx.rotate(Math.PI / 2);
10893               ctx.fillText(node.name, 0, 0);
10894             } else {
10895               ctx.textAlign = 'center';
10896               ctx.fillText(node.name, x + width/2, y + label.size/2 + config.labelOffset);
10897             }
10898           }
10899           ctx.restore();
10900         }
10901       }
10902     },
10903     'contains': function(node, mpos) {
10904       var pos = node.pos.getc(true), 
10905           width = node.getData('width'),
10906           height = node.getData('height'),
10907           algnPos = this.getAlignedPos(pos, width, height),
10908           x = algnPos.x, y = algnPos.y,
10909           dimArray = node.getData('dimArray'),
10910           config = node.getData('config'),
10911           rx = mpos.x - x,
10912           horz = config.orientation == 'horizontal';
10913       //bounding box check
10914       if(horz) {
10915         if(mpos.x < x || mpos.x > x + width
10916             || mpos.y > y + height || mpos.y < y) {
10917             return false;
10918           }
10919       } else {
10920         if(mpos.x < x || mpos.x > x + width
10921             || mpos.y > y || mpos.y < y - height) {
10922             return false;
10923           }
10924       }
10925       //deep check
10926       for(var i=0, l=dimArray.length, acum=(horz? x:y); i<l; i++) {
10927         var dimi = dimArray[i];
10928         if(horz) {
10929           acum += dimi;
10930           var intersec = acum;
10931           if(mpos.x <= intersec) {
10932             return {
10933               'name': node.getData('stringArray')[i],
10934               'color': node.getData('colorArray')[i],
10935               'value': node.getData('valueArray')[i],
10936               'label': node.name
10937             };
10938           }
10939         } else {
10940           acum -= dimi;
10941           var intersec = acum;
10942           if(mpos.y >= intersec) {
10943             return {
10944               'name': node.getData('stringArray')[i],
10945               'color': node.getData('colorArray')[i],
10946               'value': node.getData('valueArray')[i],
10947               'label': node.name
10948             };
10949           }
10950         }
10951       }
10952       return false;
10953     }
10954   },
10955   'barchart-grouped' : {
10956     'render' : function(node, canvas) {
10957       var pos = node.pos.getc(true), 
10958           width = node.getData('width'),
10959           height = node.getData('height'),
10960           algnPos = this.getAlignedPos(pos, width, height),
10961           x = algnPos.x, y = algnPos.y,
10962           dimArray = node.getData('dimArray'),
10963           valueArray = node.getData('valueArray'),
10964           valueLength = valueArray.length,
10965           colorArray = node.getData('colorArray'),
10966           colorLength = colorArray.length,
10967           stringArray = node.getData('stringArray'); 
10968
10969       var ctx = canvas.getCtx(),
10970           opt = {},
10971           border = node.getData('border'),
10972           gradient = node.getData('gradient'),
10973           config = node.getData('config'),
10974           horz = config.orientation == 'horizontal',
10975           aggregates = config.showAggregates,
10976           showLabels = config.showLabels,
10977           label = config.Label,
10978           fixedDim = (horz? height : width) / valueLength;
10979       
10980       if (colorArray && dimArray && stringArray) {
10981         for (var i=0, l=valueLength, acum=0, valAcum=0; i<l; i++) {
10982           ctx.fillStyle = ctx.strokeStyle = colorArray[i % colorLength];
10983           if(gradient) {
10984             var linear;
10985             if(horz) {
10986               linear = ctx.createLinearGradient(x + dimArray[i]/2, y + fixedDim * i, 
10987                   x + dimArray[i]/2, y + fixedDim * (i + 1));
10988             } else {
10989               linear = ctx.createLinearGradient(x + fixedDim * i, y - dimArray[i]/2, 
10990                   x + fixedDim * (i + 1), y - dimArray[i]/2);
10991             }
10992             var color = $.rgbToHex($.map($.hexToRgb(colorArray[i % colorLength].slice(1)), 
10993                 function(v) { return (v * 0.5) >> 0; }));
10994             linear.addColorStop(0, color);
10995             linear.addColorStop(0.5, colorArray[i % colorLength]);
10996             linear.addColorStop(1, color);
10997             ctx.fillStyle = linear;
10998           }
10999           if(horz) {
11000             ctx.fillRect(x, y + fixedDim * i, dimArray[i], fixedDim);
11001           } else {
11002             ctx.fillRect(x + fixedDim * i, y - dimArray[i], fixedDim, dimArray[i]);
11003           }
11004           if(border && border.name == stringArray[i]) {
11005             opt.acum = fixedDim * i;
11006             opt.dimValue = dimArray[i];
11007           }
11008           acum += (dimArray[i] || 0);
11009           valAcum += (valueArray[i] || 0);
11010         }
11011         if(border) {
11012           ctx.save();
11013           ctx.lineWidth = 2;
11014           ctx.strokeStyle = border.color;
11015           if(horz) {
11016             ctx.strokeRect(x + 1, y + opt.acum + 1, opt.dimValue -2, fixedDim - 2);
11017           } else {
11018             ctx.strokeRect(x + opt.acum + 1, y - opt.dimValue + 1, fixedDim -2, opt.dimValue -2);
11019           }
11020           ctx.restore();
11021         }
11022         if(label.type == 'Native') {
11023           ctx.save();
11024           ctx.fillStyle = ctx.strokeStyle = label.color;
11025           ctx.font = label.style + ' ' + label.size + 'px ' + label.family;
11026           ctx.textBaseline = 'middle';
11027           var aggValue = aggregates(node.name, valAcum, node);
11028           if(aggValue !== false) {
11029             aggValue = aggValue !== true? aggValue : valAcum;
11030             if(horz) {
11031               ctx.textAlign = 'right';
11032               ctx.fillText(aggValue, x + Math.max.apply(null, dimArray) - config.labelOffset, y + height/2);
11033             } else {
11034               ctx.textAlign = 'center';
11035               ctx.fillText(aggValue, x + width/2, y - Math.max.apply(null, dimArray) - label.size/2 - config.labelOffset);
11036             }
11037           }
11038           if(showLabels(node.name, valAcum, node)) {
11039             if(horz) {
11040               ctx.textAlign = 'center';
11041               ctx.translate(x - config.labelOffset - label.size/2, y + height/2);
11042               ctx.rotate(Math.PI / 2);
11043               ctx.fillText(node.name, 0, 0);
11044             } else {
11045               ctx.textAlign = 'center';
11046               ctx.fillText(node.name, x + width/2, y + label.size/2 + config.labelOffset);
11047             }
11048           }
11049           ctx.restore();
11050         }
11051       }
11052     },
11053     'contains': function(node, mpos) {
11054       var pos = node.pos.getc(true), 
11055           width = node.getData('width'),
11056           height = node.getData('height'),
11057           algnPos = this.getAlignedPos(pos, width, height),
11058           x = algnPos.x, y = algnPos.y,
11059           dimArray = node.getData('dimArray'),
11060           len = dimArray.length,
11061           config = node.getData('config'),
11062           rx = mpos.x - x,
11063           horz = config.orientation == 'horizontal',
11064           fixedDim = (horz? height : width) / len;
11065       //bounding box check
11066       if(horz) {
11067         if(mpos.x < x || mpos.x > x + width
11068             || mpos.y > y + height || mpos.y < y) {
11069             return false;
11070           }
11071       } else {
11072         if(mpos.x < x || mpos.x > x + width
11073             || mpos.y > y || mpos.y < y - height) {
11074             return false;
11075           }
11076       }
11077       //deep check
11078       for(var i=0, l=dimArray.length; i<l; i++) {
11079         var dimi = dimArray[i];
11080         if(horz) {
11081           var limit = y + fixedDim * i;
11082           if(mpos.x <= x+ dimi && mpos.y >= limit && mpos.y <= limit + fixedDim) {
11083             return {
11084               'name': node.getData('stringArray')[i],
11085               'color': node.getData('colorArray')[i],
11086               'value': node.getData('valueArray')[i],
11087               'label': node.name
11088             };
11089           }
11090         } else {
11091           var limit = x + fixedDim * i;
11092           if(mpos.x >= limit && mpos.x <= limit + fixedDim && mpos.y >= y - dimi) {
11093             return {
11094               'name': node.getData('stringArray')[i],
11095               'color': node.getData('colorArray')[i],
11096               'value': node.getData('valueArray')[i],
11097               'label': node.name
11098             };
11099           }
11100         }
11101       }
11102       return false;
11103     }
11104   }
11105 });
11106
11107 /*
11108   Class: BarChart
11109   
11110   A visualization that displays stacked bar charts.
11111   
11112   Constructor Options:
11113   
11114   See <Options.BarChart>.
11115
11116 */
11117 $jit.BarChart = new Class({
11118   st: null,
11119   colors: ["#416D9C", "#70A35E", "#EBB056", "#C74243", "#83548B", "#909291", "#557EAA"],
11120   selected: {},
11121   busy: false,
11122   
11123   initialize: function(opt) {
11124     this.controller = this.config = 
11125       $.merge(Options("Canvas", "Margin", "Label", "BarChart"), {
11126         Label: { type: 'Native' }
11127       }, opt);
11128     //set functions for showLabels and showAggregates
11129     var showLabels = this.config.showLabels,
11130         typeLabels = $.type(showLabels),
11131         showAggregates = this.config.showAggregates,
11132         typeAggregates = $.type(showAggregates);
11133     this.config.showLabels = typeLabels == 'function'? showLabels : $.lambda(showLabels);
11134     this.config.showAggregates = typeAggregates == 'function'? showAggregates : $.lambda(showAggregates);
11135     
11136     this.initializeViz();
11137   },
11138   
11139   initializeViz: function() {
11140     var config = this.config, that = this;
11141     var nodeType = config.type.split(":")[0],
11142         horz = config.orientation == 'horizontal',
11143         nodeLabels = {};
11144     
11145     var delegate = new $jit.ST({
11146       injectInto: config.injectInto,
11147       width: config.width,
11148       height: config.height,
11149       orientation: horz? 'left' : 'bottom',
11150       levelDistance: 0,
11151       siblingOffset: config.barsOffset,
11152       subtreeOffset: 0,
11153       withLabels: config.Label.type != 'Native',      
11154       useCanvas: config.useCanvas,
11155       Label: {
11156         type: config.Label.type
11157       },
11158       Node: {
11159         overridable: true,
11160         type: 'barchart-' + nodeType,
11161         align: 'left',
11162         width: 1,
11163         height: 1
11164       },
11165       Edge: {
11166         type: 'none'
11167       },
11168       Tips: {
11169         enable: config.Tips.enable,
11170         type: 'Native',
11171         force: true,
11172         onShow: function(tip, node, contains) {
11173           var elem = contains;
11174           config.Tips.onShow(tip, elem, node);
11175         }
11176       },
11177       Events: {
11178         enable: true,
11179         type: 'Native',
11180         onClick: function(node, eventInfo, evt) {
11181           if(!config.Events.enable) return;
11182           var elem = eventInfo.getContains();
11183           config.Events.onClick(elem, eventInfo, evt);
11184         },
11185         onMouseMove: function(node, eventInfo, evt) {
11186           if(!config.hoveredColor) return;
11187           if(node) {
11188             var elem = eventInfo.getContains();
11189             that.select(node.id, elem.name, elem.index);
11190           } else {
11191             that.select(false, false, false);
11192           }
11193         }
11194       },
11195       onCreateLabel: function(domElement, node) {
11196         var labelConf = config.Label,
11197             valueArray = node.getData('valueArray'),
11198             acum = $.reduce(valueArray, function(x, y) { return x + y; }, 0);
11199         var nlbs = {
11200           wrapper: document.createElement('div'),
11201           aggregate: document.createElement('div'),
11202           label: document.createElement('div')
11203         };
11204         var wrapper = nlbs.wrapper,
11205             label = nlbs.label,
11206             aggregate = nlbs.aggregate,
11207             wrapperStyle = wrapper.style,
11208             labelStyle = label.style,
11209             aggregateStyle = aggregate.style;
11210         //store node labels
11211         nodeLabels[node.id] = nlbs;
11212         //append labels
11213         wrapper.appendChild(label);
11214         wrapper.appendChild(aggregate);
11215         if(!config.showLabels(node.name, acum, node)) {
11216           labelStyle.display = 'none';
11217         }
11218         if(!config.showAggregates(node.name, acum, node)) {
11219           aggregateStyle.display = 'none';
11220         }
11221         wrapperStyle.position = 'relative';
11222         wrapperStyle.overflow = 'visible';
11223         wrapperStyle.fontSize = labelConf.size + 'px';
11224         wrapperStyle.fontFamily = labelConf.family;
11225         wrapperStyle.color = labelConf.color;
11226         wrapperStyle.textAlign = 'center';
11227         aggregateStyle.position = labelStyle.position = 'absolute';
11228         
11229         domElement.style.width = node.getData('width') + 'px';
11230         domElement.style.height = node.getData('height') + 'px';
11231         aggregateStyle.left = labelStyle.left =  '0px';
11232
11233         label.innerHTML = node.name;
11234         
11235         domElement.appendChild(wrapper);
11236       },
11237       onPlaceLabel: function(domElement, node) {
11238         if(!nodeLabels[node.id]) return;
11239         var labels = nodeLabels[node.id],
11240             wrapperStyle = labels.wrapper.style,
11241             labelStyle = labels.label.style,
11242             aggregateStyle = labels.aggregate.style,
11243             grouped = config.type.split(':')[0] == 'grouped',
11244             horz = config.orientation == 'horizontal',
11245             dimArray = node.getData('dimArray'),
11246             valArray = node.getData('valueArray'),
11247             width = (grouped && horz)? Math.max.apply(null, dimArray) : node.getData('width'),
11248             height = (grouped && !horz)? Math.max.apply(null, dimArray) : node.getData('height'),
11249             font = parseInt(wrapperStyle.fontSize, 10),
11250             domStyle = domElement.style;
11251             
11252         
11253         if(dimArray && valArray) {
11254           wrapperStyle.width = aggregateStyle.width = labelStyle.width = domElement.style.width = width + 'px';
11255           for(var i=0, l=valArray.length, acum=0; i<l; i++) {
11256             if(dimArray[i] > 0) {
11257               acum+= valArray[i];
11258             }
11259           }
11260           if(config.showLabels(node.name, acum, node)) {
11261             labelStyle.display = '';
11262           } else {
11263             labelStyle.display = 'none';
11264           }
11265           var aggValue = config.showAggregates(node.name, acum, node);
11266           if(aggValue !== false) {
11267             aggregateStyle.display = '';
11268           } else {
11269             aggregateStyle.display = 'none';
11270           }
11271           if(config.orientation == 'horizontal') {
11272             aggregateStyle.textAlign = 'right';
11273             labelStyle.textAlign = 'left';
11274             labelStyle.textIndex = aggregateStyle.textIndent = config.labelOffset + 'px';
11275             aggregateStyle.top = labelStyle.top = (height-font)/2 + 'px';
11276             domElement.style.height = wrapperStyle.height = height + 'px';
11277           } else {
11278             aggregateStyle.top = (-font - config.labelOffset) + 'px';
11279             labelStyle.top = (config.labelOffset + height) + 'px';
11280             domElement.style.top = parseInt(domElement.style.top, 10) - height + 'px';
11281             domElement.style.height = wrapperStyle.height = height + 'px';
11282           }
11283           labels.aggregate.innerHTML = aggValue !== true? aggValue : acum;
11284         }
11285       }
11286     });
11287     
11288     var size = delegate.canvas.getSize(),
11289         margin = config.Margin;
11290     if(horz) {
11291       delegate.config.offsetX = size.width/2 - margin.left
11292         - (config.showLabels && (config.labelOffset + config.Label.size));    
11293       delegate.config.offsetY = (margin.bottom - margin.top)/2;
11294     } else {
11295       delegate.config.offsetY = -size.height/2 + margin.bottom 
11296         + (config.showLabels && (config.labelOffset + config.Label.size));
11297       delegate.config.offsetX = (margin.right - margin.left)/2;
11298     }
11299     this.delegate = delegate;
11300     this.canvas = this.delegate.canvas;
11301   },
11302   
11303   /*
11304     Method: loadJSON
11305    
11306     Loads JSON data into the visualization. 
11307     
11308     Parameters:
11309     
11310     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>.
11311     
11312     Example:
11313     (start code js)
11314     var barChart = new $jit.BarChart(options);
11315     barChart.loadJSON(json);
11316     (end code)
11317  */  
11318   loadJSON: function(json) {
11319     if(this.busy) return;
11320     this.busy = true;
11321     
11322     var prefix = $.time(), 
11323         ch = [], 
11324         delegate = this.delegate,
11325         name = $.splat(json.label), 
11326         color = $.splat(json.color || this.colors),
11327         config = this.config,
11328         gradient = !!config.type.split(":")[1],
11329         animate = config.animate,
11330         horz = config.orientation == 'horizontal',
11331         that = this;
11332     
11333     for(var i=0, values=json.values, l=values.length; i<l; i++) {
11334       var val = values[i]
11335       var valArray = $.splat(values[i].values);
11336       var acum = 0;
11337       ch.push({
11338         'id': prefix + val.label,
11339         'name': val.label,
11340         'data': {
11341           'value': valArray,
11342           '$valueArray': valArray,
11343           '$colorArray': color,
11344           '$stringArray': name,
11345           '$gradient': gradient,
11346           '$config': config
11347         },
11348         'children': []
11349       });
11350     }
11351     var root = {
11352       'id': prefix + '$root',
11353       'name': '',
11354       'data': {
11355         '$type': 'none',
11356         '$width': 1,
11357         '$height': 1
11358       },
11359       'children': ch
11360     };
11361     delegate.loadJSON(root);
11362     
11363     this.normalizeDims();
11364     delegate.compute();
11365     delegate.select(delegate.root);
11366     if(animate) {
11367       if(horz) {
11368         delegate.fx.animate({
11369           modes: ['node-property:width:dimArray'],
11370           duration:1500,
11371           onComplete: function() {
11372             that.busy = false;
11373           }
11374         });
11375       } else {
11376         delegate.fx.animate({
11377           modes: ['node-property:height:dimArray'],
11378           duration:1500,
11379           onComplete: function() {
11380             that.busy = false;
11381           }
11382         });
11383       }
11384     } else {
11385       this.busy = false;
11386     }
11387   },
11388   
11389   /*
11390     Method: updateJSON
11391    
11392     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.
11393     
11394     Parameters:
11395     
11396     json - (object) JSON data to be updated. The JSON format corresponds to the one described in <BarChart.loadJSON>.
11397     onComplete - (object) A callback object to be called when the animation transition when updating the data end.
11398     
11399     Example:
11400     
11401     (start code js)
11402     barChart.updateJSON(json, {
11403       onComplete: function() {
11404         alert('update complete!');
11405       }
11406     });
11407     (end code)
11408  */  
11409   updateJSON: function(json, onComplete) {
11410     if(this.busy) return;
11411     this.busy = true;
11412     this.select(false, false, false);
11413     var delegate = this.delegate;
11414     var graph = delegate.graph;
11415     var values = json.values;
11416     var animate = this.config.animate;
11417     var that = this;
11418     var horz = this.config.orientation == 'horizontal';
11419     $.each(values, function(v) {
11420       var n = graph.getByName(v.label);
11421       if(n) {
11422         n.setData('valueArray', $.splat(v.values));
11423         if(json.label) {
11424           n.setData('stringArray', $.splat(json.label));
11425         }
11426       }
11427     });
11428     this.normalizeDims();
11429     delegate.compute();
11430     delegate.select(delegate.root);
11431     if(animate) {
11432       if(horz) {
11433         delegate.fx.animate({
11434           modes: ['node-property:width:dimArray'],
11435           duration:1500,
11436           onComplete: function() {
11437             that.busy = false;
11438             onComplete && onComplete.onComplete();
11439           }
11440         });
11441       } else {
11442         delegate.fx.animate({
11443           modes: ['node-property:height:dimArray'],
11444           duration:1500,
11445           onComplete: function() {
11446             that.busy = false;
11447             onComplete && onComplete.onComplete();
11448           }
11449         });
11450       }
11451     }
11452   },
11453   
11454   //adds the little brown bar when hovering the node
11455   select: function(id, name) {
11456     if(!this.config.hoveredColor) return;
11457     var s = this.selected;
11458     if(s.id != id || s.name != name) {
11459       s.id = id;
11460       s.name = name;
11461       s.color = this.config.hoveredColor;
11462       this.delegate.graph.eachNode(function(n) {
11463         if(id == n.id) {
11464           n.setData('border', s);
11465         } else {
11466           n.setData('border', false);
11467         }
11468       });
11469       this.delegate.plot();
11470     }
11471   },
11472   
11473   /*
11474     Method: getLegend
11475    
11476     Returns an object containing as keys the legend names and as values hex strings with color values.
11477     
11478     Example:
11479     
11480     (start code js)
11481     var legend = barChart.getLegend();
11482     (end code)
11483   */  
11484   getLegend: function() {
11485     var legend = {};
11486     var n;
11487     this.delegate.graph.getNode(this.delegate.root).eachAdjacency(function(adj) {
11488       n = adj.nodeTo;
11489     });
11490     var colors = n.getData('colorArray'),
11491         len = colors.length;
11492     $.each(n.getData('stringArray'), function(s, i) {
11493       legend[s] = colors[i % len];
11494     });
11495     return legend;
11496   },
11497   
11498   /*
11499     Method: getMaxValue
11500    
11501     Returns the maximum accumulated value for the stacks. This method is used for normalizing the graph heights according to the canvas height.
11502     
11503     Example:
11504     
11505     (start code js)
11506     var ans = barChart.getMaxValue();
11507     (end code)
11508     
11509     In some cases it could be useful to override this method to normalize heights for a group of BarCharts, like when doing small multiples.
11510     
11511     Example:
11512     
11513     (start code js)
11514     //will return 100 for all BarChart instances,
11515     //displaying all of them with the same scale
11516     $jit.BarChart.implement({
11517       'getMaxValue': function() {
11518         return 100;
11519       }
11520     });
11521     (end code)
11522     
11523   */  
11524   getMaxValue: function() {
11525     var maxValue = 0, stacked = this.config.type.split(':')[0] == 'stacked';
11526     this.delegate.graph.eachNode(function(n) {
11527       var valArray = n.getData('valueArray'),
11528           acum = 0;
11529       if(!valArray) return;
11530       if(stacked) {
11531         $.each(valArray, function(v) { 
11532           acum += +v;
11533         });
11534       } else {
11535         acum = Math.max.apply(null, valArray);
11536       }
11537       maxValue = maxValue>acum? maxValue:acum;
11538     });
11539     return maxValue;
11540   },
11541   
11542   setBarType: function(type) {
11543     this.config.type = type;
11544     this.delegate.config.Node.type = 'barchart-' + type.split(':')[0];
11545   },
11546   
11547   normalizeDims: function() {
11548     //number of elements
11549     var root = this.delegate.graph.getNode(this.delegate.root), l=0;
11550     root.eachAdjacency(function() {
11551       l++;
11552     });
11553     var maxValue = this.getMaxValue() || 1,
11554         size = this.delegate.canvas.getSize(),
11555         config = this.config,
11556         margin = config.Margin,
11557         marginWidth = margin.left + margin.right,
11558         marginHeight = margin.top + margin.bottom,
11559         horz = config.orientation == 'horizontal',
11560         fixedDim = (size[horz? 'height':'width'] - (horz? marginHeight:marginWidth) - (l -1) * config.barsOffset) / l,
11561         animate = config.animate,
11562         height = size[horz? 'width':'height'] - (horz? marginWidth:marginHeight) 
11563           - (!horz && config.showAggregates && (config.Label.size + config.labelOffset))
11564           - (config.showLabels && (config.Label.size + config.labelOffset)),
11565         dim1 = horz? 'height':'width',
11566         dim2 = horz? 'width':'height';
11567     this.delegate.graph.eachNode(function(n) {
11568       var acum = 0, animateValue = [];
11569       $.each(n.getData('valueArray'), function(v) {
11570         acum += +v;
11571         animateValue.push(0);
11572       });
11573       n.setData(dim1, fixedDim);
11574       if(animate) {
11575         n.setData(dim2, acum * height / maxValue, 'end');
11576         n.setData('dimArray', $.map(n.getData('valueArray'), function(n) { 
11577           return n * height / maxValue; 
11578         }), 'end');
11579         var dimArray = n.getData('dimArray');
11580         if(!dimArray) {
11581           n.setData('dimArray', animateValue);
11582         }
11583       } else {
11584         n.setData(dim2, acum * height / maxValue);
11585         n.setData('dimArray', $.map(n.getData('valueArray'), function(n) { 
11586           return n * height / maxValue; 
11587         }));
11588       }
11589     });
11590   }
11591 });
11592
11593
11594 /*
11595  * File: Options.PieChart.js
11596  *
11597 */
11598 /*
11599   Object: Options.PieChart
11600   
11601   <PieChart> options. 
11602   Other options included in the PieChart are <Options.Canvas>, <Options.Label>, <Options.Tips> and <Options.Events>.
11603   
11604   Syntax:
11605   
11606   (start code js)
11607
11608   Options.PieChart = {
11609     animate: true,
11610     offset: 25,
11611     sliceOffset:0,
11612     labelOffset: 3,
11613     type: 'stacked',
11614     hoveredColor: '#9fd4ff',
11615     showLabels: true,
11616     resizeLabels: false,
11617     updateHeights: false
11618   };  
11619
11620   (end code)
11621   
11622   Example:
11623   
11624   (start code js)
11625
11626   var pie = new $jit.PieChart({
11627     animate: true,
11628     sliceOffset: 5,
11629     type: 'stacked:gradient'
11630   });  
11631
11632   (end code)
11633   
11634   Parameters:
11635   
11636   animate - (boolean) Default's *true*. Whether to add animated transitions when plotting/updating the visualization.
11637   offset - (number) Default's *25*. Adds margin between the visualization and the canvas.
11638   sliceOffset - (number) Default's *0*. Separation between the center of the canvas and each pie slice.
11639   labelOffset - (number) Default's *3*. Adds margin between the label and the default place where it should be drawn.
11640   type - (string) Default's *'stacked'*. Stack style. Posible values are 'stacked', 'stacked:gradient' to add gradients.
11641   hoveredColor - (boolean|string) Default's *'#9fd4ff'*. Sets the selected color for a hovered pie stack.
11642   showLabels - (boolean) Default's *true*. Display the name of the slots.
11643   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.
11644   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.
11645
11646 */
11647 Options.PieChart = {
11648   $extend: true,
11649
11650   animate: true,
11651   offset: 25, // page offset
11652   sliceOffset:0,
11653   labelOffset: 3, // label offset
11654   type: 'stacked', // gradient
11655   hoveredColor: '#9fd4ff',
11656   Events: {
11657     enable: false,
11658     onClick: $.empty
11659   },
11660   Tips: {
11661     enable: false,
11662     onShow: $.empty,
11663     onHide: $.empty
11664   },
11665   showLabels: true,
11666   resizeLabels: false,
11667   
11668   //only valid for mono-valued datasets
11669   updateHeights: false
11670 };
11671
11672 /*
11673  * Class: Layouts.Radial
11674  * 
11675  * Implements a Radial Layout.
11676  * 
11677  * Implemented By:
11678  * 
11679  * <RGraph>, <Hypertree>
11680  * 
11681  */
11682 Layouts.Radial = new Class({
11683
11684   /*
11685    * Method: compute
11686    * 
11687    * Computes nodes' positions.
11688    * 
11689    * Parameters:
11690    * 
11691    * property - _optional_ A <Graph.Node> position property to store the new
11692    * positions. Possible values are 'pos', 'end' or 'start'.
11693    * 
11694    */
11695   compute : function(property) {
11696     var prop = $.splat(property || [ 'current', 'start', 'end' ]);
11697     NodeDim.compute(this.graph, prop, this.config);
11698     this.graph.computeLevels(this.root, 0, "ignore");
11699     var lengthFunc = this.createLevelDistanceFunc(); 
11700     this.computeAngularWidths(prop);
11701     this.computePositions(prop, lengthFunc);
11702   },
11703
11704   /*
11705    * computePositions
11706    * 
11707    * Performs the main algorithm for computing node positions.
11708    */
11709   computePositions : function(property, getLength) {
11710     var propArray = property;
11711     var graph = this.graph;
11712     var root = graph.getNode(this.root);
11713     var parent = this.parent;
11714     var config = this.config;
11715
11716     for ( var i=0, l=propArray.length; i < l; i++) {
11717       var pi = propArray[i];
11718       root.setPos($P(0, 0), pi);
11719       root.setData('span', Math.PI * 2, pi);
11720     }
11721
11722     root.angleSpan = {
11723       begin : 0,
11724       end : 2 * Math.PI
11725     };
11726
11727     graph.eachBFS(this.root, function(elem) {
11728       var angleSpan = elem.angleSpan.end - elem.angleSpan.begin;
11729       var angleInit = elem.angleSpan.begin;
11730       var len = getLength(elem);
11731       //Calculate the sum of all angular widths
11732       var totalAngularWidths = 0, subnodes = [], maxDim = {};
11733       elem.eachSubnode(function(sib) {
11734         totalAngularWidths += sib._treeAngularWidth;
11735         //get max dim
11736         for ( var i=0, l=propArray.length; i < l; i++) {
11737           var pi = propArray[i], dim = sib.getData('dim', pi);
11738           maxDim[pi] = (pi in maxDim)? (dim > maxDim[pi]? dim : maxDim[pi]) : dim;
11739         }
11740         subnodes.push(sib);
11741       }, "ignore");
11742       //Maintain children order
11743       //Second constraint for <http://bailando.sims.berkeley.edu/papers/infovis01.htm>
11744       if (parent && parent.id == elem.id && subnodes.length > 0
11745           && subnodes[0].dist) {
11746         subnodes.sort(function(a, b) {
11747           return (a.dist >= b.dist) - (a.dist <= b.dist);
11748         });
11749       }
11750       //Calculate nodes positions.
11751       for (var k = 0, ls=subnodes.length; k < ls; k++) {
11752         var child = subnodes[k];
11753         if (!child._flag) {
11754           var angleProportion = child._treeAngularWidth / totalAngularWidths * angleSpan;
11755           var theta = angleInit + angleProportion / 2;
11756
11757           for ( var i=0, l=propArray.length; i < l; i++) {
11758             var pi = propArray[i];
11759             child.setPos($P(theta, len), pi);
11760             child.setData('span', angleProportion, pi);
11761             child.setData('dim-quotient', child.getData('dim', pi) / maxDim[pi], pi);
11762           }
11763
11764           child.angleSpan = {
11765             begin : angleInit,
11766             end : angleInit + angleProportion
11767           };
11768           angleInit += angleProportion;
11769         }
11770       }
11771     }, "ignore");
11772   },
11773
11774   /*
11775    * Method: setAngularWidthForNodes
11776    * 
11777    * Sets nodes angular widths.
11778    */
11779   setAngularWidthForNodes : function(prop) {
11780     this.graph.eachBFS(this.root, function(elem, i) {
11781       var diamValue = elem.getData('angularWidth', prop[0]) || 5;
11782       elem._angularWidth = diamValue / i;
11783     }, "ignore");
11784   },
11785
11786   /*
11787    * Method: setSubtreesAngularWidth
11788    * 
11789    * Sets subtrees angular widths.
11790    */
11791   setSubtreesAngularWidth : function() {
11792     var that = this;
11793     this.graph.eachNode(function(elem) {
11794       that.setSubtreeAngularWidth(elem);
11795     }, "ignore");
11796   },
11797
11798   /*
11799    * Method: setSubtreeAngularWidth
11800    * 
11801    * Sets the angular width for a subtree.
11802    */
11803   setSubtreeAngularWidth : function(elem) {
11804     var that = this, nodeAW = elem._angularWidth, sumAW = 0;
11805     elem.eachSubnode(function(child) {
11806       that.setSubtreeAngularWidth(child);
11807       sumAW += child._treeAngularWidth;
11808     }, "ignore");
11809     elem._treeAngularWidth = Math.max(nodeAW, sumAW);
11810   },
11811
11812   /*
11813    * Method: computeAngularWidths
11814    * 
11815    * Computes nodes and subtrees angular widths.
11816    */
11817   computeAngularWidths : function(prop) {
11818     this.setAngularWidthForNodes(prop);
11819     this.setSubtreesAngularWidth();
11820   }
11821
11822 });
11823
11824
11825 /*
11826  * File: Sunburst.js
11827  */
11828
11829 /*
11830    Class: Sunburst
11831       
11832    A radial space filling tree visualization.
11833    
11834    Inspired by:
11835  
11836    Sunburst <http://www.cc.gatech.edu/gvu/ii/sunburst/>.
11837    
11838    Note:
11839    
11840    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.
11841    
11842   Implements:
11843   
11844   All <Loader> methods
11845   
11846    Constructor Options:
11847    
11848    Inherits options from
11849    
11850    - <Options.Canvas>
11851    - <Options.Controller>
11852    - <Options.Node>
11853    - <Options.Edge>
11854    - <Options.Label>
11855    - <Options.Events>
11856    - <Options.Tips>
11857    - <Options.NodeStyles>
11858    - <Options.Navigation>
11859    
11860    Additionally, there are other parameters and some default values changed
11861    
11862    interpolation - (string) Default's *linear*. Describes the way nodes are interpolated. Possible values are 'linear' and 'polar'.
11863    levelDistance - (number) Default's *100*. The distance between levels of the tree. 
11864    Node.type - Described in <Options.Node>. Default's to *multipie*.
11865    Node.height - Described in <Options.Node>. Default's *0*.
11866    Edge.type - Described in <Options.Edge>. Default's *none*.
11867    Label.textAlign - Described in <Options.Label>. Default's *start*.
11868    Label.textBaseline - Described in <Options.Label>. Default's *middle*.
11869      
11870    Instance Properties:
11871
11872    canvas - Access a <Canvas> instance.
11873    graph - Access a <Graph> instance.
11874    op - Access a <Sunburst.Op> instance.
11875    fx - Access a <Sunburst.Plot> instance.
11876    labels - Access a <Sunburst.Label> interface implementation.   
11877
11878 */
11879
11880 $jit.Sunburst = new Class({
11881
11882   Implements: [ Loader, Extras, Layouts.Radial ],
11883
11884   initialize: function(controller) {
11885     var $Sunburst = $jit.Sunburst;
11886
11887     var config = {
11888       interpolation: 'linear',
11889       levelDistance: 100,
11890       Node: {
11891         'type': 'multipie',
11892         'height':0
11893       },
11894       Edge: {
11895         'type': 'none'
11896       },
11897       Label: {
11898         textAlign: 'start',
11899         textBaseline: 'middle'
11900       }
11901     };
11902
11903     this.controller = this.config = $.merge(Options("Canvas", "Node", "Edge",
11904         "Fx", "Tips", "NodeStyles", "Events", "Navigation", "Controller", "Label"), config, controller);
11905
11906     var canvasConfig = this.config;
11907     if(canvasConfig.useCanvas) {
11908       this.canvas = canvasConfig.useCanvas;
11909       this.config.labelContainer = this.canvas.id + '-label';
11910     } else {
11911       if(canvasConfig.background) {
11912         canvasConfig.background = $.merge({
11913           type: 'Circles'
11914         }, canvasConfig.background);
11915       }
11916       this.canvas = new Canvas(this, canvasConfig);
11917       this.config.labelContainer = (typeof canvasConfig.injectInto == 'string'? canvasConfig.injectInto : canvasConfig.injectInto.id) + '-label';
11918     }
11919
11920     this.graphOptions = {
11921       'klass': Polar,
11922       'Node': {
11923         'selected': false,
11924         'exist': true,
11925         'drawn': true
11926       }
11927     };
11928     this.graph = new Graph(this.graphOptions, this.config.Node,
11929         this.config.Edge);
11930     this.labels = new $Sunburst.Label[canvasConfig.Label.type](this);
11931     this.fx = new $Sunburst.Plot(this, $Sunburst);
11932     this.op = new $Sunburst.Op(this);
11933     this.json = null;
11934     this.root = null;
11935     this.rotated = null;
11936     this.busy = false;
11937     // initialize extras
11938     this.initializeExtras();
11939   },
11940
11941   /* 
11942   
11943     createLevelDistanceFunc 
11944   
11945     Returns the levelDistance function used for calculating a node distance 
11946     to its origin. This function returns a function that is computed 
11947     per level and not per node, such that all nodes with the same depth will have the 
11948     same distance to the origin. The resulting function gets the 
11949     parent node as parameter and returns a float.
11950
11951    */
11952   createLevelDistanceFunc: function() {
11953     var ld = this.config.levelDistance;
11954     return function(elem) {
11955       return (elem._depth + 1) * ld;
11956     };
11957   },
11958
11959   /* 
11960      Method: refresh 
11961      
11962      Computes positions and plots the tree.
11963
11964    */
11965   refresh: function() {
11966     this.compute();
11967     this.plot();
11968   },
11969
11970   /*
11971    reposition
11972   
11973    An alias for computing new positions to _endPos_
11974
11975    See also:
11976
11977    <Sunburst.compute>
11978    
11979   */
11980   reposition: function() {
11981     this.compute('end');
11982   },
11983
11984   /*
11985   Method: rotate
11986   
11987   Rotates the graph so that the selected node is horizontal on the right.
11988
11989   Parameters:
11990   
11991   node - (object) A <Graph.Node>.
11992   method - (string) Whether to perform an animation or just replot the graph. Possible values are "replot" or "animate".
11993   opt - (object) Configuration options merged with this visualization configuration options.
11994   
11995   See also:
11996
11997   <Sunburst.rotateAngle>
11998   
11999   */
12000   rotate: function(node, method, opt) {
12001     var theta = node.getPos(opt.property || 'current').getp(true).theta;
12002     this.rotated = node;
12003     this.rotateAngle(-theta, method, opt);
12004   },
12005
12006   /*
12007   Method: rotateAngle
12008   
12009   Rotates the graph of an angle theta.
12010   
12011    Parameters:
12012    
12013    node - (object) A <Graph.Node>.
12014    method - (string) Whether to perform an animation or just replot the graph. Possible values are "replot" or "animate".
12015    opt - (object) Configuration options merged with this visualization configuration options.
12016    
12017    See also:
12018
12019    <Sunburst.rotate>
12020   
12021   */
12022   rotateAngle: function(theta, method, opt) {
12023     var that = this;
12024     var options = $.merge(this.config, opt || {}, {
12025       modes: [ 'polar' ]
12026     });
12027     var prop = opt.property || (method === "animate" ? 'end' : 'current');
12028     if(method === 'animate') {
12029       this.fx.animation.pause();
12030     }
12031     this.graph.eachNode(function(n) {
12032       var p = n.getPos(prop);
12033       p.theta += theta;
12034       if (p.theta < 0) {
12035         p.theta += Math.PI * 2;
12036       }
12037     });
12038     if (method == 'animate') {
12039       this.fx.animate(options);
12040     } else if (method == 'replot') {
12041       this.fx.plot();
12042       this.busy = false;
12043     }
12044   },
12045
12046   /*
12047    Method: plot
12048   
12049    Plots the Sunburst. This is a shortcut to *fx.plot*.
12050   */
12051   plot: function() {
12052     this.fx.plot();
12053   }
12054 });
12055
12056 $jit.Sunburst.$extend = true;
12057
12058 (function(Sunburst) {
12059
12060   /*
12061      Class: Sunburst.Op
12062
12063      Custom extension of <Graph.Op>.
12064
12065      Extends:
12066
12067      All <Graph.Op> methods
12068      
12069      See also:
12070      
12071      <Graph.Op>
12072
12073   */
12074   Sunburst.Op = new Class( {
12075
12076     Implements: Graph.Op
12077
12078   });
12079
12080   /*
12081      Class: Sunburst.Plot
12082
12083     Custom extension of <Graph.Plot>.
12084   
12085     Extends:
12086   
12087     All <Graph.Plot> methods
12088     
12089     See also:
12090     
12091     <Graph.Plot>
12092   
12093   */
12094   Sunburst.Plot = new Class( {
12095
12096     Implements: Graph.Plot
12097
12098   });
12099
12100   /*
12101     Class: Sunburst.Label
12102
12103     Custom extension of <Graph.Label>. 
12104     Contains custom <Graph.Label.SVG>, <Graph.Label.HTML> and <Graph.Label.Native> extensions.
12105   
12106     Extends:
12107   
12108     All <Graph.Label> methods and subclasses.
12109   
12110     See also:
12111   
12112     <Graph.Label>, <Graph.Label.Native>, <Graph.Label.HTML>, <Graph.Label.SVG>.
12113   
12114    */
12115   Sunburst.Label = {};
12116
12117   /*
12118      Sunburst.Label.Native
12119
12120      Custom extension of <Graph.Label.Native>.
12121
12122      Extends:
12123
12124      All <Graph.Label.Native> methods
12125
12126      See also:
12127
12128      <Graph.Label.Native>
12129   */
12130   Sunburst.Label.Native = new Class( {
12131     Implements: Graph.Label.Native,
12132
12133     initialize: function(viz) {
12134       this.viz = viz;
12135       this.label = viz.config.Label;
12136       this.config = viz.config;
12137     },
12138
12139     renderLabel: function(canvas, node, controller) {
12140       var span = node.getData('span');
12141       if(span < Math.PI /2 && Math.tan(span) * 
12142           this.config.levelDistance * node._depth < 10) {
12143         return;
12144       }
12145       var ctx = canvas.getCtx();
12146       var measure = ctx.measureText(node.name);
12147       if (node.id == this.viz.root) {
12148         var x = -measure.width / 2, y = 0, thetap = 0;
12149         var ld = 0;
12150       } else {
12151         var indent = 5;
12152         var ld = controller.levelDistance - indent;
12153         var clone = node.pos.clone();
12154         clone.rho += indent;
12155         var p = clone.getp(true);
12156         var ct = clone.getc(true);
12157         var x = ct.x, y = ct.y;
12158         // get angle in degrees
12159         var pi = Math.PI;
12160         var cond = (p.theta > pi / 2 && p.theta < 3 * pi / 2);
12161         var thetap = cond ? p.theta + pi : p.theta;
12162         if (cond) {
12163           x -= Math.abs(Math.cos(p.theta) * measure.width);
12164           y += Math.sin(p.theta) * measure.width;
12165         } else if (node.id == this.viz.root) {
12166           x -= measure.width / 2;
12167         }
12168       }
12169       ctx.save();
12170       ctx.translate(x, y);
12171       ctx.rotate(thetap);
12172       ctx.fillText(node.name, 0, 0);
12173       ctx.restore();
12174     }
12175   });
12176
12177   /*
12178      Sunburst.Label.SVG
12179
12180     Custom extension of <Graph.Label.SVG>.
12181   
12182     Extends:
12183   
12184     All <Graph.Label.SVG> methods
12185   
12186     See also:
12187   
12188     <Graph.Label.SVG>
12189   
12190   */
12191   Sunburst.Label.SVG = new Class( {
12192     Implements: Graph.Label.SVG,
12193
12194     initialize: function(viz) {
12195       this.viz = viz;
12196     },
12197
12198     /* 
12199        placeLabel
12200
12201        Overrides abstract method placeLabel in <Graph.Plot>.
12202
12203        Parameters:
12204
12205        tag - A DOM label element.
12206        node - A <Graph.Node>.
12207        controller - A configuration/controller object passed to the visualization.
12208       
12209      */
12210     placeLabel: function(tag, node, controller) {
12211       var pos = node.pos.getc(true), viz = this.viz, canvas = this.viz.canvas;
12212       var radius = canvas.getSize();
12213       var labelPos = {
12214         x: Math.round(pos.x + radius.width / 2),
12215         y: Math.round(pos.y + radius.height / 2)
12216       };
12217       tag.setAttribute('x', labelPos.x);
12218       tag.setAttribute('y', labelPos.y);
12219
12220       var bb = tag.getBBox();
12221       if (bb) {
12222         // center the label
12223     var x = tag.getAttribute('x');
12224     var y = tag.getAttribute('y');
12225     // get polar coordinates
12226     var p = node.pos.getp(true);
12227     // get angle in degrees
12228     var pi = Math.PI;
12229     var cond = (p.theta > pi / 2 && p.theta < 3 * pi / 2);
12230     if (cond) {
12231       tag.setAttribute('x', x - bb.width);
12232       tag.setAttribute('y', y - bb.height);
12233     } else if (node.id == viz.root) {
12234       tag.setAttribute('x', x - bb.width / 2);
12235     }
12236
12237     var thetap = cond ? p.theta + pi : p.theta;
12238     if(node._depth)
12239       tag.setAttribute('transform', 'rotate(' + thetap * 360 / (2 * pi) + ' ' + x
12240           + ' ' + y + ')');
12241   }
12242
12243   controller.onPlaceLabel(tag, node);
12244 }
12245   });
12246
12247   /*
12248      Sunburst.Label.HTML
12249
12250      Custom extension of <Graph.Label.HTML>.
12251
12252      Extends:
12253
12254      All <Graph.Label.HTML> methods.
12255
12256      See also:
12257
12258      <Graph.Label.HTML>
12259
12260   */
12261   Sunburst.Label.HTML = new Class( {
12262     Implements: Graph.Label.HTML,
12263
12264     initialize: function(viz) {
12265       this.viz = viz;
12266     },
12267     /* 
12268        placeLabel
12269
12270        Overrides abstract method placeLabel in <Graph.Plot>.
12271
12272        Parameters:
12273
12274        tag - A DOM label element.
12275        node - A <Graph.Node>.
12276        controller - A configuration/controller object passed to the visualization.
12277       
12278      */
12279     placeLabel: function(tag, node, controller) {
12280       var pos = node.pos.clone(), 
12281           canvas = this.viz.canvas,
12282           height = node.getData('height'),
12283           ldist = ((height || node._depth == 0)? height : this.viz.config.levelDistance) /2,
12284           radius = canvas.getSize();
12285       pos.rho += ldist;
12286       pos = pos.getc(true);
12287       
12288       var labelPos = {
12289         x: Math.round(pos.x + radius.width / 2),
12290         y: Math.round(pos.y + radius.height / 2)
12291       };
12292
12293       var style = tag.style;
12294       style.left = labelPos.x + 'px';
12295       style.top = labelPos.y + 'px';
12296       style.display = this.fitsInCanvas(labelPos, canvas) ? '' : 'none';
12297
12298       controller.onPlaceLabel(tag, node);
12299     }
12300   });
12301
12302   /*
12303     Class: Sunburst.Plot.NodeTypes
12304
12305     This class contains a list of <Graph.Node> built-in types. 
12306     Node types implemented are 'none', 'pie', 'multipie', 'gradient-pie' and 'gradient-multipie'.
12307
12308     You can add your custom node types, customizing your visualization to the extreme.
12309
12310     Example:
12311
12312     (start code js)
12313       Sunburst.Plot.NodeTypes.implement({
12314         'mySpecialType': {
12315           'render': function(node, canvas) {
12316             //print your custom node to canvas
12317           },
12318           //optional
12319           'contains': function(node, pos) {
12320             //return true if pos is inside the node or false otherwise
12321           }
12322         }
12323       });
12324     (end code)
12325
12326   */
12327   Sunburst.Plot.NodeTypes = new Class( {
12328     'none': {
12329       'render': $.empty,
12330       'contains': $.lambda(false),
12331       'anglecontains': function(node, pos) {
12332         var span = node.getData('span') / 2, theta = node.pos.theta;
12333         var begin = theta - span, end = theta + span;
12334         if (begin < 0)
12335           begin += Math.PI * 2;
12336         var atan = Math.atan2(pos.y, pos.x);
12337         if (atan < 0)
12338           atan += Math.PI * 2;
12339         if (begin > end) {
12340           return (atan > begin && atan <= Math.PI * 2) || atan < end;
12341         } else {
12342           return atan > begin && atan < end;
12343         }
12344       }
12345     },
12346
12347     'pie': {
12348       'render': function(node, canvas) {
12349         var span = node.getData('span') / 2, theta = node.pos.theta;
12350         var begin = theta - span, end = theta + span;
12351         var polarNode = node.pos.getp(true);
12352         var polar = new Polar(polarNode.rho, begin);
12353         var p1coord = polar.getc(true);
12354         polar.theta = end;
12355         var p2coord = polar.getc(true);
12356
12357         var ctx = canvas.getCtx();
12358         ctx.beginPath();
12359         ctx.moveTo(0, 0);
12360         ctx.lineTo(p1coord.x, p1coord.y);
12361         ctx.moveTo(0, 0);
12362         ctx.lineTo(p2coord.x, p2coord.y);
12363         ctx.moveTo(0, 0);
12364         ctx.arc(0, 0, polarNode.rho * node.getData('dim-quotient'), begin, end,
12365             false);
12366         ctx.fill();
12367       },
12368       'contains': function(node, pos) {
12369         if (this.nodeTypes['none'].anglecontains.call(this, node, pos)) {
12370           var rho = Math.sqrt(pos.x * pos.x + pos.y * pos.y);
12371           var ld = this.config.levelDistance, d = node._depth;
12372           return (rho <= ld * d);
12373         }
12374         return false;
12375       }
12376     },
12377     'multipie': {
12378       'render': function(node, canvas) {
12379         var height = node.getData('height');
12380         var ldist = height? height : this.config.levelDistance;
12381         var span = node.getData('span') / 2, theta = node.pos.theta;
12382         var begin = theta - span, end = theta + span;
12383         var polarNode = node.pos.getp(true);
12384
12385         var polar = new Polar(polarNode.rho, begin);
12386         var p1coord = polar.getc(true);
12387
12388         polar.theta = end;
12389         var p2coord = polar.getc(true);
12390
12391         polar.rho += ldist;
12392         var p3coord = polar.getc(true);
12393
12394         polar.theta = begin;
12395         var p4coord = polar.getc(true);
12396
12397         var ctx = canvas.getCtx();
12398         ctx.moveTo(0, 0);
12399         ctx.beginPath();
12400         ctx.arc(0, 0, polarNode.rho, begin, end, false);
12401         ctx.arc(0, 0, polarNode.rho + ldist, end, begin, true);
12402         ctx.moveTo(p1coord.x, p1coord.y);
12403         ctx.lineTo(p4coord.x, p4coord.y);
12404         ctx.moveTo(p2coord.x, p2coord.y);
12405         ctx.lineTo(p3coord.x, p3coord.y);
12406         ctx.fill();
12407
12408         if (node.collapsed) {
12409           ctx.save();
12410           ctx.lineWidth = 2;
12411           ctx.moveTo(0, 0);
12412           ctx.beginPath();
12413           ctx.arc(0, 0, polarNode.rho + ldist + 5, end - 0.01, begin + 0.01,
12414               true);
12415           ctx.stroke();
12416           ctx.restore();
12417         }
12418       },
12419       'contains': function(node, pos) {
12420         if (this.nodeTypes['none'].anglecontains.call(this, node, pos)) {
12421           var rho = Math.sqrt(pos.x * pos.x + pos.y * pos.y);
12422           var height = node.getData('height');
12423           var ldist = height? height : this.config.levelDistance;
12424           var ld = this.config.levelDistance, d = node._depth;
12425           return (rho >= ld * d) && (rho <= (ld * d + ldist));
12426         }
12427         return false;
12428       }
12429     },
12430
12431     'gradient-multipie': {
12432       'render': function(node, canvas) {
12433         var ctx = canvas.getCtx();
12434         var height = node.getData('height');
12435         var ldist = height? height : this.config.levelDistance;
12436         var radialGradient = ctx.createRadialGradient(0, 0, node.getPos().rho,
12437             0, 0, node.getPos().rho + ldist);
12438
12439         var colorArray = $.hexToRgb(node.getData('color')), ans = [];
12440         $.each(colorArray, function(i) {
12441           ans.push(parseInt(i * 0.5, 10));
12442         });
12443         var endColor = $.rgbToHex(ans);
12444         radialGradient.addColorStop(0, endColor);
12445         radialGradient.addColorStop(1, node.getData('color'));
12446         ctx.fillStyle = radialGradient;
12447         this.nodeTypes['multipie'].render.call(this, node, canvas);
12448       },
12449       'contains': function(node, pos) {
12450         return this.nodeTypes['multipie'].contains.call(this, node, pos);
12451       }
12452     },
12453
12454     'gradient-pie': {
12455       'render': function(node, canvas) {
12456         var ctx = canvas.getCtx();
12457         var radialGradient = ctx.createRadialGradient(0, 0, 0, 0, 0, node
12458             .getPos().rho);
12459
12460         var colorArray = $.hexToRgb(node.getData('color')), ans = [];
12461         $.each(colorArray, function(i) {
12462           ans.push(parseInt(i * 0.5, 10));
12463         });
12464         var endColor = $.rgbToHex(ans);
12465         radialGradient.addColorStop(1, endColor);
12466         radialGradient.addColorStop(0, node.getData('color'));
12467         ctx.fillStyle = radialGradient;
12468         this.nodeTypes['pie'].render.call(this, node, canvas);
12469       },
12470       'contains': function(node, pos) {
12471         return this.nodeTypes['pie'].contains.call(this, node, pos);
12472       }
12473     }
12474   });
12475
12476   /*
12477     Class: Sunburst.Plot.EdgeTypes
12478
12479     This class contains a list of <Graph.Adjacence> built-in types. 
12480     Edge types implemented are 'none', 'line' and 'arrow'.
12481   
12482     You can add your custom edge types, customizing your visualization to the extreme.
12483   
12484     Example:
12485   
12486     (start code js)
12487       Sunburst.Plot.EdgeTypes.implement({
12488         'mySpecialType': {
12489           'render': function(adj, canvas) {
12490             //print your custom edge to canvas
12491           },
12492           //optional
12493           'contains': function(adj, pos) {
12494             //return true if pos is inside the arc or false otherwise
12495           }
12496         }
12497       });
12498     (end code)
12499   
12500   */
12501   Sunburst.Plot.EdgeTypes = new Class({
12502     'none': $.empty,
12503     'line': {
12504       'render': function(adj, canvas) {
12505         var from = adj.nodeFrom.pos.getc(true),
12506             to = adj.nodeTo.pos.getc(true);
12507         this.edgeHelper.line.render(from, to, canvas);
12508       },
12509       'contains': function(adj, pos) {
12510         var from = adj.nodeFrom.pos.getc(true),
12511             to = adj.nodeTo.pos.getc(true);
12512         return this.edgeHelper.line.contains(from, to, pos, this.edge.epsilon);
12513       }
12514     },
12515     'arrow': {
12516       'render': function(adj, canvas) {
12517         var from = adj.nodeFrom.pos.getc(true),
12518             to = adj.nodeTo.pos.getc(true),
12519             dim = adj.getData('dim'),
12520             direction = adj.data.$direction,
12521             inv = (direction && direction.length>1 && direction[0] != adj.nodeFrom.id);
12522         this.edgeHelper.arrow.render(from, to, dim, inv, canvas);
12523       },
12524       'contains': function(adj, pos) {
12525         var from = adj.nodeFrom.pos.getc(true),
12526             to = adj.nodeTo.pos.getc(true);
12527         return this.edgeHelper.arrow.contains(from, to, pos, this.edge.epsilon);
12528       }
12529     },
12530     'hyperline': {
12531       'render': function(adj, canvas) {
12532         var from = adj.nodeFrom.pos.getc(),
12533             to = adj.nodeTo.pos.getc(),
12534             dim = Math.max(from.norm(), to.norm());
12535         this.edgeHelper.hyperline.render(from.$scale(1/dim), to.$scale(1/dim), dim, canvas);
12536       },
12537       'contains': $.lambda(false) //TODO(nico): Implement this!
12538     }
12539   });
12540
12541 })($jit.Sunburst);
12542
12543
12544 /*
12545  * File: PieChart.js
12546  *
12547 */
12548
12549 $jit.Sunburst.Plot.NodeTypes.implement({
12550   'piechart-stacked' : {
12551     'render' : function(node, canvas) {
12552       var pos = node.pos.getp(true),
12553           dimArray = node.getData('dimArray'),
12554           valueArray = node.getData('valueArray'),
12555           colorArray = node.getData('colorArray'),
12556           colorLength = colorArray.length,
12557           stringArray = node.getData('stringArray'),
12558           span = node.getData('span') / 2,
12559           theta = node.pos.theta,
12560           begin = theta - span,
12561           end = theta + span,
12562           polar = new Polar;
12563     
12564       var ctx = canvas.getCtx(), 
12565           opt = {},
12566           gradient = node.getData('gradient'),
12567           border = node.getData('border'),
12568           config = node.getData('config'),
12569           showLabels = config.showLabels,
12570           resizeLabels = config.resizeLabels,
12571           label = config.Label;
12572
12573       var xpos = config.sliceOffset * Math.cos((begin + end) /2);
12574       var ypos = config.sliceOffset * Math.sin((begin + end) /2);
12575
12576       if (colorArray && dimArray && stringArray) {
12577         for (var i=0, l=dimArray.length, acum=0, valAcum=0; i<l; i++) {
12578           var dimi = dimArray[i], colori = colorArray[i % colorLength];
12579           if(dimi <= 0) continue;
12580           ctx.fillStyle = ctx.strokeStyle = colori;
12581           if(gradient && dimi) {
12582             var radialGradient = ctx.createRadialGradient(xpos, ypos, acum + config.sliceOffset,
12583                 xpos, ypos, acum + dimi + config.sliceOffset);
12584             var colorRgb = $.hexToRgb(colori), 
12585                 ans = $.map(colorRgb, function(i) { return (i * 0.8) >> 0; }),
12586                 endColor = $.rgbToHex(ans);
12587
12588             radialGradient.addColorStop(0, colori);
12589             radialGradient.addColorStop(0.5, colori);
12590             radialGradient.addColorStop(1, endColor);
12591             ctx.fillStyle = radialGradient;
12592           }
12593           
12594           polar.rho = acum + config.sliceOffset;
12595           polar.theta = begin;
12596           var p1coord = polar.getc(true);
12597           polar.theta = end;
12598           var p2coord = polar.getc(true);
12599           polar.rho += dimi;
12600           var p3coord = polar.getc(true);
12601           polar.theta = begin;
12602           var p4coord = polar.getc(true);
12603
12604           ctx.beginPath();
12605           //fixing FF arc method + fill
12606           ctx.arc(xpos, ypos, acum + .01, begin, end, false);
12607           ctx.arc(xpos, ypos, acum + dimi + .01, end, begin, true);
12608           ctx.fill();
12609           if(border && border.name == stringArray[i]) {
12610             opt.acum = acum;
12611             opt.dimValue = dimArray[i];
12612             opt.begin = begin;
12613             opt.end = end;
12614           }
12615           acum += (dimi || 0);
12616           valAcum += (valueArray[i] || 0);
12617         }
12618         if(border) {
12619           ctx.save();
12620           ctx.globalCompositeOperation = "source-over";
12621           ctx.lineWidth = 2;
12622           ctx.strokeStyle = border.color;
12623           var s = begin < end? 1 : -1;
12624           ctx.beginPath();
12625           //fixing FF arc method + fill
12626           ctx.arc(xpos, ypos, opt.acum + .01 + 1, opt.begin, opt.end, false);
12627           ctx.arc(xpos, ypos, opt.acum + opt.dimValue + .01 - 1, opt.end, opt.begin, true);
12628           ctx.closePath();
12629           ctx.stroke();
12630           ctx.restore();
12631         }
12632         if(showLabels && label.type == 'Native') {
12633           ctx.save();
12634           ctx.fillStyle = ctx.strokeStyle = label.color;
12635           var scale = resizeLabels? node.getData('normalizedDim') : 1,
12636               fontSize = (label.size * scale) >> 0;
12637           fontSize = fontSize < +resizeLabels? +resizeLabels : fontSize;
12638           
12639           ctx.font = label.style + ' ' + fontSize + 'px ' + label.family;
12640           ctx.textBaseline = 'middle';
12641           ctx.textAlign = 'center';
12642           
12643           polar.rho = acum + config.labelOffset + config.sliceOffset;
12644           polar.theta = node.pos.theta;
12645           var cart = polar.getc(true);
12646           
12647           ctx.fillText(node.name, cart.x, cart.y);
12648           ctx.restore();
12649         }
12650       }
12651     },
12652     'contains': function(node, pos) {
12653       if (this.nodeTypes['none'].anglecontains.call(this, node, pos)) {
12654         var rho = Math.sqrt(pos.x * pos.x + pos.y * pos.y);
12655         var ld = this.config.levelDistance, d = node._depth;
12656         var config = node.getData('config');
12657         if(rho <=ld * d + config.sliceOffset) {
12658           var dimArray = node.getData('dimArray');
12659           for(var i=0,l=dimArray.length,acum=config.sliceOffset; i<l; i++) {
12660             var dimi = dimArray[i];
12661             if(rho >= acum && rho <= acum + dimi) {
12662               return {
12663                 name: node.getData('stringArray')[i],
12664                 color: node.getData('colorArray')[i],
12665                 value: node.getData('valueArray')[i],
12666                 label: node.name
12667               };
12668             }
12669             acum += dimi;
12670           }
12671         }
12672         return false;
12673         
12674       }
12675       return false;
12676     }
12677   }
12678 });
12679
12680 /*
12681   Class: PieChart
12682   
12683   A visualization that displays stacked bar charts.
12684   
12685   Constructor Options:
12686   
12687   See <Options.PieChart>.
12688
12689 */
12690 $jit.PieChart = new Class({
12691   sb: null,
12692   colors: ["#416D9C", "#70A35E", "#EBB056", "#C74243", "#83548B", "#909291", "#557EAA"],
12693   selected: {},
12694   busy: false,
12695   
12696   initialize: function(opt) {
12697     this.controller = this.config = 
12698       $.merge(Options("Canvas", "PieChart", "Label"), {
12699         Label: { type: 'Native' }
12700       }, opt);
12701     this.initializeViz();
12702   },
12703   
12704   initializeViz: function() {
12705     var config = this.config, that = this;
12706     var nodeType = config.type.split(":")[0];
12707     var delegate = new $jit.Sunburst({
12708       injectInto: config.injectInto,
12709       width: config.width,
12710       height: config.height,
12711       useCanvas: config.useCanvas,
12712       withLabels: config.Label.type != 'Native',
12713       Label: {
12714         type: config.Label.type
12715       },
12716       Node: {
12717         overridable: true,
12718         type: 'piechart-' + nodeType,
12719         width: 1,
12720         height: 1
12721       },
12722       Edge: {
12723         type: 'none'
12724       },
12725       Tips: {
12726         enable: config.Tips.enable,
12727         type: 'Native',
12728         force: true,
12729         onShow: function(tip, node, contains) {
12730           var elem = contains;
12731           config.Tips.onShow(tip, elem, node);
12732         }
12733       },
12734       Events: {
12735         enable: true,
12736         type: 'Native',
12737         onClick: function(node, eventInfo, evt) {
12738           if(!config.Events.enable) return;
12739           var elem = eventInfo.getContains();
12740           config.Events.onClick(elem, eventInfo, evt);
12741         },
12742         onMouseMove: function(node, eventInfo, evt) {
12743           if(!config.hoveredColor) return;
12744           if(node) {
12745             var elem = eventInfo.getContains();
12746             that.select(node.id, elem.name, elem.index);
12747           } else {
12748             that.select(false, false, false);
12749           }
12750         }
12751       },
12752       onCreateLabel: function(domElement, node) {
12753         var labelConf = config.Label;
12754         if(config.showLabels) {
12755           var style = domElement.style;
12756           style.fontSize = labelConf.size + 'px';
12757           style.fontFamily = labelConf.family;
12758           style.color = labelConf.color;
12759           style.textAlign = 'center';
12760           domElement.innerHTML = node.name;
12761         }
12762       },
12763       onPlaceLabel: function(domElement, node) {
12764         if(!config.showLabels) return;
12765         var pos = node.pos.getp(true),
12766             dimArray = node.getData('dimArray'),
12767             span = node.getData('span') / 2,
12768             theta = node.pos.theta,
12769             begin = theta - span,
12770             end = theta + span,
12771             polar = new Polar;
12772       
12773         var showLabels = config.showLabels,
12774             resizeLabels = config.resizeLabels,
12775             label = config.Label;
12776         
12777         if (dimArray) {
12778           for (var i=0, l=dimArray.length, acum=0; i<l; i++) {
12779             acum += dimArray[i];
12780           }
12781           var scale = resizeLabels? node.getData('normalizedDim') : 1,
12782               fontSize = (label.size * scale) >> 0;
12783           fontSize = fontSize < +resizeLabels? +resizeLabels : fontSize;
12784           domElement.style.fontSize = fontSize + 'px';
12785           polar.rho = acum + config.labelOffset + config.sliceOffset;
12786           polar.theta = (begin + end) / 2;
12787           var pos = polar.getc(true);
12788           var radius = that.canvas.getSize();
12789           var labelPos = {
12790             x: Math.round(pos.x + radius.width / 2),
12791             y: Math.round(pos.y + radius.height / 2)
12792           };
12793           domElement.style.left = labelPos.x + 'px';
12794           domElement.style.top = labelPos.y + 'px';
12795         }
12796       }
12797     });
12798     
12799     var size = delegate.canvas.getSize(),
12800         min = Math.min;
12801     delegate.config.levelDistance = min(size.width, size.height)/2 
12802       - config.offset - config.sliceOffset;
12803     this.delegate = delegate;
12804     this.canvas = this.delegate.canvas;
12805     this.canvas.getCtx().globalCompositeOperation = 'lighter';
12806   },
12807   
12808   /*
12809     Method: loadJSON
12810    
12811     Loads JSON data into the visualization. 
12812     
12813     Parameters:
12814     
12815     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>.
12816     
12817     Example:
12818     (start code js)
12819     var pieChart = new $jit.PieChart(options);
12820     pieChart.loadJSON(json);
12821     (end code)
12822   */  
12823   loadJSON: function(json) {
12824     var prefix = $.time(), 
12825         ch = [], 
12826         delegate = this.delegate,
12827         name = $.splat(json.label),
12828         nameLength = name.length,
12829         color = $.splat(json.color || this.colors),
12830         colorLength = color.length,
12831         config = this.config,
12832         gradient = !!config.type.split(":")[1],
12833         animate = config.animate,
12834         mono = nameLength == 1;
12835     
12836     for(var i=0, values=json.values, l=values.length; i<l; i++) {
12837       var val = values[i];
12838       var valArray = $.splat(val.values);
12839       ch.push({
12840         'id': prefix + val.label,
12841         'name': val.label,
12842         'data': {
12843           'value': valArray,
12844           '$valueArray': valArray,
12845           '$colorArray': mono? $.splat(color[i % colorLength]) : color,
12846           '$stringArray': name,
12847           '$gradient': gradient,
12848           '$config': config,
12849           '$angularWidth': $.reduce(valArray, function(x,y){return x+y;})
12850         },
12851         'children': []
12852       });
12853     }
12854     var root = {
12855       'id': prefix + '$root',
12856       'name': '',
12857       'data': {
12858         '$type': 'none',
12859         '$width': 1,
12860         '$height': 1
12861       },
12862       'children': ch
12863     };
12864     delegate.loadJSON(root);
12865     
12866     this.normalizeDims();
12867     delegate.refresh();
12868     if(animate) {
12869       delegate.fx.animate({
12870         modes: ['node-property:dimArray'],
12871         duration:1500
12872       });
12873     }
12874   },
12875   
12876   /*
12877     Method: updateJSON
12878    
12879     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.
12880     
12881     Parameters:
12882     
12883     json - (object) JSON data to be updated. The JSON format corresponds to the one described in <PieChart.loadJSON>.
12884     onComplete - (object) A callback object to be called when the animation transition when updating the data end.
12885     
12886     Example:
12887     
12888     (start code js)
12889     pieChart.updateJSON(json, {
12890       onComplete: function() {
12891         alert('update complete!');
12892       }
12893     });
12894     (end code)
12895   */  
12896   updateJSON: function(json, onComplete) {
12897     if(this.busy) return;
12898     this.busy = true;
12899     
12900     var delegate = this.delegate;
12901     var graph = delegate.graph;
12902     var values = json.values;
12903     var animate = this.config.animate;
12904     var that = this;
12905     $.each(values, function(v) {
12906       var n = graph.getByName(v.label),
12907           vals = $.splat(v.values);
12908       if(n) {
12909         n.setData('valueArray', vals);
12910         n.setData('angularWidth', $.reduce(vals, function(x,y){return x+y;}));
12911         if(json.label) {
12912           n.setData('stringArray', $.splat(json.label));
12913         }
12914       }
12915     });
12916     this.normalizeDims();
12917     if(animate) {
12918       delegate.compute('end');
12919       delegate.fx.animate({
12920         modes: ['node-property:dimArray:span', 'linear'],
12921         duration:1500,
12922         onComplete: function() {
12923           that.busy = false;
12924           onComplete && onComplete.onComplete();
12925         }
12926       });
12927     } else {
12928       delegate.refresh();
12929     }
12930   },
12931     
12932   //adds the little brown bar when hovering the node
12933   select: function(id, name) {
12934     if(!this.config.hoveredColor) return;
12935     var s = this.selected;
12936     if(s.id != id || s.name != name) {
12937       s.id = id;
12938       s.name = name;
12939       s.color = this.config.hoveredColor;
12940       this.delegate.graph.eachNode(function(n) {
12941         if(id == n.id) {
12942           n.setData('border', s);
12943         } else {
12944           n.setData('border', false);
12945         }
12946       });
12947       this.delegate.plot();
12948     }
12949   },
12950   
12951   /*
12952     Method: getLegend
12953    
12954     Returns an object containing as keys the legend names and as values hex strings with color values.
12955     
12956     Example:
12957     
12958     (start code js)
12959     var legend = pieChart.getLegend();
12960     (end code)
12961   */  
12962   getLegend: function() {
12963     var legend = {};
12964     var n;
12965     this.delegate.graph.getNode(this.delegate.root).eachAdjacency(function(adj) {
12966       n = adj.nodeTo;
12967     });
12968     var colors = n.getData('colorArray'),
12969         len = colors.length;
12970     $.each(n.getData('stringArray'), function(s, i) {
12971       legend[s] = colors[i % len];
12972     });
12973     return legend;
12974   },
12975   
12976   /*
12977     Method: getMaxValue
12978    
12979     Returns the maximum accumulated value for the stacks. This method is used for normalizing the graph heights according to the canvas height.
12980     
12981     Example:
12982     
12983     (start code js)
12984     var ans = pieChart.getMaxValue();
12985     (end code)
12986     
12987     In some cases it could be useful to override this method to normalize heights for a group of PieCharts, like when doing small multiples.
12988     
12989     Example:
12990     
12991     (start code js)
12992     //will return 100 for all PieChart instances,
12993     //displaying all of them with the same scale
12994     $jit.PieChart.implement({
12995       'getMaxValue': function() {
12996         return 100;
12997       }
12998     });
12999     (end code)
13000     
13001   */  
13002   getMaxValue: function() {
13003     var maxValue = 0;
13004     this.delegate.graph.eachNode(function(n) {
13005       var valArray = n.getData('valueArray'),
13006           acum = 0;
13007       $.each(valArray, function(v) { 
13008         acum += +v;
13009       });
13010       maxValue = maxValue>acum? maxValue:acum;
13011     });
13012     return maxValue;
13013   },
13014   
13015   normalizeDims: function() {
13016     //number of elements
13017     var root = this.delegate.graph.getNode(this.delegate.root), l=0;
13018     root.eachAdjacency(function() {
13019       l++;
13020     });
13021     var maxValue = this.getMaxValue() || 1,
13022         config = this.config,
13023         animate = config.animate,
13024         rho = this.delegate.config.levelDistance;
13025     this.delegate.graph.eachNode(function(n) {
13026       var acum = 0, animateValue = [];
13027       $.each(n.getData('valueArray'), function(v) {
13028         acum += +v;
13029         animateValue.push(1);
13030       });
13031       var stat = (animateValue.length == 1) && !config.updateHeights;
13032       if(animate) {
13033         n.setData('dimArray', $.map(n.getData('valueArray'), function(n) { 
13034           return stat? rho: (n * rho / maxValue); 
13035         }), 'end');
13036         var dimArray = n.getData('dimArray');
13037         if(!dimArray) {
13038           n.setData('dimArray', animateValue);
13039         }
13040       } else {
13041         n.setData('dimArray', $.map(n.getData('valueArray'), function(n) { 
13042           return stat? rho : (n * rho / maxValue); 
13043         }));
13044       }
13045       n.setData('normalizedDim', acum / maxValue);
13046     });
13047   }
13048 });
13049
13050
13051 /*
13052  * Class: Layouts.TM
13053  * 
13054  * Implements TreeMaps layouts (SliceAndDice, Squarified, Strip).
13055  * 
13056  * Implemented By:
13057  * 
13058  * <TM>
13059  * 
13060  */
13061 Layouts.TM = {};
13062
13063 Layouts.TM.SliceAndDice = new Class({
13064   compute: function(prop) {
13065     var root = this.graph.getNode(this.clickedNode && this.clickedNode.id || this.root);
13066     this.controller.onBeforeCompute(root);
13067     var size = this.canvas.getSize(),
13068         config = this.config,
13069         width = size.width,
13070         height = size.height;
13071     this.graph.computeLevels(this.root, 0, "ignore");
13072     //set root position and dimensions
13073     root.getPos(prop).setc(-width/2, -height/2);
13074     root.setData('width', width, prop);
13075     root.setData('height', height + config.titleHeight, prop);
13076     this.computePositions(root, root, this.layout.orientation, prop);
13077     this.controller.onAfterCompute(root);
13078   },
13079   
13080   computePositions: function(par, ch, orn, prop) {
13081     //compute children areas
13082     var totalArea = 0;
13083     par.eachSubnode(function(n) {
13084       totalArea += n.getData('area', prop);
13085     });
13086     
13087     var config = this.config,
13088         offst = config.offset,
13089         width  = par.getData('width', prop),
13090         height = Math.max(par.getData('height', prop) - config.titleHeight, 0),
13091         fact = par == ch? 1 : (ch.getData('area', prop) / totalArea);
13092
13093     var otherSize, size, dim, pos, pos2, posth, pos2th;
13094     var horizontal = (orn == "h");
13095     if(horizontal) {
13096       orn = 'v';
13097       otherSize = height;
13098       size = width * fact;
13099       dim = 'height';
13100       pos = 'y';
13101       pos2 = 'x';
13102       posth = config.titleHeight;
13103       pos2th = 0;
13104     } else {
13105       orn = 'h';    
13106       otherSize = height * fact;
13107       size = width;
13108       dim = 'width';
13109       pos = 'x';
13110       pos2 = 'y';
13111       posth = 0;
13112       pos2th = config.titleHeight;
13113     }
13114     var cpos = ch.getPos(prop);
13115     ch.setData('width', size, prop);
13116     ch.setData('height', otherSize, prop);
13117     var offsetSize = 0, tm = this;
13118     ch.eachSubnode(function(n) {
13119       var p = n.getPos(prop);
13120       p[pos] = offsetSize + cpos[pos] + posth;
13121       p[pos2] = cpos[pos2] + pos2th;
13122       tm.computePositions(ch, n, orn, prop);
13123       offsetSize += n.getData(dim, prop);
13124     });
13125   }
13126
13127 });
13128
13129 Layouts.TM.Area = {
13130  /*
13131     Method: compute
13132  
13133    Called by loadJSON to calculate recursively all node positions and lay out the tree.
13134  
13135     Parameters:
13136
13137        json - A JSON tree. See also <Loader.loadJSON>.
13138        coord - A coordinates object specifying width, height, left and top style properties.
13139  */
13140  compute: function(prop) {
13141     prop = prop || "current";
13142     var root = this.graph.getNode(this.clickedNode && this.clickedNode.id || this.root);
13143     this.controller.onBeforeCompute(root);
13144     var config = this.config,
13145         size = this.canvas.getSize(),
13146         width = size.width,
13147         height = size.height,
13148         offst = config.offset,
13149         offwdth = width - offst,
13150         offhght = height - offst;
13151     this.graph.computeLevels(this.root, 0, "ignore");
13152     //set root position and dimensions
13153     root.getPos(prop).setc(-width/2, -height/2);
13154     root.setData('width', width, prop);
13155     root.setData('height', height, prop);
13156     //create a coordinates object
13157     var coord = {
13158         'top': -height/2 + config.titleHeight,
13159         'left': -width/2,
13160         'width': offwdth,
13161         'height': offhght - config.titleHeight
13162     };
13163     this.computePositions(root, coord, prop);
13164     this.controller.onAfterCompute(root);
13165  }, 
13166  
13167  /*
13168     Method: computeDim
13169  
13170    Computes dimensions and positions of a group of nodes
13171    according to a custom layout row condition. 
13172  
13173     Parameters:
13174
13175        tail - An array of nodes.  
13176        initElem - An array of nodes (containing the initial node to be laid).
13177        w - A fixed dimension where nodes will be layed out.
13178        coord - A coordinates object specifying width, height, left and top style properties.
13179        comp - A custom comparison function
13180  */
13181  computeDim: function(tail, initElem, w, coord, comp, prop) {
13182    if(tail.length + initElem.length == 1) {
13183      var l = (tail.length == 1)? tail : initElem;
13184      this.layoutLast(l, w, coord, prop);
13185      return;
13186    }
13187    if(tail.length >= 2 && initElem.length == 0) {
13188      initElem = [tail.shift()];
13189    }
13190    if(tail.length == 0) {
13191      if(initElem.length > 0) this.layoutRow(initElem, w, coord, prop);
13192      return;
13193    }
13194    var c = tail[0];
13195    if(comp(initElem, w) >= comp([c].concat(initElem), w)) {
13196      this.computeDim(tail.slice(1), initElem.concat([c]), w, coord, comp, prop);
13197    } else {
13198      var newCoords = this.layoutRow(initElem, w, coord, prop);
13199      this.computeDim(tail, [], newCoords.dim, newCoords, comp, prop);
13200    }
13201  },
13202
13203  
13204  /*
13205     Method: worstAspectRatio
13206  
13207    Calculates the worst aspect ratio of a group of rectangles. 
13208        
13209     See also:
13210        
13211        <http://en.wikipedia.org/wiki/Aspect_ratio>
13212    
13213     Parameters:
13214
13215      ch - An array of nodes.  
13216      w  - The fixed dimension where rectangles are being laid out.
13217
13218     Returns:
13219  
13220         The worst aspect ratio.
13221
13222
13223  */
13224  worstAspectRatio: function(ch, w) {
13225    if(!ch || ch.length == 0) return Number.MAX_VALUE;
13226    var areaSum = 0, maxArea = 0, minArea = Number.MAX_VALUE;
13227    for(var i=0, l=ch.length; i<l; i++) {
13228      var area = ch[i]._area;
13229      areaSum += area; 
13230      minArea = minArea < area? minArea : area;
13231      maxArea = maxArea > area? maxArea : area; 
13232    }
13233    var sqw = w * w, sqAreaSum = areaSum * areaSum;
13234    return Math.max(sqw * maxArea / sqAreaSum,
13235            sqAreaSum / (sqw * minArea));
13236  },
13237  
13238  /*
13239     Method: avgAspectRatio
13240  
13241    Calculates the average aspect ratio of a group of rectangles. 
13242        
13243        See also:
13244        
13245        <http://en.wikipedia.org/wiki/Aspect_ratio>
13246    
13247     Parameters:
13248
13249      ch - An array of nodes.  
13250        w - The fixed dimension where rectangles are being laid out.
13251
13252     Returns:
13253  
13254         The average aspect ratio.
13255
13256
13257  */
13258  avgAspectRatio: function(ch, w) {
13259    if(!ch || ch.length == 0) return Number.MAX_VALUE;
13260    var arSum = 0;
13261    for(var i=0, l=ch.length; i<l; i++) {
13262      var area = ch[i]._area;
13263      var h = area / w;
13264      arSum += w > h? w / h : h / w;
13265    }
13266    return arSum / l;
13267  },
13268
13269  /*
13270     layoutLast
13271  
13272    Performs the layout of the last computed sibling.
13273  
13274     Parameters:
13275
13276        ch - An array of nodes.  
13277        w - A fixed dimension where nodes will be layed out.
13278      coord - A coordinates object specifying width, height, left and top style properties.
13279  */
13280  layoutLast: function(ch, w, coord, prop) {
13281    var child = ch[0];
13282    child.getPos(prop).setc(coord.left, coord.top);
13283    child.setData('width', coord.width, prop);
13284    child.setData('height', coord.height, prop);
13285  }
13286 };
13287
13288
13289 Layouts.TM.Squarified = new Class({
13290  Implements: Layouts.TM.Area,
13291  
13292  computePositions: function(node, coord, prop) {
13293    var config = this.config, 
13294        max = Math.max;
13295    
13296    if (coord.width >= coord.height) 
13297      this.layout.orientation = 'h';
13298    else
13299      this.layout.orientation = 'v';
13300    
13301    var ch = node.getSubnodes([1, 1], "ignore");
13302    if(ch.length > 0) {
13303      this.processChildrenLayout(node, ch, coord, prop);
13304      for(var i=0, l=ch.length; i<l; i++) {
13305        var chi = ch[i], 
13306            offst = config.offset,
13307            height = max(chi.getData('height', prop) - offst - config.titleHeight, 0),
13308            width = max(chi.getData('width', prop) - offst, 0),
13309            chipos = chi.getPos(prop);
13310
13311        coord = {
13312          'width': width,
13313          'height': height,
13314          'top': chipos.y + config.titleHeight,
13315          'left': chipos.x
13316        };
13317        this.computePositions(chi, coord, prop);
13318      }
13319    }
13320  },
13321
13322  /*
13323     Method: processChildrenLayout
13324  
13325    Computes children real areas and other useful parameters for performing the Squarified algorithm.
13326  
13327     Parameters:
13328
13329        par - The parent node of the json subtree.  
13330        ch - An Array of nodes
13331      coord - A coordinates object specifying width, height, left and top style properties.
13332  */
13333  processChildrenLayout: function(par, ch, coord, prop) {
13334    //compute children real areas
13335    var parentArea = coord.width * coord.height;
13336    var i, l=ch.length, totalChArea=0, chArea = [];
13337    for(i=0; i<l; i++) {
13338      chArea[i] = parseFloat(ch[i].getData('area', prop));
13339      totalChArea += chArea[i];
13340    }
13341    for(i=0; i<l; i++) {
13342      ch[i]._area = parentArea * chArea[i] / totalChArea;
13343    }
13344    var minimumSideValue = this.layout.horizontal()? coord.height : coord.width;
13345    ch.sort(function(a, b) { 
13346      var diff = b._area - a._area; 
13347      return diff? diff : (b.id == a.id? 0 : (b.id < a.id? 1 : -1)); 
13348    });
13349    var initElem = [ch[0]];
13350    var tail = ch.slice(1);
13351    this.squarify(tail, initElem, minimumSideValue, coord, prop);
13352  },
13353
13354  /*
13355    Method: squarify
13356  
13357    Performs an heuristic method to calculate div elements sizes in order to have a good aspect ratio.
13358  
13359     Parameters:
13360
13361        tail - An array of nodes.  
13362        initElem - An array of nodes, containing the initial node to be laid out.
13363        w - A fixed dimension where nodes will be laid out.
13364        coord - A coordinates object specifying width, height, left and top style properties.
13365  */
13366  squarify: function(tail, initElem, w, coord, prop) {
13367    this.computeDim(tail, initElem, w, coord, this.worstAspectRatio, prop);
13368  },
13369  
13370  /*
13371     Method: layoutRow
13372  
13373    Performs the layout of an array of nodes.
13374  
13375     Parameters:
13376
13377        ch - An array of nodes.  
13378        w - A fixed dimension where nodes will be laid out.
13379        coord - A coordinates object specifying width, height, left and top style properties.
13380  */
13381  layoutRow: function(ch, w, coord, prop) {
13382    if(this.layout.horizontal()) {
13383      return this.layoutV(ch, w, coord, prop);
13384    } else {
13385      return this.layoutH(ch, w, coord, prop);
13386    }
13387  },
13388  
13389  layoutV: function(ch, w, coord, prop) {
13390    var totalArea = 0, rnd = function(x) { return x; }; 
13391    $.each(ch, function(elem) { totalArea += elem._area; });
13392    var width = rnd(totalArea / w), top =  0; 
13393    for(var i=0, l=ch.length; i<l; i++) {
13394      var h = rnd(ch[i]._area / width);
13395      var chi = ch[i];
13396      chi.getPos(prop).setc(coord.left, coord.top + top);
13397      chi.setData('width', width, prop);
13398      chi.setData('height', h, prop);
13399      top += h;
13400    }
13401    var ans = {
13402      'height': coord.height,
13403      'width': coord.width - width,
13404      'top': coord.top,
13405      'left': coord.left + width
13406    };
13407    //take minimum side value.
13408    ans.dim = Math.min(ans.width, ans.height);
13409    if(ans.dim != ans.height) this.layout.change();
13410    return ans;
13411  },
13412  
13413  layoutH: function(ch, w, coord, prop) {
13414    var totalArea = 0; 
13415    $.each(ch, function(elem) { totalArea += elem._area; });
13416    var height = totalArea / w,
13417        top = coord.top, 
13418        left = 0;
13419    
13420    for(var i=0, l=ch.length; i<l; i++) {
13421      var chi = ch[i];
13422      var w = chi._area / height;
13423      chi.getPos(prop).setc(coord.left + left, top);
13424      chi.setData('width', w, prop);
13425      chi.setData('height', height, prop);
13426      left += w;
13427    }
13428    var ans = {
13429      'height': coord.height - height,
13430      'width': coord.width,
13431      'top': coord.top + height,
13432      'left': coord.left
13433    };
13434    ans.dim = Math.min(ans.width, ans.height);
13435    if(ans.dim != ans.width) this.layout.change();
13436    return ans;
13437  }
13438 });
13439
13440 Layouts.TM.Strip = new Class({
13441   Implements: Layouts.TM.Area,
13442
13443     /*
13444       Method: compute
13445     
13446      Called by loadJSON to calculate recursively all node positions and lay out the tree.
13447     
13448       Parameters:
13449     
13450          json - A JSON subtree. See also <Loader.loadJSON>. 
13451        coord - A coordinates object specifying width, height, left and top style properties.
13452     */
13453     computePositions: function(node, coord, prop) {
13454      var  ch = node.getSubnodes([1, 1], "ignore"), 
13455           config = this.config,
13456           max = Math.max;
13457      if(ch.length > 0) {
13458        this.processChildrenLayout(node, ch, coord, prop);
13459        for(var i=0, l=ch.length; i<l; i++) {
13460          var chi = ch[i];
13461          var offst = config.offset,
13462              height = max(chi.getData('height', prop) - offst - config.titleHeight, 0),
13463              width  = max(chi.getData('width', prop)  - offst, 0);
13464          var chipos = chi.getPos(prop);
13465          coord = {
13466            'width': width,
13467            'height': height,
13468            'top': chipos.y + config.titleHeight,
13469            'left': chipos.x
13470          };
13471          this.computePositions(chi, coord, prop);
13472        }
13473      }
13474     },
13475     
13476     /*
13477       Method: processChildrenLayout
13478     
13479      Computes children real areas and other useful parameters for performing the Strip algorithm.
13480     
13481       Parameters:
13482     
13483          par - The parent node of the json subtree.  
13484          ch - An Array of nodes
13485          coord - A coordinates object specifying width, height, left and top style properties.
13486     */
13487     processChildrenLayout: function(par, ch, coord, prop) {
13488      //compute children real areas
13489       var parentArea = coord.width * coord.height;
13490       var i, l=ch.length, totalChArea=0, chArea = [];
13491       for(i=0; i<l; i++) {
13492         chArea[i] = +ch[i].getData('area', prop);
13493         totalChArea += chArea[i];
13494       }
13495       for(i=0; i<l; i++) {
13496         ch[i]._area = parentArea * chArea[i] / totalChArea;
13497       }
13498      var side = this.layout.horizontal()? coord.width : coord.height;
13499      var initElem = [ch[0]];
13500      var tail = ch.slice(1);
13501      this.stripify(tail, initElem, side, coord, prop);
13502     },
13503     
13504     /*
13505       Method: stripify
13506     
13507      Performs an heuristic method to calculate div elements sizes in order to have 
13508      a good compromise between aspect ratio and order.
13509     
13510       Parameters:
13511     
13512          tail - An array of nodes.  
13513          initElem - An array of nodes.
13514          w - A fixed dimension where nodes will be layed out.
13515        coord - A coordinates object specifying width, height, left and top style properties.
13516     */
13517     stripify: function(tail, initElem, w, coord, prop) {
13518      this.computeDim(tail, initElem, w, coord, this.avgAspectRatio, prop);
13519     },
13520     
13521     /*
13522       Method: layoutRow
13523     
13524      Performs the layout of an array of nodes.
13525     
13526       Parameters:
13527     
13528          ch - An array of nodes.  
13529          w - A fixed dimension where nodes will be laid out.
13530          coord - A coordinates object specifying width, height, left and top style properties.
13531     */
13532     layoutRow: function(ch, w, coord, prop) {
13533      if(this.layout.horizontal()) {
13534        return this.layoutH(ch, w, coord, prop);
13535      } else {
13536        return this.layoutV(ch, w, coord, prop);
13537      }
13538     },
13539     
13540     layoutV: function(ch, w, coord, prop) {
13541      var totalArea = 0; 
13542      $.each(ch, function(elem) { totalArea += elem._area; });
13543      var width = totalArea / w, top =  0; 
13544      for(var i=0, l=ch.length; i<l; i++) {
13545        var chi = ch[i];
13546        var h = chi._area / width;
13547        chi.getPos(prop).setc(coord.left, 
13548            coord.top + (w - h - top));
13549        chi.setData('width', width, prop);
13550        chi.setData('height', h, prop);
13551        top += h;
13552      }
13553     
13554      return {
13555        'height': coord.height,
13556        'width': coord.width - width,
13557        'top': coord.top,
13558        'left': coord.left + width,
13559        'dim': w
13560      };
13561     },
13562     
13563     layoutH: function(ch, w, coord, prop) {
13564      var totalArea = 0; 
13565      $.each(ch, function(elem) { totalArea += elem._area; });
13566      var height = totalArea / w,
13567          top = coord.height - height, 
13568          left = 0;
13569      
13570      for(var i=0, l=ch.length; i<l; i++) {
13571        var chi = ch[i];
13572        var s = chi._area / height;
13573        chi.getPos(prop).setc(coord.left + left, coord.top + top);
13574        chi.setData('width', s, prop);
13575        chi.setData('height', height, prop);
13576        left += s;
13577      }
13578      return {
13579        'height': coord.height - height,
13580        'width': coord.width,
13581        'top': coord.top,
13582        'left': coord.left,
13583        'dim': w
13584      };
13585     }
13586  });
13587
13588
13589 /*
13590  * Class: Layouts.Icicle
13591  *
13592  * Implements the icicle tree layout.
13593  *
13594  * Implemented By:
13595  *
13596  * <Icicle>
13597  *
13598  */
13599
13600 Layouts.Icicle = new Class({
13601  /*
13602   * Method: compute
13603   *
13604   * Called by loadJSON to calculate all node positions.
13605   *
13606   * Parameters:
13607   *
13608   * posType - The nodes' position to compute. Either "start", "end" or
13609   *            "current". Defaults to "current".
13610   */
13611   compute: function(posType) {
13612     posType = posType || "current";
13613
13614     var root = this.graph.getNode(this.root),
13615         config = this.config,
13616         size = this.canvas.getSize(),
13617         width = size.width,
13618         height = size.height,
13619         offset = config.offset,
13620         levelsToShow = config.constrained ? config.levelsToShow : Number.MAX_VALUE;
13621
13622     this.controller.onBeforeCompute(root);
13623
13624     Graph.Util.computeLevels(this.graph, root.id, 0, "ignore");
13625
13626     var treeDepth = 0;
13627
13628     Graph.Util.eachLevel(root, 0, false, function (n, d) { if(d > treeDepth) treeDepth = d; });
13629
13630     var startNode = this.graph.getNode(this.clickedNode && this.clickedNode.id || root.id);
13631     var maxDepth = Math.min(treeDepth, levelsToShow-1);
13632     var initialDepth = startNode._depth;
13633     if(this.layout.horizontal()) {
13634       this.computeSubtree(startNode, -width/2, -height/2, width/(maxDepth+1), height, initialDepth, maxDepth, posType);
13635     } else {
13636       this.computeSubtree(startNode, -width/2, -height/2, width, height/(maxDepth+1), initialDepth, maxDepth, posType);
13637     }
13638   },
13639
13640   computeSubtree: function (root, x, y, width, height, initialDepth, maxDepth, posType) {
13641     root.getPos(posType).setc(x, y);
13642     root.setData('width', width, posType);
13643     root.setData('height', height, posType);
13644
13645     var nodeLength, prevNodeLength = 0, totalDim = 0;
13646     var children = Graph.Util.getSubnodes(root, [1, 1], 'ignore'); // next level from this node
13647
13648     if(!children.length)
13649       return;
13650
13651     $.each(children, function(e) { totalDim += e.getData('dim'); });
13652
13653     for(var i=0, l=children.length; i < l; i++) {
13654       if(this.layout.horizontal()) {
13655         nodeLength = height * children[i].getData('dim') / totalDim;
13656         this.computeSubtree(children[i], x+width, y, width, nodeLength, initialDepth, maxDepth, posType);
13657         y += nodeLength;
13658       } else {
13659         nodeLength = width * children[i].getData('dim') / totalDim;
13660         this.computeSubtree(children[i], x, y+height, nodeLength, height, initialDepth, maxDepth, posType);
13661         x += nodeLength;
13662       }
13663     }
13664   }
13665 });
13666
13667
13668
13669 /*
13670  * File: Icicle.js
13671  *
13672 */
13673
13674 /*
13675   Class: Icicle
13676   
13677   Icicle space filling visualization.
13678   
13679   Implements:
13680   
13681   All <Loader> methods
13682   
13683   Constructor Options:
13684   
13685   Inherits options from
13686   
13687   - <Options.Canvas>
13688   - <Options.Controller>
13689   - <Options.Node>
13690   - <Options.Edge>
13691   - <Options.Label>
13692   - <Options.Events>
13693   - <Options.Tips>
13694   - <Options.NodeStyles>
13695   - <Options.Navigation>
13696   
13697   Additionally, there are other parameters and some default values changed
13698
13699   orientation - (string) Default's *h*. Whether to set horizontal or vertical layouts. Possible values are 'h' and 'v'.
13700   offset - (number) Default's *2*. Boxes offset.
13701   constrained - (boolean) Default's *false*. Whether to show the entire tree when loaded or just the number of levels specified by _levelsToShow_.
13702   levelsToShow - (number) Default's *3*. The number of levels to show for a subtree. This number is relative to the selected node.
13703   animate - (boolean) Default's *false*. Whether to animate transitions.
13704   Node.type - Described in <Options.Node>. Default's *rectangle*.
13705   Label.type - Described in <Options.Label>. Default's *Native*.
13706   duration - Described in <Options.Fx>. Default's *700*.
13707   fps - Described in <Options.Fx>. Default's *45*.
13708   
13709   Instance Properties:
13710   
13711   canvas - Access a <Canvas> instance.
13712   graph - Access a <Graph> instance.
13713   op - Access a <Icicle.Op> instance.
13714   fx - Access a <Icicle.Plot> instance.
13715   labels - Access a <Icicle.Label> interface implementation.
13716
13717 */
13718
13719 $jit.Icicle = new Class({
13720   Implements: [ Loader, Extras, Layouts.Icicle ],
13721
13722   layout: {
13723     orientation: "h",
13724     vertical: function(){
13725       return this.orientation == "v";
13726     },
13727     horizontal: function(){
13728       return this.orientation == "h";
13729     },
13730     change: function(){
13731       this.orientation = this.vertical()? "h" : "v";
13732     }
13733   },
13734
13735   initialize: function(controller) {
13736     var config = {
13737       animate: false,
13738       orientation: "h",
13739       offset: 2,
13740       levelsToShow: Number.MAX_VALUE,
13741       constrained: false,
13742       Node: {
13743         type: 'rectangle',
13744         overridable: true
13745       },
13746       Edge: {
13747         type: 'none'
13748       },
13749       Label: {
13750         type: 'Native'
13751       },
13752       duration: 700,
13753       fps: 45
13754     };
13755
13756     var opts = Options("Canvas", "Node", "Edge", "Fx", "Tips", "NodeStyles",
13757                        "Events", "Navigation", "Controller", "Label");
13758     this.controller = this.config = $.merge(opts, config, controller);
13759     this.layout.orientation = this.config.orientation;
13760
13761     var canvasConfig = this.config;
13762     if (canvasConfig.useCanvas) {
13763       this.canvas = canvasConfig.useCanvas;
13764       this.config.labelContainer = this.canvas.id + '-label';
13765     } else {
13766       this.canvas = new Canvas(this, canvasConfig);
13767       this.config.labelContainer = (typeof canvasConfig.injectInto == 'string'? canvasConfig.injectInto : canvasConfig.injectInto.id) + '-label';
13768     }
13769
13770     this.graphOptions = {
13771       'klass': Complex,
13772       'Node': {
13773         'selected': false,
13774         'exist': true,
13775         'drawn': true
13776       }
13777     };
13778
13779     this.graph = new Graph(
13780       this.graphOptions, this.config.Node, this.config.Edge, this.config.Label);
13781
13782     this.labels = new $jit.Icicle.Label[this.config.Label.type](this);
13783     this.fx = new $jit.Icicle.Plot(this, $jit.Icicle);
13784     this.op = new $jit.Icicle.Op(this);
13785     this.group = new $jit.Icicle.Group(this);
13786     this.clickedNode = null;
13787
13788     this.initializeExtras();
13789   },
13790
13791   /* 
13792     Method: refresh 
13793     
13794     Computes positions and plots the tree.
13795   */
13796   refresh: function(){
13797     var labelType = this.config.Label.type;
13798     if(labelType != 'Native') {
13799       var that = this;
13800       this.graph.eachNode(function(n) { that.labels.hideLabel(n, false); });
13801     }
13802     this.compute();
13803     this.plot();
13804   },
13805
13806   /* 
13807     Method: plot 
13808     
13809     Plots the Icicle visualization. This is a shortcut to *fx.plot*. 
13810   
13811    */
13812   plot: function(){
13813     this.fx.plot(this.config);
13814   },
13815
13816   /* 
13817     Method: enter 
13818     
13819     Sets the node as root.
13820     
13821      Parameters:
13822      
13823      node - (object) A <Graph.Node>.
13824   
13825    */
13826   enter: function (node) {
13827     if (this.busy)
13828       return;
13829     this.busy = true;
13830
13831     var that = this,
13832         config = this.config;
13833
13834     var callback = {
13835       onComplete: function() {
13836         //compute positions of newly inserted nodes
13837         if(config.request)
13838           that.compute();
13839
13840         if(config.animate) {
13841           that.graph.nodeList.setDataset(['current', 'end'], {
13842             'alpha': [1, 0] //fade nodes
13843           });
13844
13845           Graph.Util.eachSubgraph(node, function(n) {
13846             n.setData('alpha', 1, 'end');
13847           }, "ignore");
13848
13849           that.fx.animate({
13850             duration: 500,
13851             modes:['node-property:alpha'],
13852             onComplete: function() {
13853               that.clickedNode = node;
13854               that.compute('end');
13855
13856               that.fx.animate({
13857                 modes:['linear', 'node-property:width:height'],
13858                 duration: 1000,
13859                 onComplete: function() {
13860                   that.busy = false;
13861                   that.clickedNode = node;
13862                 }
13863               });
13864             }
13865           });
13866         } else {
13867           that.clickedNode = node;
13868           that.busy = false;
13869           that.refresh();
13870         }
13871       }
13872     };
13873
13874     if(config.request) {
13875       this.requestNodes(clickedNode, callback);
13876     } else {
13877       callback.onComplete();
13878     }
13879   },
13880
13881   /* 
13882     Method: out 
13883     
13884     Sets the parent node of the current selected node as root.
13885   
13886    */
13887   out: function(){
13888     if(this.busy)
13889       return;
13890
13891     var that = this,
13892         GUtil = Graph.Util,
13893         config = this.config,
13894         graph = this.graph,
13895         parents = GUtil.getParents(graph.getNode(this.clickedNode && this.clickedNode.id || this.root)),
13896         parent = parents[0],
13897         clickedNode = parent,
13898         previousClickedNode = this.clickedNode;
13899
13900     this.busy = true;
13901     this.events.hoveredNode = false;
13902
13903     if(!parent) {
13904       this.busy = false;
13905       return;
13906     }
13907
13908     //final plot callback
13909     callback = {
13910       onComplete: function() {
13911         that.clickedNode = parent;
13912         if(config.request) {
13913           that.requestNodes(parent, {
13914             onComplete: function() {
13915               that.compute();
13916               that.plot();
13917               that.busy = false;
13918             }
13919           });
13920         } else {
13921           that.compute();
13922           that.plot();
13923           that.busy = false;
13924         }
13925       }
13926     };
13927
13928     //animate node positions
13929     if(config.animate) {
13930       this.clickedNode = clickedNode;
13931       this.compute('end');
13932       //animate the visible subtree only
13933       this.clickedNode = previousClickedNode;
13934       this.fx.animate({
13935         modes:['linear', 'node-property:width:height'],
13936         duration: 1000,
13937         onComplete: function() {
13938           //animate the parent subtree
13939           that.clickedNode = clickedNode;
13940           //change nodes alpha
13941           graph.nodeList.setDataset(['current', 'end'], {
13942             'alpha': [0, 1]
13943           });
13944           GUtil.eachSubgraph(previousClickedNode, function(node) {
13945             node.setData('alpha', 1);
13946           }, "ignore");
13947           that.fx.animate({
13948             duration: 500,
13949             modes:['node-property:alpha'],
13950             onComplete: function() {
13951               callback.onComplete();
13952             }
13953           });
13954         }
13955       });
13956     } else {
13957       callback.onComplete();
13958     }
13959   },
13960   requestNodes: function(node, onComplete){
13961     var handler = $.merge(this.controller, onComplete),
13962         levelsToShow = this.config.constrained ? this.config.levelsToShow : Number.MAX_VALUE;
13963
13964     if (handler.request) {
13965       var leaves = [], d = node._depth;
13966       Graph.Util.eachLevel(node, 0, levelsToShow, function(n){
13967         if (n.drawn && !Graph.Util.anySubnode(n)) {
13968           leaves.push(n);
13969           n._level = n._depth - d;
13970           if (this.config.constrained)
13971             n._level = levelsToShow - n._level;
13972
13973         }
13974       });
13975       this.group.requestNodes(leaves, handler);
13976     } else {
13977       handler.onComplete();
13978     }
13979   }
13980 });
13981
13982 /*
13983   Class: Icicle.Op
13984   
13985   Custom extension of <Graph.Op>.
13986   
13987   Extends:
13988   
13989   All <Graph.Op> methods
13990   
13991   See also:
13992   
13993   <Graph.Op>
13994   
13995   */
13996 $jit.Icicle.Op = new Class({
13997
13998   Implements: Graph.Op
13999
14000 });
14001
14002 /*
14003  * Performs operations on group of nodes.
14004  */
14005 $jit.Icicle.Group = new Class({
14006
14007   initialize: function(viz){
14008     this.viz = viz;
14009     this.canvas = viz.canvas;
14010     this.config = viz.config;
14011   },
14012
14013   /*
14014    * Calls the request method on the controller to request a subtree for each node.
14015    */
14016   requestNodes: function(nodes, controller){
14017     var counter = 0, len = nodes.length, nodeSelected = {};
14018     var complete = function(){
14019       controller.onComplete();
14020     };
14021     var viz = this.viz;
14022     if (len == 0)
14023       complete();
14024     for(var i = 0; i < len; i++) {
14025       nodeSelected[nodes[i].id] = nodes[i];
14026       controller.request(nodes[i].id, nodes[i]._level, {
14027         onComplete: function(nodeId, data){
14028           if (data && data.children) {
14029             data.id = nodeId;
14030             viz.op.sum(data, {
14031               type: 'nothing'
14032             });
14033           }
14034           if (++counter == len) {
14035             Graph.Util.computeLevels(viz.graph, viz.root, 0);
14036             complete();
14037           }
14038         }
14039       });
14040     }
14041   }
14042 });
14043
14044 /*
14045   Class: Icicle.Plot
14046   
14047   Custom extension of <Graph.Plot>.
14048   
14049   Extends:
14050   
14051   All <Graph.Plot> methods
14052   
14053   See also:
14054   
14055   <Graph.Plot>
14056   
14057   */
14058 $jit.Icicle.Plot = new Class({
14059   Implements: Graph.Plot,
14060
14061   plot: function(opt, animating){
14062     opt = opt || this.viz.controller;
14063     var viz = this.viz,
14064         graph = viz.graph,
14065         root = graph.getNode(viz.clickedNode && viz.clickedNode.id || viz.root),
14066         initialDepth = root._depth;
14067
14068     viz.canvas.clear();
14069     this.plotTree(root, $.merge(opt, {
14070       'withLabels': true,
14071       'hideLabels': false,
14072       'plotSubtree': function(root, node) {
14073         return !viz.config.constrained ||
14074                (node._depth - initialDepth < viz.config.levelsToShow);
14075       }
14076     }), animating);
14077   }
14078 });
14079
14080 /*
14081   Class: Icicle.Label
14082   
14083   Custom extension of <Graph.Label>. 
14084   Contains custom <Graph.Label.SVG>, <Graph.Label.HTML> and <Graph.Label.Native> extensions.
14085   
14086   Extends:
14087   
14088   All <Graph.Label> methods and subclasses.
14089   
14090   See also:
14091   
14092   <Graph.Label>, <Graph.Label.Native>, <Graph.Label.HTML>, <Graph.Label.SVG>.
14093   
14094   */
14095 $jit.Icicle.Label = {};
14096
14097 /*
14098   Icicle.Label.Native
14099   
14100   Custom extension of <Graph.Label.Native>.
14101   
14102   Extends:
14103   
14104   All <Graph.Label.Native> methods
14105   
14106   See also:
14107   
14108   <Graph.Label.Native>
14109
14110   */
14111 $jit.Icicle.Label.Native = new Class({
14112   Implements: Graph.Label.Native,
14113
14114   renderLabel: function(canvas, node, controller) {
14115     var ctx = canvas.getCtx(),
14116         width = node.getData('width'),
14117         height = node.getData('height'),
14118         size = node.getLabelData('size'),
14119         m = ctx.measureText(node.name);
14120
14121     // Guess as much as possible if the label will fit in the node
14122     if(height < (size * 1.5) || width < m.width)
14123       return;
14124
14125     var pos = node.pos.getc(true);
14126     ctx.fillText(node.name,
14127                  pos.x + width / 2,
14128                  pos.y + height / 2);
14129   }
14130 });
14131
14132 /*
14133   Icicle.Label.SVG
14134   
14135   Custom extension of <Graph.Label.SVG>.
14136   
14137   Extends:
14138   
14139   All <Graph.Label.SVG> methods
14140   
14141   See also:
14142   
14143   <Graph.Label.SVG>
14144 */
14145 $jit.Icicle.Label.SVG = new Class( {
14146   Implements: Graph.Label.SVG,
14147
14148   initialize: function(viz){
14149     this.viz = viz;
14150   },
14151
14152   /*
14153     placeLabel
14154    
14155     Overrides abstract method placeLabel in <Graph.Plot>.
14156    
14157     Parameters:
14158    
14159     tag - A DOM label element.
14160     node - A <Graph.Node>.
14161     controller - A configuration/controller object passed to the visualization.
14162    */
14163   placeLabel: function(tag, node, controller){
14164     var pos = node.pos.getc(true), canvas = this.viz.canvas;
14165     var radius = canvas.getSize();
14166     var labelPos = {
14167       x: Math.round(pos.x + radius.width / 2),
14168       y: Math.round(pos.y + radius.height / 2)
14169     };
14170     tag.setAttribute('x', labelPos.x);
14171     tag.setAttribute('y', labelPos.y);
14172
14173     controller.onPlaceLabel(tag, node);
14174   }
14175 });
14176
14177 /*
14178   Icicle.Label.HTML
14179   
14180   Custom extension of <Graph.Label.HTML>.
14181   
14182   Extends:
14183   
14184   All <Graph.Label.HTML> methods.
14185   
14186   See also:
14187   
14188   <Graph.Label.HTML>
14189   
14190   */
14191 $jit.Icicle.Label.HTML = new Class( {
14192   Implements: Graph.Label.HTML,
14193
14194   initialize: function(viz){
14195     this.viz = viz;
14196   },
14197
14198   /*
14199     placeLabel
14200    
14201     Overrides abstract method placeLabel in <Graph.Plot>.
14202    
14203     Parameters:
14204    
14205     tag - A DOM label element.
14206     node - A <Graph.Node>.
14207     controller - A configuration/controller object passed to the visualization.
14208    */
14209   placeLabel: function(tag, node, controller){
14210     var pos = node.pos.getc(true), canvas = this.viz.canvas;
14211     var radius = canvas.getSize();
14212     var labelPos = {
14213       x: Math.round(pos.x + radius.width / 2),
14214       y: Math.round(pos.y + radius.height / 2)
14215     };
14216
14217     var style = tag.style;
14218     style.left = labelPos.x + 'px';
14219     style.top = labelPos.y + 'px';
14220     style.display = '';
14221
14222     controller.onPlaceLabel(tag, node);
14223   }
14224 });
14225
14226 /*
14227   Class: Icicle.Plot.NodeTypes
14228   
14229   This class contains a list of <Graph.Node> built-in types. 
14230   Node types implemented are 'none', 'rectangle'.
14231   
14232   You can add your custom node types, customizing your visualization to the extreme.
14233   
14234   Example:
14235   
14236   (start code js)
14237     Icicle.Plot.NodeTypes.implement({
14238       'mySpecialType': {
14239         'render': function(node, canvas) {
14240           //print your custom node to canvas
14241         },
14242         //optional
14243         'contains': function(node, pos) {
14244           //return true if pos is inside the node or false otherwise
14245         }
14246       }
14247     });
14248   (end code)
14249   
14250   */
14251 $jit.Icicle.Plot.NodeTypes = new Class( {
14252   'none': {
14253     'render': $.empty
14254   },
14255
14256   'rectangle': {
14257     'render': function(node, canvas, animating) {
14258       var config = this.viz.config;
14259       var offset = config.offset;
14260       var width = node.getData('width');
14261       var height = node.getData('height');
14262       var border = node.getData('border');
14263       var pos = node.pos.getc(true);
14264       var posx = pos.x + offset / 2, posy = pos.y + offset / 2;
14265       var ctx = canvas.getCtx();
14266       
14267       if(width - offset < 2 || height - offset < 2) return;
14268       
14269       if(config.cushion) {
14270         var color = node.getData('color');
14271         var lg = ctx.createRadialGradient(posx + (width - offset)/2, 
14272                                           posy + (height - offset)/2, 1, 
14273                                           posx + (width-offset)/2, posy + (height-offset)/2, 
14274                                           width < height? height : width);
14275         var colorGrad = $.rgbToHex($.map($.hexToRgb(color), 
14276             function(r) { return r * 0.3 >> 0; }));
14277         lg.addColorStop(0, color);
14278         lg.addColorStop(1, colorGrad);
14279         ctx.fillStyle = lg;
14280       }
14281
14282       if (border) {
14283         ctx.strokeStyle = border;
14284         ctx.lineWidth = 3;
14285       }
14286
14287       ctx.fillRect(posx, posy, Math.max(0, width - offset), Math.max(0, height - offset));
14288       border && ctx.strokeRect(pos.x, pos.y, width, height);
14289     },
14290
14291     'contains': function(node, pos) {
14292       if(this.viz.clickedNode && !$jit.Graph.Util.isDescendantOf(node, this.viz.clickedNode.id)) return false;
14293       var npos = node.pos.getc(true),
14294           width = node.getData('width'),
14295           height = node.getData('height');
14296       return this.nodeHelper.rectangle.contains({x: npos.x + width/2, y: npos.y + height/2}, pos, width, height);
14297     }
14298   }
14299 });
14300
14301 $jit.Icicle.Plot.EdgeTypes = new Class( {
14302   'none': $.empty
14303 });
14304
14305
14306
14307 /*
14308  * File: Layouts.ForceDirected.js
14309  *
14310 */
14311
14312 /*
14313  * Class: Layouts.ForceDirected
14314  * 
14315  * Implements a Force Directed Layout.
14316  * 
14317  * Implemented By:
14318  * 
14319  * <ForceDirected>
14320  * 
14321  * Credits:
14322  * 
14323  * Marcus Cobden <http://marcuscobden.co.uk>
14324  * 
14325  */
14326 Layouts.ForceDirected = new Class({
14327
14328   getOptions: function(random) {
14329     var s = this.canvas.getSize();
14330     var w = s.width, h = s.height;
14331     //count nodes
14332     var count = 0;
14333     this.graph.eachNode(function(n) { 
14334       count++;
14335     });
14336     var k2 = w * h / count, k = Math.sqrt(k2);
14337     var l = this.config.levelDistance;
14338     
14339     return {
14340       width: w,
14341       height: h,
14342       tstart: w * 0.1,
14343       nodef: function(x) { return k2 / (x || 1); },
14344       edgef: function(x) { return /* x * x / k; */ k * (x - l); }
14345     };
14346   },
14347   
14348   compute: function(property, incremental) {
14349     var prop = $.splat(property || ['current', 'start', 'end']);
14350     var opt = this.getOptions();
14351     NodeDim.compute(this.graph, prop, this.config);
14352     this.graph.computeLevels(this.root, 0, "ignore");
14353     this.graph.eachNode(function(n) {
14354       $.each(prop, function(p) {
14355         var pos = n.getPos(p);
14356         if(pos.equals(Complex.KER)) {
14357           pos.x = opt.width/5 * (Math.random() - 0.5);
14358           pos.y = opt.height/5 * (Math.random() - 0.5);
14359         }
14360         //initialize disp vector
14361         n.disp = {};
14362         $.each(prop, function(p) {
14363           n.disp[p] = $C(0, 0);
14364         });
14365       });
14366     });
14367     this.computePositions(prop, opt, incremental);
14368   },
14369   
14370   computePositions: function(property, opt, incremental) {
14371     var times = this.config.iterations, i = 0, that = this;
14372     if(incremental) {
14373       (function iter() {
14374         for(var total=incremental.iter, j=0; j<total; j++) {
14375           opt.t = opt.tstart;
14376           if(times) opt.t *= (1 - i++/(times -1));
14377           that.computePositionStep(property, opt);
14378           if(times && i >= times) {
14379             incremental.onComplete();
14380             return;
14381           }
14382         }
14383         incremental.onStep(Math.round(i / (times -1) * 100));
14384         setTimeout(iter, 1);
14385       })();
14386     } else {
14387       for(; i < times; i++) {
14388         opt.t = opt.tstart * (1 - i/(times -1));
14389         this.computePositionStep(property, opt);
14390       }
14391     }
14392   },
14393   
14394   computePositionStep: function(property, opt) {
14395     var graph = this.graph;
14396     var min = Math.min, max = Math.max;
14397     var dpos = $C(0, 0);
14398     //calculate repulsive forces
14399     graph.eachNode(function(v) {
14400       //initialize disp
14401       $.each(property, function(p) {
14402         v.disp[p].x = 0; v.disp[p].y = 0;
14403       });
14404       graph.eachNode(function(u) {
14405         if(u.id != v.id) {
14406           $.each(property, function(p) {
14407             var vp = v.getPos(p), up = u.getPos(p);
14408             dpos.x = vp.x - up.x;
14409             dpos.y = vp.y - up.y;
14410             var norm = dpos.norm() || 1;
14411             v.disp[p].$add(dpos
14412                 .$scale(opt.nodef(norm) / norm));
14413           });
14414         }
14415       });
14416     });
14417     //calculate attractive forces
14418     var T = !!graph.getNode(this.root).visited;
14419     graph.eachNode(function(node) {
14420       node.eachAdjacency(function(adj) {
14421         var nodeTo = adj.nodeTo;
14422         if(!!nodeTo.visited === T) {
14423           $.each(property, function(p) {
14424             var vp = node.getPos(p), up = nodeTo.getPos(p);
14425             dpos.x = vp.x - up.x;
14426             dpos.y = vp.y - up.y;
14427             var norm = dpos.norm() || 1;
14428             node.disp[p].$add(dpos.$scale(-opt.edgef(norm) / norm));
14429             nodeTo.disp[p].$add(dpos.$scale(-1));
14430           });
14431         }
14432       });
14433       node.visited = !T;
14434     });
14435     //arrange positions to fit the canvas
14436     var t = opt.t, w2 = opt.width / 2, h2 = opt.height / 2;
14437     graph.eachNode(function(u) {
14438       $.each(property, function(p) {
14439         var disp = u.disp[p];
14440         var norm = disp.norm() || 1;
14441         var p = u.getPos(p);
14442         p.$add($C(disp.x * min(Math.abs(disp.x), t) / norm, 
14443             disp.y * min(Math.abs(disp.y), t) / norm));
14444         p.x = min(w2, max(-w2, p.x));
14445         p.y = min(h2, max(-h2, p.y));
14446       });
14447     });
14448   }
14449 });
14450
14451 /*
14452  * File: ForceDirected.js
14453  */
14454
14455 /*
14456    Class: ForceDirected
14457       
14458    A visualization that lays graphs using a Force-Directed layout algorithm.
14459    
14460    Inspired by:
14461   
14462    Force-Directed Drawing Algorithms (Stephen G. Kobourov) <http://www.cs.brown.edu/~rt/gdhandbook/chapters/force-directed.pdf>
14463    
14464   Implements:
14465   
14466   All <Loader> methods
14467   
14468    Constructor Options:
14469    
14470    Inherits options from
14471    
14472    - <Options.Canvas>
14473    - <Options.Controller>
14474    - <Options.Node>
14475    - <Options.Edge>
14476    - <Options.Label>
14477    - <Options.Events>
14478    - <Options.Tips>
14479    - <Options.NodeStyles>
14480    - <Options.Navigation>
14481    
14482    Additionally, there are two parameters
14483    
14484    levelDistance - (number) Default's *50*. The natural length desired for the edges.
14485    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*. 
14486      
14487    Instance Properties:
14488
14489    canvas - Access a <Canvas> instance.
14490    graph - Access a <Graph> instance.
14491    op - Access a <ForceDirected.Op> instance.
14492    fx - Access a <ForceDirected.Plot> instance.
14493    labels - Access a <ForceDirected.Label> interface implementation.
14494
14495 */
14496
14497 $jit.ForceDirected = new Class( {
14498
14499   Implements: [ Loader, Extras, Layouts.ForceDirected ],
14500
14501   initialize: function(controller) {
14502     var $ForceDirected = $jit.ForceDirected;
14503
14504     var config = {
14505       iterations: 50,
14506       levelDistance: 50
14507     };
14508
14509     this.controller = this.config = $.merge(Options("Canvas", "Node", "Edge",
14510         "Fx", "Tips", "NodeStyles", "Events", "Navigation", "Controller", "Label"), config, controller);
14511
14512     var canvasConfig = this.config;
14513     if(canvasConfig.useCanvas) {
14514       this.canvas = canvasConfig.useCanvas;
14515       this.config.labelContainer = this.canvas.id + '-label';
14516     } else {
14517       if(canvasConfig.background) {
14518         canvasConfig.background = $.merge({
14519           type: 'Circles'
14520         }, canvasConfig.background);
14521       }
14522       this.canvas = new Canvas(this, canvasConfig);
14523       this.config.labelContainer = (typeof canvasConfig.injectInto == 'string'? canvasConfig.injectInto : canvasConfig.injectInto.id) + '-label';
14524     }
14525
14526     this.graphOptions = {
14527       'klass': Complex,
14528       'Node': {
14529         'selected': false,
14530         'exist': true,
14531         'drawn': true
14532       }
14533     };
14534     this.graph = new Graph(this.graphOptions, this.config.Node,
14535         this.config.Edge);
14536     this.labels = new $ForceDirected.Label[canvasConfig.Label.type](this);
14537     this.fx = new $ForceDirected.Plot(this, $ForceDirected);
14538     this.op = new $ForceDirected.Op(this);
14539     this.json = null;
14540     this.busy = false;
14541     // initialize extras
14542     this.initializeExtras();
14543   },
14544
14545   /* 
14546     Method: refresh 
14547     
14548     Computes positions and plots the tree.
14549   */
14550   refresh: function() {
14551     this.compute();
14552     this.plot();
14553   },
14554
14555   reposition: function() {
14556     this.compute('end');
14557   },
14558
14559 /*
14560   Method: computeIncremental
14561   
14562   Performs the Force Directed algorithm incrementally.
14563   
14564   Description:
14565   
14566   ForceDirected algorithms can perform many computations and lead to JavaScript taking too much time to complete. 
14567   This method splits the algorithm into smaller parts allowing the user to track the evolution of the algorithm and 
14568   avoiding browser messages such as "This script is taking too long to complete".
14569   
14570   Parameters:
14571   
14572   opt - (object) The object properties are described below
14573   
14574   iter - (number) Default's *20*. Split the algorithm into pieces of _iter_ iterations. For example, if the _iterations_ configuration property 
14575   of your <ForceDirected> class is 100, then you could set _iter_ to 20 to split the main algorithm into 5 smaller pieces.
14576   
14577   property - (string) Default's *end*. Whether to update starting, current or ending node positions. Possible values are 'end', 'start', 'current'. 
14578   You can also set an array of these properties. If you'd like to keep the current node positions but to perform these 
14579   computations for final animation positions then you can just choose 'end'.
14580   
14581   onStep - (function) A callback function called when each "small part" of the algorithm completed. This function gets as first formal 
14582   parameter a percentage value.
14583   
14584   onComplete - A callback function called when the algorithm completed.
14585   
14586   Example:
14587   
14588   In this example I calculate the end positions and then animate the graph to those positions
14589   
14590   (start code js)
14591   var fd = new $jit.ForceDirected(...);
14592   fd.computeIncremental({
14593     iter: 20,
14594     property: 'end',
14595     onStep: function(perc) {
14596       Log.write("loading " + perc + "%");
14597     },
14598     onComplete: function() {
14599       Log.write("done");
14600       fd.animate();
14601     }
14602   });
14603   (end code)
14604   
14605   In this example I calculate all positions and (re)plot the graph
14606   
14607   (start code js)
14608   var fd = new ForceDirected(...);
14609   fd.computeIncremental({
14610     iter: 20,
14611     property: ['end', 'start', 'current'],
14612     onStep: function(perc) {
14613       Log.write("loading " + perc + "%");
14614     },
14615     onComplete: function() {
14616       Log.write("done");
14617       fd.plot();
14618     }
14619   });
14620   (end code)
14621   
14622   */
14623   computeIncremental: function(opt) {
14624     opt = $.merge( {
14625       iter: 20,
14626       property: 'end',
14627       onStep: $.empty,
14628       onComplete: $.empty
14629     }, opt || {});
14630
14631     this.config.onBeforeCompute(this.graph.getNode(this.root));
14632     this.compute(opt.property, opt);
14633   },
14634
14635   /*
14636     Method: plot
14637    
14638     Plots the ForceDirected graph. This is a shortcut to *fx.plot*.
14639    */
14640   plot: function() {
14641     this.fx.plot();
14642   },
14643
14644   /*
14645      Method: animate
14646     
14647      Animates the graph from the current positions to the 'end' node positions.
14648   */
14649   animate: function(opt) {
14650     this.fx.animate($.merge( {
14651       modes: [ 'linear' ]
14652     }, opt || {}));
14653   }
14654 });
14655
14656 $jit.ForceDirected.$extend = true;
14657
14658 (function(ForceDirected) {
14659
14660   /*
14661      Class: ForceDirected.Op
14662      
14663      Custom extension of <Graph.Op>.
14664
14665      Extends:
14666
14667      All <Graph.Op> methods
14668      
14669      See also:
14670      
14671      <Graph.Op>
14672
14673   */
14674   ForceDirected.Op = new Class( {
14675
14676     Implements: Graph.Op
14677
14678   });
14679
14680   /*
14681     Class: ForceDirected.Plot
14682     
14683     Custom extension of <Graph.Plot>.
14684   
14685     Extends:
14686   
14687     All <Graph.Plot> methods
14688     
14689     See also:
14690     
14691     <Graph.Plot>
14692   
14693   */
14694   ForceDirected.Plot = new Class( {
14695
14696     Implements: Graph.Plot
14697
14698   });
14699
14700   /*
14701     Class: ForceDirected.Label
14702     
14703     Custom extension of <Graph.Label>. 
14704     Contains custom <Graph.Label.SVG>, <Graph.Label.HTML> and <Graph.Label.Native> extensions.
14705   
14706     Extends:
14707   
14708     All <Graph.Label> methods and subclasses.
14709   
14710     See also:
14711   
14712     <Graph.Label>, <Graph.Label.Native>, <Graph.Label.HTML>, <Graph.Label.SVG>.
14713   
14714   */
14715   ForceDirected.Label = {};
14716
14717   /*
14718      ForceDirected.Label.Native
14719      
14720      Custom extension of <Graph.Label.Native>.
14721
14722      Extends:
14723
14724      All <Graph.Label.Native> methods
14725
14726      See also:
14727
14728      <Graph.Label.Native>
14729
14730   */
14731   ForceDirected.Label.Native = new Class( {
14732     Implements: Graph.Label.Native
14733   });
14734
14735   /*
14736     ForceDirected.Label.SVG
14737     
14738     Custom extension of <Graph.Label.SVG>.
14739   
14740     Extends:
14741   
14742     All <Graph.Label.SVG> methods
14743   
14744     See also:
14745   
14746     <Graph.Label.SVG>
14747   
14748   */
14749   ForceDirected.Label.SVG = new Class( {
14750     Implements: Graph.Label.SVG,
14751
14752     initialize: function(viz) {
14753       this.viz = viz;
14754     },
14755
14756     /* 
14757        placeLabel
14758
14759        Overrides abstract method placeLabel in <Graph.Label>.
14760
14761        Parameters:
14762
14763        tag - A DOM label element.
14764        node - A <Graph.Node>.
14765        controller - A configuration/controller object passed to the visualization.
14766       
14767      */
14768     placeLabel: function(tag, node, controller) {
14769       var pos = node.pos.getc(true), 
14770           canvas = this.viz.canvas,
14771           ox = canvas.translateOffsetX,
14772           oy = canvas.translateOffsetY,
14773           sx = canvas.scaleOffsetX,
14774           sy = canvas.scaleOffsetY,
14775           radius = canvas.getSize();
14776       var labelPos = {
14777         x: Math.round(pos.x * sx + ox + radius.width / 2),
14778         y: Math.round(pos.y * sy + oy + radius.height / 2)
14779       };
14780       tag.setAttribute('x', labelPos.x);
14781       tag.setAttribute('y', labelPos.y);
14782
14783       controller.onPlaceLabel(tag, node);
14784     }
14785   });
14786
14787   /*
14788      ForceDirected.Label.HTML
14789      
14790      Custom extension of <Graph.Label.HTML>.
14791
14792      Extends:
14793
14794      All <Graph.Label.HTML> methods.
14795
14796      See also:
14797
14798      <Graph.Label.HTML>
14799
14800   */
14801   ForceDirected.Label.HTML = new Class( {
14802     Implements: Graph.Label.HTML,
14803
14804     initialize: function(viz) {
14805       this.viz = viz;
14806     },
14807     /* 
14808        placeLabel
14809
14810        Overrides abstract method placeLabel in <Graph.Plot>.
14811
14812        Parameters:
14813
14814        tag - A DOM label element.
14815        node - A <Graph.Node>.
14816        controller - A configuration/controller object passed to the visualization.
14817       
14818      */
14819     placeLabel: function(tag, node, controller) {
14820       var pos = node.pos.getc(true), 
14821           canvas = this.viz.canvas,
14822           ox = canvas.translateOffsetX,
14823           oy = canvas.translateOffsetY,
14824           sx = canvas.scaleOffsetX,
14825           sy = canvas.scaleOffsetY,
14826           radius = canvas.getSize();
14827       var labelPos = {
14828         x: Math.round(pos.x * sx + ox + radius.width / 2),
14829         y: Math.round(pos.y * sy + oy + radius.height / 2)
14830       };
14831       var style = tag.style;
14832       style.left = labelPos.x + 'px';
14833       style.top = labelPos.y + 'px';
14834       style.display = this.fitsInCanvas(labelPos, canvas) ? '' : 'none';
14835
14836       controller.onPlaceLabel(tag, node);
14837     }
14838   });
14839
14840   /*
14841     Class: ForceDirected.Plot.NodeTypes
14842
14843     This class contains a list of <Graph.Node> built-in types. 
14844     Node types implemented are 'none', 'circle', 'triangle', 'rectangle', 'star', 'ellipse' and 'square'.
14845
14846     You can add your custom node types, customizing your visualization to the extreme.
14847
14848     Example:
14849
14850     (start code js)
14851       ForceDirected.Plot.NodeTypes.implement({
14852         'mySpecialType': {
14853           'render': function(node, canvas) {
14854             //print your custom node to canvas
14855           },
14856           //optional
14857           'contains': function(node, pos) {
14858             //return true if pos is inside the node or false otherwise
14859           }
14860         }
14861       });
14862     (end code)
14863
14864   */
14865   ForceDirected.Plot.NodeTypes = new Class({
14866     'none': {
14867       'render': $.empty,
14868       'contains': $.lambda(false)
14869     },
14870     'circle': {
14871       'render': function(node, canvas){
14872         var pos = node.pos.getc(true), 
14873             dim = node.getData('dim');
14874         this.nodeHelper.circle.render('fill', pos, dim, canvas);
14875       },
14876       'contains': function(node, pos){
14877         var npos = node.pos.getc(true), 
14878             dim = node.getData('dim');
14879         return this.nodeHelper.circle.contains(npos, pos, dim);
14880       }
14881     },
14882     'ellipse': {
14883       'render': function(node, canvas){
14884         var pos = node.pos.getc(true), 
14885             width = node.getData('width'), 
14886             height = node.getData('height');
14887         this.nodeHelper.ellipse.render('fill', pos, width, height, canvas);
14888         },
14889       'contains': function(node, pos){
14890         var npos = node.pos.getc(true), 
14891             width = node.getData('width'), 
14892             height = node.getData('height');
14893         return this.nodeHelper.ellipse.contains(npos, pos, width, height);
14894       }
14895     },
14896     'square': {
14897       'render': function(node, canvas){
14898         var pos = node.pos.getc(true), 
14899             dim = node.getData('dim');
14900         this.nodeHelper.square.render('fill', pos, dim, canvas);
14901       },
14902       'contains': function(node, pos){
14903         var npos = node.pos.getc(true), 
14904             dim = node.getData('dim');
14905         return this.nodeHelper.square.contains(npos, pos, dim);
14906       }
14907     },
14908     'rectangle': {
14909       'render': function(node, canvas){
14910         var pos = node.pos.getc(true), 
14911             width = node.getData('width'), 
14912             height = node.getData('height');
14913         this.nodeHelper.rectangle.render('fill', pos, width, height, canvas);
14914       },
14915       'contains': function(node, pos){
14916         var npos = node.pos.getc(true), 
14917             width = node.getData('width'), 
14918             height = node.getData('height');
14919         return this.nodeHelper.rectangle.contains(npos, pos, width, height);
14920       }
14921     },
14922     'triangle': {
14923       'render': function(node, canvas){
14924         var pos = node.pos.getc(true), 
14925             dim = node.getData('dim');
14926         this.nodeHelper.triangle.render('fill', pos, dim, canvas);
14927       },
14928       'contains': function(node, pos) {
14929         var npos = node.pos.getc(true), 
14930             dim = node.getData('dim');
14931         return this.nodeHelper.triangle.contains(npos, pos, dim);
14932       }
14933     },
14934     'star': {
14935       'render': function(node, canvas){
14936         var pos = node.pos.getc(true),
14937             dim = node.getData('dim');
14938         this.nodeHelper.star.render('fill', pos, dim, canvas);
14939       },
14940       'contains': function(node, pos) {
14941         var npos = node.pos.getc(true),
14942             dim = node.getData('dim');
14943         return this.nodeHelper.star.contains(npos, pos, dim);
14944       }
14945     }
14946   });
14947
14948   /*
14949     Class: ForceDirected.Plot.EdgeTypes
14950   
14951     This class contains a list of <Graph.Adjacence> built-in types. 
14952     Edge types implemented are 'none', 'line' and 'arrow'.
14953   
14954     You can add your custom edge types, customizing your visualization to the extreme.
14955   
14956     Example:
14957   
14958     (start code js)
14959       ForceDirected.Plot.EdgeTypes.implement({
14960         'mySpecialType': {
14961           'render': function(adj, canvas) {
14962             //print your custom edge to canvas
14963           },
14964           //optional
14965           'contains': function(adj, pos) {
14966             //return true if pos is inside the arc or false otherwise
14967           }
14968         }
14969       });
14970     (end code)
14971   
14972   */
14973   ForceDirected.Plot.EdgeTypes = new Class({
14974     'none': $.empty,
14975     'line': {
14976       'render': function(adj, canvas) {
14977         var from = adj.nodeFrom.pos.getc(true),
14978             to = adj.nodeTo.pos.getc(true);
14979         this.edgeHelper.line.render(from, to, canvas);
14980       },
14981       'contains': function(adj, pos) {
14982         var from = adj.nodeFrom.pos.getc(true),
14983             to = adj.nodeTo.pos.getc(true);
14984         return this.edgeHelper.line.contains(from, to, pos, this.edge.epsilon);
14985       }
14986     },
14987     'arrow': {
14988       'render': function(adj, canvas) {
14989         var from = adj.nodeFrom.pos.getc(true),
14990             to = adj.nodeTo.pos.getc(true),
14991             dim = adj.getData('dim'),
14992             direction = adj.data.$direction,
14993             inv = (direction && direction.length>1 && direction[0] != adj.nodeFrom.id);
14994         this.edgeHelper.arrow.render(from, to, dim, inv, canvas);
14995       },
14996       'contains': function(adj, pos) {
14997         var from = adj.nodeFrom.pos.getc(true),
14998             to = adj.nodeTo.pos.getc(true);
14999         return this.edgeHelper.arrow.contains(from, to, pos, this.edge.epsilon);
15000       }
15001     }
15002   });
15003
15004 })($jit.ForceDirected);
15005
15006
15007 /*
15008  * File: Treemap.js
15009  *
15010 */
15011
15012 $jit.TM = {};
15013
15014 var TM = $jit.TM;
15015
15016 $jit.TM.$extend = true;
15017
15018 /*
15019   Class: TM.Base
15020   
15021   Abstract class providing base functionality for <TM.Squarified>, <TM.Strip> and <TM.SliceAndDice> visualizations.
15022   
15023   Implements:
15024   
15025   All <Loader> methods
15026   
15027   Constructor Options:
15028   
15029   Inherits options from
15030   
15031   - <Options.Canvas>
15032   - <Options.Controller>
15033   - <Options.Node>
15034   - <Options.Edge>
15035   - <Options.Label>
15036   - <Options.Events>
15037   - <Options.Tips>
15038   - <Options.NodeStyles>
15039   - <Options.Navigation>
15040   
15041   Additionally, there are other parameters and some default values changed
15042
15043   orientation - (string) Default's *h*. Whether to set horizontal or vertical layouts. Possible values are 'h' and 'v'.
15044   titleHeight - (number) Default's *13*. The height of the title rectangle for inner (non-leaf) nodes.
15045   offset - (number) Default's *2*. Boxes offset.
15046   constrained - (boolean) Default's *false*. Whether to show the entire tree when loaded or just the number of levels specified by _levelsToShow_.
15047   levelsToShow - (number) Default's *3*. The number of levels to show for a subtree. This number is relative to the selected node.
15048   animate - (boolean) Default's *false*. Whether to animate transitions.
15049   Node.type - Described in <Options.Node>. Default's *rectangle*.
15050   duration - Described in <Options.Fx>. Default's *700*.
15051   fps - Described in <Options.Fx>. Default's *45*.
15052   
15053   Instance Properties:
15054   
15055   canvas - Access a <Canvas> instance.
15056   graph - Access a <Graph> instance.
15057   op - Access a <TM.Op> instance.
15058   fx - Access a <TM.Plot> instance.
15059   labels - Access a <TM.Label> interface implementation.
15060
15061   Inspired by:
15062   
15063   Squarified Treemaps (Mark Bruls, Kees Huizing, and Jarke J. van Wijk) <http://www.win.tue.nl/~vanwijk/stm.pdf>
15064   
15065   Tree visualization with tree-maps: 2-d space-filling approach (Ben Shneiderman) <http://hcil.cs.umd.edu/trs/91-03/91-03.html>
15066   
15067    Note:
15068    
15069    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.
15070
15071 */
15072 TM.Base = {
15073   layout: {
15074     orientation: "h",
15075     vertical: function(){
15076       return this.orientation == "v";
15077     },
15078     horizontal: function(){
15079       return this.orientation == "h";
15080     },
15081     change: function(){
15082       this.orientation = this.vertical()? "h" : "v";
15083     }
15084   },
15085
15086   initialize: function(controller){
15087     var config = {
15088       orientation: "h",
15089       titleHeight: 13,
15090       offset: 2,
15091       levelsToShow: 0,
15092       constrained: false,
15093       animate: false,
15094       Node: {
15095         type: 'rectangle',
15096         overridable: true,
15097         //we all know why this is not zero,
15098         //right, Firefox?
15099         width: 3,
15100         height: 3,
15101         color: '#444'
15102       },
15103       Label: {
15104         textAlign: 'center',
15105         textBaseline: 'top'
15106       },
15107       Edge: {
15108         type: 'none'
15109       },
15110       duration: 700,
15111       fps: 45
15112     };
15113
15114     this.controller = this.config = $.merge(Options("Canvas", "Node", "Edge",
15115         "Fx", "Controller", "Tips", "NodeStyles", "Events", "Navigation", "Label"), config, controller);
15116     this.layout.orientation = this.config.orientation;
15117
15118     var canvasConfig = this.config;
15119     if (canvasConfig.useCanvas) {
15120       this.canvas = canvasConfig.useCanvas;
15121       this.config.labelContainer = this.canvas.id + '-label';
15122     } else {
15123       if(canvasConfig.background) {
15124         canvasConfig.background = $.merge({
15125           type: 'Circles'
15126         }, canvasConfig.background);
15127       }
15128       this.canvas = new Canvas(this, canvasConfig);
15129       this.config.labelContainer = (typeof canvasConfig.injectInto == 'string'? canvasConfig.injectInto : canvasConfig.injectInto.id) + '-label';
15130     }
15131
15132     this.graphOptions = {
15133       'klass': Complex,
15134       'Node': {
15135         'selected': false,
15136         'exist': true,
15137         'drawn': true
15138       }
15139     };
15140     this.graph = new Graph(this.graphOptions, this.config.Node,
15141         this.config.Edge);
15142     this.labels = new TM.Label[canvasConfig.Label.type](this);
15143     this.fx = new TM.Plot(this);
15144     this.op = new TM.Op(this);
15145     this.group = new TM.Group(this);
15146     this.geom = new TM.Geom(this);
15147     this.clickedNode = null;
15148     this.busy = false;
15149     // initialize extras
15150     this.initializeExtras();
15151   },
15152
15153   /* 
15154     Method: refresh 
15155     
15156     Computes positions and plots the tree.
15157   */
15158   refresh: function(){
15159     if(this.busy) return;
15160     this.busy = true;
15161     var that = this;
15162     if(this.config.animate) {
15163       this.compute('end');
15164       this.config.levelsToShow > 0 && this.geom.setRightLevelToShow(this.graph.getNode(this.clickedNode 
15165           && this.clickedNode.id || this.root));
15166       this.fx.animate($.merge(this.config, {
15167         modes: ['linear', 'node-property:width:height'],
15168         onComplete: function() {
15169           that.busy = false;
15170         }
15171       }));
15172     } else {
15173       var labelType = this.config.Label.type;
15174       if(labelType != 'Native') {
15175         var that = this;
15176         this.graph.eachNode(function(n) { that.labels.hideLabel(n, false); });
15177       }
15178       this.busy = false;
15179       this.compute();
15180       this.config.levelsToShow > 0 && this.geom.setRightLevelToShow(this.graph.getNode(this.clickedNode 
15181           && this.clickedNode.id || this.root));
15182       this.plot();
15183     }
15184   },
15185
15186   /* 
15187     Method: plot 
15188     
15189     Plots the TreeMap. This is a shortcut to *fx.plot*. 
15190   
15191    */
15192   plot: function(){
15193     this.fx.plot();
15194   },
15195
15196   /* 
15197   Method: leaf 
15198   
15199   Returns whether the node is a leaf.
15200   
15201    Parameters:
15202    
15203    n - (object) A <Graph.Node>.
15204
15205  */
15206   leaf: function(n){
15207     return n.getSubnodes([
15208         1, 1
15209     ], "ignore").length == 0;
15210   },
15211   
15212   /* 
15213   Method: enter 
15214   
15215   Sets the node as root.
15216   
15217    Parameters:
15218    
15219    n - (object) A <Graph.Node>.
15220
15221  */
15222   enter: function(n){
15223     if(this.busy) return;
15224     this.busy = true;
15225     
15226     var that = this,
15227         config = this.config,
15228         graph = this.graph,
15229         clickedNode = n,
15230         previousClickedNode = this.clickedNode;
15231
15232     var callback = {
15233       onComplete: function() {
15234         //ensure that nodes are shown for that level
15235         if(config.levelsToShow > 0) {
15236           that.geom.setRightLevelToShow(n);
15237         }
15238         //compute positions of newly inserted nodes
15239         if(config.levelsToShow > 0 || config.request) that.compute();
15240         if(config.animate) {
15241           //fade nodes
15242           graph.nodeList.setData('alpha', 0, 'end');
15243           n.eachSubgraph(function(n) {
15244             n.setData('alpha', 1, 'end');
15245           }, "ignore");
15246           that.fx.animate({
15247             duration: 500,
15248             modes:['node-property:alpha'],
15249             onComplete: function() {
15250               //compute end positions
15251               that.clickedNode = clickedNode;
15252               that.compute('end');
15253               //animate positions
15254               //TODO(nico) commenting this line didn't seem to throw errors...
15255               that.clickedNode = previousClickedNode;
15256               that.fx.animate({
15257                 modes:['linear', 'node-property:width:height'],
15258                 duration: 1000,
15259                 onComplete: function() { 
15260                   that.busy = false;
15261                   //TODO(nico) check comment above
15262                   that.clickedNode = clickedNode;
15263                 }
15264               });
15265             }
15266           });
15267         } else {
15268           that.busy = false;
15269           that.clickedNode = n;
15270           that.refresh();
15271         }
15272       }
15273     };
15274     if(config.request) {
15275       this.requestNodes(clickedNode, callback);
15276     } else {
15277       callback.onComplete();
15278     }
15279   },
15280
15281   /* 
15282   Method: out 
15283   
15284   Sets the parent node of the current selected node as root.
15285
15286  */
15287   out: function(){
15288     if(this.busy) return;
15289     this.busy = true;
15290     this.events.hoveredNode = false;
15291     var that = this,
15292         config = this.config,
15293         graph = this.graph,
15294         parents = graph.getNode(this.clickedNode 
15295             && this.clickedNode.id || this.root).getParents(),
15296         parent = parents[0],
15297         clickedNode = parent,
15298         previousClickedNode = this.clickedNode;
15299     
15300     //if no parents return
15301     if(!parent) {
15302       this.busy = false;
15303       return;
15304     }
15305     //final plot callback
15306     callback = {
15307       onComplete: function() {
15308         that.clickedNode = parent;
15309         if(config.request) {
15310           that.requestNodes(parent, {
15311             onComplete: function() {
15312               that.compute();
15313               that.plot();
15314               that.busy = false;
15315             }
15316           });
15317         } else {
15318           that.compute();
15319           that.plot();
15320           that.busy = false;
15321         }
15322       }
15323     };
15324     //prune tree
15325     if (config.levelsToShow > 0)
15326       this.geom.setRightLevelToShow(parent);
15327     //animate node positions
15328     if(config.animate) {
15329       this.clickedNode = clickedNode;
15330       this.compute('end');
15331       //animate the visible subtree only
15332       this.clickedNode = previousClickedNode;
15333       this.fx.animate({
15334         modes:['linear', 'node-property:width:height'],
15335         duration: 1000,
15336         onComplete: function() {
15337           //animate the parent subtree
15338           that.clickedNode = clickedNode;
15339           //change nodes alpha
15340           graph.eachNode(function(n) {
15341             n.setDataset(['current', 'end'], {
15342               'alpha': [0, 1]
15343             });
15344           }, "ignore");
15345           previousClickedNode.eachSubgraph(function(node) {
15346             node.setData('alpha', 1);
15347           }, "ignore");
15348           that.fx.animate({
15349             duration: 500,
15350             modes:['node-property:alpha'],
15351             onComplete: function() {
15352               callback.onComplete();
15353             }
15354           });
15355         }
15356       });
15357     } else {
15358       callback.onComplete();
15359     }
15360   },
15361
15362   requestNodes: function(node, onComplete){
15363     var handler = $.merge(this.controller, onComplete), 
15364         lev = this.config.levelsToShow;
15365     if (handler.request) {
15366       var leaves = [], d = node._depth;
15367       node.eachLevel(0, lev, function(n){
15368         var nodeLevel = lev - (n._depth - d);
15369         if (n.drawn && !n.anySubnode() && nodeLevel > 0) {
15370           leaves.push(n);
15371           n._level = nodeLevel;
15372         }
15373       });
15374       this.group.requestNodes(leaves, handler);
15375     } else {
15376       handler.onComplete();
15377     }
15378   },
15379   
15380   reposition: function() {
15381     this.compute('end');
15382   }
15383 };
15384
15385 /*
15386   Class: TM.Op
15387   
15388   Custom extension of <Graph.Op>.
15389   
15390   Extends:
15391   
15392   All <Graph.Op> methods
15393   
15394   See also:
15395   
15396   <Graph.Op>
15397   
15398   */
15399 TM.Op = new Class({
15400   Implements: Graph.Op,
15401
15402   initialize: function(viz){
15403     this.viz = viz;
15404   }
15405 });
15406
15407 //extend level methods of Graph.Geom
15408 TM.Geom = new Class({
15409   Implements: Graph.Geom,
15410   
15411   getRightLevelToShow: function() {
15412     return this.viz.config.levelsToShow;
15413   },
15414   
15415   setRightLevelToShow: function(node) {
15416     var level = this.getRightLevelToShow(), 
15417         fx = this.viz.labels;
15418     node.eachLevel(0, level+1, function(n) {
15419       var d = n._depth - node._depth;
15420       if(d > level) {
15421         n.drawn = false; 
15422         n.exist = false;
15423         n.ignore = true;
15424         fx.hideLabel(n, false);
15425       } else {
15426         n.drawn = true;
15427         n.exist = true;
15428         delete n.ignore;
15429       }
15430     });
15431     node.drawn = true;
15432     delete node.ignore;
15433   }
15434 });
15435
15436 /*
15437
15438 Performs operations on group of nodes.
15439
15440 */
15441 TM.Group = new Class( {
15442
15443   initialize: function(viz){
15444     this.viz = viz;
15445     this.canvas = viz.canvas;
15446     this.config = viz.config;
15447   },
15448
15449   /*
15450   
15451     Calls the request method on the controller to request a subtree for each node. 
15452   */
15453   requestNodes: function(nodes, controller){
15454     var counter = 0, len = nodes.length, nodeSelected = {};
15455     var complete = function(){
15456       controller.onComplete();
15457     };
15458     var viz = this.viz;
15459     if (len == 0)
15460       complete();
15461     for ( var i = 0; i < len; i++) {
15462       nodeSelected[nodes[i].id] = nodes[i];
15463       controller.request(nodes[i].id, nodes[i]._level, {
15464         onComplete: function(nodeId, data){
15465           if (data && data.children) {
15466             data.id = nodeId;
15467             viz.op.sum(data, {
15468               type: 'nothing'
15469             });
15470           }
15471           if (++counter == len) {
15472             viz.graph.computeLevels(viz.root, 0);
15473             complete();
15474           }
15475         }
15476       });
15477     }
15478   }
15479 });
15480
15481 /*
15482   Class: TM.Plot
15483   
15484   Custom extension of <Graph.Plot>.
15485   
15486   Extends:
15487   
15488   All <Graph.Plot> methods
15489   
15490   See also:
15491   
15492   <Graph.Plot>
15493   
15494   */
15495 TM.Plot = new Class({
15496
15497   Implements: Graph.Plot,
15498
15499   initialize: function(viz){
15500     this.viz = viz;
15501     this.config = viz.config;
15502     this.node = this.config.Node;
15503     this.edge = this.config.Edge;
15504     this.animation = new Animation;
15505     this.nodeTypes = new TM.Plot.NodeTypes;
15506     this.edgeTypes = new TM.Plot.EdgeTypes;
15507     this.labels = viz.labels;
15508   },
15509
15510   plot: function(opt, animating){
15511     var viz = this.viz, 
15512         graph = viz.graph;
15513     viz.canvas.clear();
15514     this.plotTree(graph.getNode(viz.clickedNode && viz.clickedNode.id || viz.root), $.merge(viz.config, opt || {}, {
15515       'withLabels': true,
15516       'hideLabels': false,
15517       'plotSubtree': function(n, ch){
15518         return n.anySubnode("exist");
15519       }
15520     }), animating);
15521   }
15522 });
15523
15524 /*
15525   Class: TM.Label
15526   
15527   Custom extension of <Graph.Label>. 
15528   Contains custom <Graph.Label.SVG>, <Graph.Label.HTML> and <Graph.Label.Native> extensions.
15529
15530   Extends:
15531
15532   All <Graph.Label> methods and subclasses.
15533
15534   See also:
15535
15536   <Graph.Label>, <Graph.Label.Native>, <Graph.Label.HTML>, <Graph.Label.SVG>.
15537   
15538 */
15539 TM.Label = {};
15540
15541 /*
15542  TM.Label.Native
15543
15544  Custom extension of <Graph.Label.Native>.
15545
15546  Extends:
15547
15548  All <Graph.Label.Native> methods
15549
15550  See also:
15551
15552  <Graph.Label.Native>
15553 */
15554 TM.Label.Native = new Class({
15555   Implements: Graph.Label.Native,
15556
15557   initialize: function(viz) {
15558     this.config = viz.config;
15559     this.leaf = viz.leaf;
15560   },
15561   
15562   renderLabel: function(canvas, node, controller){
15563     if(!this.leaf(node) && !this.config.titleHeight) return;
15564     var pos = node.pos.getc(true), 
15565         ctx = canvas.getCtx(),
15566         width = node.getData('width'),
15567         height = node.getData('height'),
15568         x = pos.x + width/2,
15569         y = pos.y;
15570         
15571     ctx.fillText(node.name, x, y, width);
15572   }
15573 });
15574
15575 /*
15576  TM.Label.SVG
15577
15578   Custom extension of <Graph.Label.SVG>.
15579
15580   Extends:
15581
15582   All <Graph.Label.SVG> methods
15583
15584   See also:
15585
15586   <Graph.Label.SVG>
15587 */
15588 TM.Label.SVG = new Class( {
15589   Implements: Graph.Label.SVG,
15590
15591   initialize: function(viz){
15592     this.viz = viz;
15593     this.leaf = viz.leaf;
15594     this.config = viz.config;
15595   },
15596
15597   /* 
15598   placeLabel
15599
15600   Overrides abstract method placeLabel in <Graph.Plot>.
15601
15602   Parameters:
15603
15604   tag - A DOM label element.
15605   node - A <Graph.Node>.
15606   controller - A configuration/controller object passed to the visualization.
15607   
15608   */
15609   placeLabel: function(tag, node, controller){
15610     var pos = node.pos.getc(true), 
15611         canvas = this.viz.canvas,
15612         ox = canvas.translateOffsetX,
15613         oy = canvas.translateOffsetY,
15614         sx = canvas.scaleOffsetX,
15615         sy = canvas.scaleOffsetY,
15616         radius = canvas.getSize();
15617     var labelPos = {
15618       x: Math.round(pos.x * sx + ox + radius.width / 2),
15619       y: Math.round(pos.y * sy + oy + radius.height / 2)
15620     };
15621     tag.setAttribute('x', labelPos.x);
15622     tag.setAttribute('y', labelPos.y);
15623
15624     if(!this.leaf(node) && !this.config.titleHeight) {
15625       tag.style.display = 'none';
15626     }
15627     controller.onPlaceLabel(tag, node);
15628   }
15629 });
15630
15631 /*
15632  TM.Label.HTML
15633
15634  Custom extension of <Graph.Label.HTML>.
15635
15636  Extends:
15637
15638  All <Graph.Label.HTML> methods.
15639
15640  See also:
15641
15642  <Graph.Label.HTML>
15643
15644 */
15645 TM.Label.HTML = new Class( {
15646   Implements: Graph.Label.HTML,
15647
15648   initialize: function(viz){
15649     this.viz = viz;
15650     this.leaf = viz.leaf;
15651     this.config = viz.config;
15652   },
15653
15654   /* 
15655     placeLabel
15656   
15657     Overrides abstract method placeLabel in <Graph.Plot>.
15658   
15659     Parameters:
15660   
15661     tag - A DOM label element.
15662     node - A <Graph.Node>.
15663     controller - A configuration/controller object passed to the visualization.
15664   
15665   */
15666   placeLabel: function(tag, node, controller){
15667     var pos = node.pos.getc(true), 
15668         canvas = this.viz.canvas,
15669         ox = canvas.translateOffsetX,
15670         oy = canvas.translateOffsetY,
15671         sx = canvas.scaleOffsetX,
15672         sy = canvas.scaleOffsetY,
15673         radius = canvas.getSize();
15674     var labelPos = {
15675       x: Math.round(pos.x * sx + ox + radius.width / 2),
15676       y: Math.round(pos.y * sy + oy + radius.height / 2)
15677     };
15678
15679     var style = tag.style;
15680     style.left = labelPos.x + 'px';
15681     style.top = labelPos.y + 'px';
15682     style.width = node.getData('width') * sx + 'px';
15683     style.height = node.getData('height') * sy + 'px';
15684     style.zIndex = node._depth * 100;
15685     style.display = '';
15686
15687     if(!this.leaf(node) && !this.config.titleHeight) {
15688       tag.style.display = 'none';
15689     }
15690     controller.onPlaceLabel(tag, node);
15691   }
15692 });
15693
15694 /*
15695   Class: TM.Plot.NodeTypes
15696
15697   This class contains a list of <Graph.Node> built-in types. 
15698   Node types implemented are 'none', 'rectangle'.
15699
15700   You can add your custom node types, customizing your visualization to the extreme.
15701
15702   Example:
15703
15704   (start code js)
15705     TM.Plot.NodeTypes.implement({
15706       'mySpecialType': {
15707         'render': function(node, canvas) {
15708           //print your custom node to canvas
15709         },
15710         //optional
15711         'contains': function(node, pos) {
15712           //return true if pos is inside the node or false otherwise
15713         }
15714       }
15715     });
15716   (end code)
15717
15718 */
15719 TM.Plot.NodeTypes = new Class( {
15720   'none': {
15721     'render': $.empty
15722   },
15723
15724   'rectangle': {
15725     'render': function(node, canvas, animating){
15726       var leaf = this.viz.leaf(node),
15727           config = this.config,
15728           offst = config.offset,
15729           titleHeight = config.titleHeight,
15730           pos = node.pos.getc(true),
15731           width = node.getData('width'),
15732           height = node.getData('height'),
15733           border = node.getData('border'),
15734           ctx = canvas.getCtx(),
15735           posx = pos.x + offst / 2, 
15736           posy = pos.y + offst / 2;
15737       if(width <= offst || height <= offst) return;
15738       if (leaf) {
15739         if(config.cushion) {
15740           var lg = ctx.createRadialGradient(posx + (width-offst)/2, posy + (height-offst)/2, 1, 
15741               posx + (width-offst)/2, posy + (height-offst)/2, width < height? height : width);
15742           var color = node.getData('color');
15743           var colorGrad = $.rgbToHex($.map($.hexToRgb(color), 
15744               function(r) { return r * 0.2 >> 0; }));
15745           lg.addColorStop(0, color);
15746           lg.addColorStop(1, colorGrad);
15747           ctx.fillStyle = lg;
15748         }
15749         ctx.fillRect(posx, posy, width - offst, height - offst);
15750         if(border) {
15751           ctx.save();
15752           ctx.strokeStyle = border;
15753           ctx.strokeRect(posx, posy, width - offst, height - offst);
15754           ctx.restore();
15755         }
15756       } else if(titleHeight > 0){
15757         ctx.fillRect(pos.x + offst / 2, pos.y + offst / 2, width - offst,
15758             titleHeight - offst);
15759         if(border) {
15760           ctx.save();
15761           ctx.strokeStyle = border;
15762           ctx.strokeRect(pos.x + offst / 2, pos.y + offst / 2, width - offst,
15763               height - offst);
15764           ctx.restore();
15765         }
15766       }
15767     },
15768     'contains': function(node, pos) {
15769       if(this.viz.clickedNode && !node.isDescendantOf(this.viz.clickedNode.id) || node.ignore) return false;
15770       var npos = node.pos.getc(true),
15771           width = node.getData('width'), 
15772           leaf = this.viz.leaf(node),
15773           height = leaf? node.getData('height') : this.config.titleHeight;
15774       return this.nodeHelper.rectangle.contains({x: npos.x + width/2, y: npos.y + height/2}, pos, width, height);
15775     }
15776   }
15777 });
15778
15779 TM.Plot.EdgeTypes = new Class( {
15780   'none': $.empty
15781 });
15782
15783 /*
15784   Class: TM.SliceAndDice
15785   
15786   A slice and dice TreeMap visualization.
15787   
15788   Implements:
15789   
15790   All <TM.Base> methods and properties.
15791 */
15792 TM.SliceAndDice = new Class( {
15793   Implements: [
15794       Loader, Extras, TM.Base, Layouts.TM.SliceAndDice
15795   ]
15796 });
15797
15798 /*
15799   Class: TM.Squarified
15800   
15801   A squarified TreeMap visualization.
15802
15803   Implements:
15804   
15805   All <TM.Base> methods and properties.
15806 */
15807 TM.Squarified = new Class( {
15808   Implements: [
15809       Loader, Extras, TM.Base, Layouts.TM.Squarified
15810   ]
15811 });
15812
15813 /*
15814   Class: TM.Strip
15815   
15816   A strip TreeMap visualization.
15817
15818   Implements:
15819   
15820   All <TM.Base> methods and properties.
15821 */
15822 TM.Strip = new Class( {
15823   Implements: [
15824       Loader, Extras, TM.Base, Layouts.TM.Strip
15825   ]
15826 });
15827
15828
15829 /*
15830  * File: RGraph.js
15831  *
15832  */
15833
15834 /*
15835    Class: RGraph
15836    
15837    A radial graph visualization with advanced animations.
15838    
15839    Inspired by:
15840  
15841    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>
15842    
15843    Note:
15844    
15845    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.
15846    
15847   Implements:
15848   
15849   All <Loader> methods
15850   
15851    Constructor Options:
15852    
15853    Inherits options from
15854    
15855    - <Options.Canvas>
15856    - <Options.Controller>
15857    - <Options.Node>
15858    - <Options.Edge>
15859    - <Options.Label>
15860    - <Options.Events>
15861    - <Options.Tips>
15862    - <Options.NodeStyles>
15863    - <Options.Navigation>
15864    
15865    Additionally, there are other parameters and some default values changed
15866    
15867    interpolation - (string) Default's *linear*. Describes the way nodes are interpolated. Possible values are 'linear' and 'polar'.
15868    levelDistance - (number) Default's *100*. The distance between levels of the tree. 
15869      
15870    Instance Properties:
15871
15872    canvas - Access a <Canvas> instance.
15873    graph - Access a <Graph> instance.
15874    op - Access a <RGraph.Op> instance.
15875    fx - Access a <RGraph.Plot> instance.
15876    labels - Access a <RGraph.Label> interface implementation.   
15877 */
15878
15879 $jit.RGraph = new Class( {
15880
15881   Implements: [
15882       Loader, Extras, Layouts.Radial
15883   ],
15884
15885   initialize: function(controller){
15886     var $RGraph = $jit.RGraph;
15887
15888     var config = {
15889       interpolation: 'linear',
15890       levelDistance: 100
15891     };
15892
15893     this.controller = this.config = $.merge(Options("Canvas", "Node", "Edge",
15894         "Fx", "Controller", "Tips", "NodeStyles", "Events", "Navigation", "Label"), config, controller);
15895
15896     var canvasConfig = this.config;
15897     if(canvasConfig.useCanvas) {
15898       this.canvas = canvasConfig.useCanvas;
15899       this.config.labelContainer = this.canvas.id + '-label';
15900     } else {
15901       if(canvasConfig.background) {
15902         canvasConfig.background = $.merge({
15903           type: 'Circles'
15904         }, canvasConfig.background);
15905       }
15906       this.canvas = new Canvas(this, canvasConfig);
15907       this.config.labelContainer = (typeof canvasConfig.injectInto == 'string'? canvasConfig.injectInto : canvasConfig.injectInto.id) + '-label';
15908     }
15909
15910     this.graphOptions = {
15911       'klass': Polar,
15912       'Node': {
15913         'selected': false,
15914         'exist': true,
15915         'drawn': true
15916       }
15917     };
15918     this.graph = new Graph(this.graphOptions, this.config.Node,
15919         this.config.Edge);
15920     this.labels = new $RGraph.Label[canvasConfig.Label.type](this);
15921     this.fx = new $RGraph.Plot(this, $RGraph);
15922     this.op = new $RGraph.Op(this);
15923     this.json = null;
15924     this.root = null;
15925     this.busy = false;
15926     this.parent = false;
15927     // initialize extras
15928     this.initializeExtras();
15929   },
15930
15931   /* 
15932   
15933     createLevelDistanceFunc 
15934   
15935     Returns the levelDistance function used for calculating a node distance 
15936     to its origin. This function returns a function that is computed 
15937     per level and not per node, such that all nodes with the same depth will have the 
15938     same distance to the origin. The resulting function gets the 
15939     parent node as parameter and returns a float.
15940
15941    */
15942   createLevelDistanceFunc: function(){
15943     var ld = this.config.levelDistance;
15944     return function(elem){
15945       return (elem._depth + 1) * ld;
15946     };
15947   },
15948
15949   /* 
15950      Method: refresh 
15951      
15952      Computes positions and plots the tree.
15953
15954    */
15955   refresh: function(){
15956     this.compute();
15957     this.plot();
15958   },
15959
15960   reposition: function(){
15961     this.compute('end');
15962   },
15963
15964   /*
15965    Method: plot
15966   
15967    Plots the RGraph. This is a shortcut to *fx.plot*.
15968   */
15969   plot: function(){
15970     this.fx.plot();
15971   },
15972   /*
15973    getNodeAndParentAngle
15974   
15975    Returns the _parent_ of the given node, also calculating its angle span.
15976   */
15977   getNodeAndParentAngle: function(id){
15978     var theta = false;
15979     var n = this.graph.getNode(id);
15980     var ps = n.getParents();
15981     var p = (ps.length > 0)? ps[0] : false;
15982     if (p) {
15983       var posParent = p.pos.getc(), posChild = n.pos.getc();
15984       var newPos = posParent.add(posChild.scale(-1));
15985       theta = Math.atan2(newPos.y, newPos.x);
15986       if (theta < 0)
15987         theta += 2 * Math.PI;
15988     }
15989     return {
15990       parent: p,
15991       theta: theta
15992     };
15993   },
15994   /*
15995    tagChildren
15996   
15997    Enumerates the children in order to maintain child ordering (second constraint of the paper).
15998   */
15999   tagChildren: function(par, id){
16000     if (par.angleSpan) {
16001       var adjs = [];
16002       par.eachAdjacency(function(elem){
16003         adjs.push(elem.nodeTo);
16004       }, "ignore");
16005       var len = adjs.length;
16006       for ( var i = 0; i < len && id != adjs[i].id; i++)
16007         ;
16008       for ( var j = (i + 1) % len, k = 0; id != adjs[j].id; j = (j + 1) % len) {
16009         adjs[j].dist = k++;
16010       }
16011     }
16012   },
16013   /* 
16014   Method: onClick 
16015   
16016   Animates the <RGraph> to center the node specified by *id*.
16017
16018    Parameters:
16019
16020    id - A <Graph.Node> id.
16021    opt - (optional|object) An object containing some extra properties described below
16022    hideLabels - (boolean) Default's *true*. Hide labels when performing the animation.
16023
16024    Example:
16025
16026    (start code js)
16027      rgraph.onClick('someid');
16028      //or also...
16029      rgraph.onClick('someid', {
16030       hideLabels: false
16031      });
16032     (end code)
16033     
16034   */
16035   onClick: function(id, opt){
16036     if (this.root != id && !this.busy) {
16037       this.busy = true;
16038       this.root = id;
16039       var that = this;
16040       this.controller.onBeforeCompute(this.graph.getNode(id));
16041       var obj = this.getNodeAndParentAngle(id);
16042
16043       // second constraint
16044       this.tagChildren(obj.parent, id);
16045       this.parent = obj.parent;
16046       this.compute('end');
16047
16048       // first constraint
16049       var thetaDiff = obj.theta - obj.parent.endPos.theta;
16050       this.graph.eachNode(function(elem){
16051         elem.endPos.set(elem.endPos.getp().add($P(thetaDiff, 0)));
16052       });
16053
16054       var mode = this.config.interpolation;
16055       opt = $.merge( {
16056         onComplete: $.empty
16057       }, opt || {});
16058
16059       this.fx.animate($.merge( {
16060         hideLabels: true,
16061         modes: [
16062           mode
16063         ]
16064       }, opt, {
16065         onComplete: function(){
16066           that.busy = false;
16067           opt.onComplete();
16068         }
16069       }));
16070     }
16071   }
16072 });
16073
16074 $jit.RGraph.$extend = true;
16075
16076 (function(RGraph){
16077
16078   /*
16079      Class: RGraph.Op
16080      
16081      Custom extension of <Graph.Op>.
16082
16083      Extends:
16084
16085      All <Graph.Op> methods
16086      
16087      See also:
16088      
16089      <Graph.Op>
16090
16091   */
16092   RGraph.Op = new Class( {
16093
16094     Implements: Graph.Op
16095
16096   });
16097
16098   /*
16099      Class: RGraph.Plot
16100     
16101     Custom extension of <Graph.Plot>.
16102   
16103     Extends:
16104   
16105     All <Graph.Plot> methods
16106     
16107     See also:
16108     
16109     <Graph.Plot>
16110   
16111   */
16112   RGraph.Plot = new Class( {
16113
16114     Implements: Graph.Plot
16115
16116   });
16117
16118   /*
16119     Object: RGraph.Label
16120
16121     Custom extension of <Graph.Label>. 
16122     Contains custom <Graph.Label.SVG>, <Graph.Label.HTML> and <Graph.Label.Native> extensions.
16123   
16124     Extends:
16125   
16126     All <Graph.Label> methods and subclasses.
16127   
16128     See also:
16129   
16130     <Graph.Label>, <Graph.Label.Native>, <Graph.Label.HTML>, <Graph.Label.SVG>.
16131   
16132    */
16133   RGraph.Label = {};
16134
16135   /*
16136      RGraph.Label.Native
16137
16138      Custom extension of <Graph.Label.Native>.
16139
16140      Extends:
16141
16142      All <Graph.Label.Native> methods
16143
16144      See also:
16145
16146      <Graph.Label.Native>
16147
16148   */
16149   RGraph.Label.Native = new Class( {
16150     Implements: Graph.Label.Native
16151   });
16152
16153   /*
16154      RGraph.Label.SVG
16155     
16156     Custom extension of <Graph.Label.SVG>.
16157   
16158     Extends:
16159   
16160     All <Graph.Label.SVG> methods
16161   
16162     See also:
16163   
16164     <Graph.Label.SVG>
16165   
16166   */
16167   RGraph.Label.SVG = new Class( {
16168     Implements: Graph.Label.SVG,
16169
16170     initialize: function(viz){
16171       this.viz = viz;
16172     },
16173
16174     /* 
16175        placeLabel
16176
16177        Overrides abstract method placeLabel in <Graph.Plot>.
16178
16179        Parameters:
16180
16181        tag - A DOM label element.
16182        node - A <Graph.Node>.
16183        controller - A configuration/controller object passed to the visualization.
16184       
16185      */
16186     placeLabel: function(tag, node, controller){
16187       var pos = node.pos.getc(true), 
16188           canvas = this.viz.canvas,
16189           ox = canvas.translateOffsetX,
16190           oy = canvas.translateOffsetY,
16191           sx = canvas.scaleOffsetX,
16192           sy = canvas.scaleOffsetY,
16193           radius = canvas.getSize();
16194       var labelPos = {
16195         x: Math.round(pos.x * sx + ox + radius.width / 2),
16196         y: Math.round(pos.y * sy + oy + radius.height / 2)
16197       };
16198       tag.setAttribute('x', labelPos.x);
16199       tag.setAttribute('y', labelPos.y);
16200
16201       controller.onPlaceLabel(tag, node);
16202     }
16203   });
16204
16205   /*
16206      RGraph.Label.HTML
16207
16208      Custom extension of <Graph.Label.HTML>.
16209
16210      Extends:
16211
16212      All <Graph.Label.HTML> methods.
16213
16214      See also:
16215
16216      <Graph.Label.HTML>
16217
16218   */
16219   RGraph.Label.HTML = new Class( {
16220     Implements: Graph.Label.HTML,
16221
16222     initialize: function(viz){
16223       this.viz = viz;
16224     },
16225     /* 
16226        placeLabel
16227
16228        Overrides abstract method placeLabel in <Graph.Plot>.
16229
16230        Parameters:
16231
16232        tag - A DOM label element.
16233        node - A <Graph.Node>.
16234        controller - A configuration/controller object passed to the visualization.
16235       
16236      */
16237     placeLabel: function(tag, node, controller){
16238       var pos = node.pos.getc(true), 
16239           canvas = this.viz.canvas,
16240           ox = canvas.translateOffsetX,
16241           oy = canvas.translateOffsetY,
16242           sx = canvas.scaleOffsetX,
16243           sy = canvas.scaleOffsetY,
16244           radius = canvas.getSize();
16245       var labelPos = {
16246         x: Math.round(pos.x * sx + ox + radius.width / 2),
16247         y: Math.round(pos.y * sy + oy + radius.height / 2)
16248       };
16249
16250       var style = tag.style;
16251       style.left = labelPos.x + 'px';
16252       style.top = labelPos.y + 'px';
16253       style.display = this.fitsInCanvas(labelPos, canvas)? '' : 'none';
16254
16255       controller.onPlaceLabel(tag, node);
16256     }
16257   });
16258
16259   /*
16260     Class: RGraph.Plot.NodeTypes
16261
16262     This class contains a list of <Graph.Node> built-in types. 
16263     Node types implemented are 'none', 'circle', 'triangle', 'rectangle', 'star', 'ellipse' and 'square'.
16264
16265     You can add your custom node types, customizing your visualization to the extreme.
16266
16267     Example:
16268
16269     (start code js)
16270       RGraph.Plot.NodeTypes.implement({
16271         'mySpecialType': {
16272           'render': function(node, canvas) {
16273             //print your custom node to canvas
16274           },
16275           //optional
16276           'contains': function(node, pos) {
16277             //return true if pos is inside the node or false otherwise
16278           }
16279         }
16280       });
16281     (end code)
16282
16283   */
16284   RGraph.Plot.NodeTypes = new Class({
16285     'none': {
16286       'render': $.empty,
16287       'contains': $.lambda(false)
16288     },
16289     'circle': {
16290       'render': function(node, canvas){
16291         var pos = node.pos.getc(true), 
16292             dim = node.getData('dim');
16293         this.nodeHelper.circle.render('fill', pos, dim, canvas);
16294       },
16295       'contains': function(node, pos){
16296         var npos = node.pos.getc(true), 
16297             dim = node.getData('dim');
16298         return this.nodeHelper.circle.contains(npos, pos, dim);
16299       }
16300     },
16301     'ellipse': {
16302       'render': function(node, canvas){
16303         var pos = node.pos.getc(true), 
16304             width = node.getData('width'), 
16305             height = node.getData('height');
16306         this.nodeHelper.ellipse.render('fill', pos, width, height, canvas);
16307         },
16308       'contains': function(node, pos){
16309         var npos = node.pos.getc(true), 
16310             width = node.getData('width'), 
16311             height = node.getData('height');
16312         return this.nodeHelper.ellipse.contains(npos, pos, width, height);
16313       }
16314     },
16315     'square': {
16316       'render': function(node, canvas){
16317         var pos = node.pos.getc(true), 
16318             dim = node.getData('dim');
16319         this.nodeHelper.square.render('fill', pos, dim, canvas);
16320       },
16321       'contains': function(node, pos){
16322         var npos = node.pos.getc(true), 
16323             dim = node.getData('dim');
16324         return this.nodeHelper.square.contains(npos, pos, dim);
16325       }
16326     },
16327     'rectangle': {
16328       'render': function(node, canvas){
16329         var pos = node.pos.getc(true), 
16330             width = node.getData('width'), 
16331             height = node.getData('height');
16332         this.nodeHelper.rectangle.render('fill', pos, width, height, canvas);
16333       },
16334       'contains': function(node, pos){
16335         var npos = node.pos.getc(true), 
16336             width = node.getData('width'), 
16337             height = node.getData('height');
16338         return this.nodeHelper.rectangle.contains(npos, pos, width, height);
16339       }
16340     },
16341     'triangle': {
16342       'render': function(node, canvas){
16343         var pos = node.pos.getc(true), 
16344             dim = node.getData('dim');
16345         this.nodeHelper.triangle.render('fill', pos, dim, canvas);
16346       },
16347       'contains': function(node, pos) {
16348         var npos = node.pos.getc(true), 
16349             dim = node.getData('dim');
16350         return this.nodeHelper.triangle.contains(npos, pos, dim);
16351       }
16352     },
16353     'star': {
16354       'render': function(node, canvas){
16355         var pos = node.pos.getc(true),
16356             dim = node.getData('dim');
16357         this.nodeHelper.star.render('fill', pos, dim, canvas);
16358       },
16359       'contains': function(node, pos) {
16360         var npos = node.pos.getc(true),
16361             dim = node.getData('dim');
16362         return this.nodeHelper.star.contains(npos, pos, dim);
16363       }
16364     }
16365   });
16366
16367   /*
16368     Class: RGraph.Plot.EdgeTypes
16369
16370     This class contains a list of <Graph.Adjacence> built-in types. 
16371     Edge types implemented are 'none', 'line' and 'arrow'.
16372   
16373     You can add your custom edge types, customizing your visualization to the extreme.
16374   
16375     Example:
16376   
16377     (start code js)
16378       RGraph.Plot.EdgeTypes.implement({
16379         'mySpecialType': {
16380           'render': function(adj, canvas) {
16381             //print your custom edge to canvas
16382           },
16383           //optional
16384           'contains': function(adj, pos) {
16385             //return true if pos is inside the arc or false otherwise
16386           }
16387         }
16388       });
16389     (end code)
16390   
16391   */
16392   RGraph.Plot.EdgeTypes = new Class({
16393     'none': $.empty,
16394     'line': {
16395       'render': function(adj, canvas) {
16396         var from = adj.nodeFrom.pos.getc(true),
16397             to = adj.nodeTo.pos.getc(true);
16398         this.edgeHelper.line.render(from, to, canvas);
16399       },
16400       'contains': function(adj, pos) {
16401         var from = adj.nodeFrom.pos.getc(true),
16402             to = adj.nodeTo.pos.getc(true);
16403         return this.edgeHelper.line.contains(from, to, pos, this.edge.epsilon);
16404       }
16405     },
16406     'arrow': {
16407       'render': function(adj, canvas) {
16408         var from = adj.nodeFrom.pos.getc(true),
16409             to = adj.nodeTo.pos.getc(true),
16410             dim = adj.getData('dim'),
16411             direction = adj.data.$direction,
16412             inv = (direction && direction.length>1 && direction[0] != adj.nodeFrom.id);
16413         this.edgeHelper.arrow.render(from, to, dim, inv, canvas);
16414       },
16415       'contains': function(adj, pos) {
16416         var from = adj.nodeFrom.pos.getc(true),
16417             to = adj.nodeTo.pos.getc(true);
16418         return this.edgeHelper.arrow.contains(from, to, pos, this.edge.epsilon);
16419       }
16420     }
16421   });
16422
16423 })($jit.RGraph);
16424
16425
16426 /*
16427  * File: Hypertree.js
16428  * 
16429 */
16430
16431 /* 
16432      Complex 
16433      
16434      A multi-purpose Complex Class with common methods. Extended for the Hypertree. 
16435  
16436 */
16437 /* 
16438    moebiusTransformation 
16439  
16440    Calculates a moebius transformation for this point / complex. 
16441     For more information go to: 
16442         http://en.wikipedia.org/wiki/Moebius_transformation. 
16443  
16444    Parameters: 
16445  
16446       c - An initialized Complex instance representing a translation Vector. 
16447 */
16448
16449 Complex.prototype.moebiusTransformation = function(c) {
16450   var num = this.add(c);
16451   var den = c.$conjugate().$prod(this);
16452   den.x++;
16453   return num.$div(den);
16454 };
16455
16456 /* 
16457     moebiusTransformation 
16458      
16459     Calculates a moebius transformation for the hyperbolic tree. 
16460      
16461     <http://en.wikipedia.org/wiki/Moebius_transformation> 
16462       
16463      Parameters: 
16464      
16465         graph - A <Graph> instance.
16466         pos - A <Complex>.
16467         prop - A property array.
16468         theta - Rotation angle. 
16469         startPos - _optional_ start position. 
16470 */
16471 Graph.Util.moebiusTransformation = function(graph, pos, prop, startPos, flags) {
16472   this.eachNode(graph, function(elem) {
16473     for ( var i = 0; i < prop.length; i++) {
16474       var p = pos[i].scale(-1), property = startPos ? startPos : prop[i];
16475       elem.getPos(prop[i]).set(elem.getPos(property).getc().moebiusTransformation(p));
16476     }
16477   }, flags);
16478 };
16479
16480 /* 
16481    Class: Hypertree 
16482    
16483    A Hyperbolic Tree/Graph visualization.
16484    
16485    Inspired by:
16486  
16487    A Focus+Context Technique Based on Hyperbolic Geometry for Visualizing Large Hierarchies (John Lamping, Ramana Rao, and Peter Pirolli). 
16488    <http://www.cs.tau.ac.il/~asharf/shrek/Projects/HypBrowser/startree-chi95.pdf>
16489  
16490   Note:
16491  
16492   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.
16493
16494   Implements:
16495   
16496   All <Loader> methods
16497   
16498   Constructor Options:
16499   
16500   Inherits options from
16501   
16502   - <Options.Canvas>
16503   - <Options.Controller>
16504   - <Options.Node>
16505   - <Options.Edge>
16506   - <Options.Label>
16507   - <Options.Events>
16508   - <Options.Tips>
16509   - <Options.NodeStyles>
16510   - <Options.Navigation>
16511   
16512   Additionally, there are other parameters and some default values changed
16513   
16514   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*.
16515   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.
16516   fps - Described in <Options.Fx>. It's default value has been changed to *35*.
16517   duration - Described in <Options.Fx>. It's default value has been changed to *1500*.
16518   Edge.type - Described in <Options.Edge>. It's default value has been changed to *hyperline*. 
16519   
16520   Instance Properties:
16521   
16522   canvas - Access a <Canvas> instance.
16523   graph - Access a <Graph> instance.
16524   op - Access a <Hypertree.Op> instance.
16525   fx - Access a <Hypertree.Plot> instance.
16526   labels - Access a <Hypertree.Label> interface implementation.
16527
16528 */
16529
16530 $jit.Hypertree = new Class( {
16531
16532   Implements: [ Loader, Extras, Layouts.Radial ],
16533
16534   initialize: function(controller) {
16535     var $Hypertree = $jit.Hypertree;
16536
16537     var config = {
16538       radius: "auto",
16539       offset: 0,
16540       Edge: {
16541         type: 'hyperline'
16542       },
16543       duration: 1500,
16544       fps: 35
16545     };
16546     this.controller = this.config = $.merge(Options("Canvas", "Node", "Edge",
16547         "Fx", "Tips", "NodeStyles", "Events", "Navigation", "Controller", "Label"), config, controller);
16548
16549     var canvasConfig = this.config;
16550     if(canvasConfig.useCanvas) {
16551       this.canvas = canvasConfig.useCanvas;
16552       this.config.labelContainer = this.canvas.id + '-label';
16553     } else {
16554       if(canvasConfig.background) {
16555         canvasConfig.background = $.merge({
16556           type: 'Circles'
16557         }, canvasConfig.background);
16558       }
16559       this.canvas = new Canvas(this, canvasConfig);
16560       this.config.labelContainer = (typeof canvasConfig.injectInto == 'string'? canvasConfig.injectInto : canvasConfig.injectInto.id) + '-label';
16561     }
16562
16563     this.graphOptions = {
16564       'klass': Polar,
16565       'Node': {
16566         'selected': false,
16567         'exist': true,
16568         'drawn': true
16569       }
16570     };
16571     this.graph = new Graph(this.graphOptions, this.config.Node,
16572         this.config.Edge);
16573     this.labels = new $Hypertree.Label[canvasConfig.Label.type](this);
16574     this.fx = new $Hypertree.Plot(this, $Hypertree);
16575     this.op = new $Hypertree.Op(this);
16576     this.json = null;
16577     this.root = null;
16578     this.busy = false;
16579     // initialize extras
16580     this.initializeExtras();
16581   },
16582
16583   /* 
16584   
16585   createLevelDistanceFunc 
16586
16587   Returns the levelDistance function used for calculating a node distance 
16588   to its origin. This function returns a function that is computed 
16589   per level and not per node, such that all nodes with the same depth will have the 
16590   same distance to the origin. The resulting function gets the 
16591   parent node as parameter and returns a float.
16592
16593   */
16594   createLevelDistanceFunc: function() {
16595     // get max viz. length.
16596     var r = this.getRadius();
16597     // get max depth.
16598     var depth = 0, max = Math.max, config = this.config;
16599     this.graph.eachNode(function(node) {
16600       depth = max(node._depth, depth);
16601     }, "ignore");
16602     depth++;
16603     // node distance generator
16604     var genDistFunc = function(a) {
16605       return function(node) {
16606         node.scale = r;
16607         var d = node._depth + 1;
16608         var acum = 0, pow = Math.pow;
16609         while (d) {
16610           acum += pow(a, d--);
16611         }
16612         return acum - config.offset;
16613       };
16614     };
16615     // estimate better edge length.
16616     for ( var i = 0.51; i <= 1; i += 0.01) {
16617       var valSeries = (1 - Math.pow(i, depth)) / (1 - i);
16618       if (valSeries >= 2) { return genDistFunc(i - 0.01); }
16619     }
16620     return genDistFunc(0.75);
16621   },
16622
16623   /* 
16624     Method: getRadius 
16625     
16626     Returns the current radius of the visualization. If *config.radius* is *auto* then it 
16627     calculates the radius by taking the smaller size of the <Canvas> widget.
16628     
16629     See also:
16630     
16631     <Canvas.getSize>
16632    
16633   */
16634   getRadius: function() {
16635     var rad = this.config.radius;
16636     if (rad !== "auto") { return rad; }
16637     var s = this.canvas.getSize();
16638     return Math.min(s.width, s.height) / 2;
16639   },
16640
16641   /* 
16642     Method: refresh 
16643     
16644     Computes positions and plots the tree.
16645
16646     Parameters:
16647
16648     reposition - (optional|boolean) Set this to *true* to force all positions (current, start, end) to match.
16649
16650    */
16651   refresh: function(reposition) {
16652     if (reposition) {
16653       this.reposition();
16654       this.graph.eachNode(function(node) {
16655         node.startPos.rho = node.pos.rho = node.endPos.rho;
16656         node.startPos.theta = node.pos.theta = node.endPos.theta;
16657       });
16658     } else {
16659       this.compute();
16660     }
16661     this.plot();
16662   },
16663
16664   /* 
16665    reposition 
16666    
16667    Computes nodes' positions and restores the tree to its previous position.
16668
16669    For calculating nodes' positions the root must be placed on its origin. This method does this 
16670      and then attemps to restore the hypertree to its previous position.
16671     
16672   */
16673   reposition: function() {
16674     this.compute('end');
16675     var vector = this.graph.getNode(this.root).pos.getc().scale(-1);
16676     Graph.Util.moebiusTransformation(this.graph, [ vector ], [ 'end' ],
16677         'end', "ignore");
16678     this.graph.eachNode(function(node) {
16679       if (node.ignore) {
16680         node.endPos.rho = node.pos.rho;
16681         node.endPos.theta = node.pos.theta;
16682       }
16683     });
16684   },
16685
16686   /* 
16687    Method: plot 
16688    
16689    Plots the <Hypertree>. This is a shortcut to *fx.plot*. 
16690
16691   */
16692   plot: function() {
16693     this.fx.plot();
16694   },
16695
16696   /* 
16697    Method: onClick 
16698    
16699    Animates the <Hypertree> to center the node specified by *id*.
16700
16701    Parameters:
16702
16703    id - A <Graph.Node> id.
16704    opt - (optional|object) An object containing some extra properties described below
16705    hideLabels - (boolean) Default's *true*. Hide labels when performing the animation.
16706
16707    Example:
16708
16709    (start code js)
16710      ht.onClick('someid');
16711      //or also...
16712      ht.onClick('someid', {
16713       hideLabels: false
16714      });
16715     (end code)
16716     
16717   */
16718   onClick: function(id, opt) {
16719     var pos = this.graph.getNode(id).pos.getc(true);
16720     this.move(pos, opt);
16721   },
16722
16723   /* 
16724    Method: move 
16725
16726    Translates the tree to the given position. 
16727
16728    Parameters:
16729
16730    pos - (object) A *x, y* coordinate object where x, y in [0, 1), to move the tree to.
16731    opt - This object has been defined in <Hypertree.onClick>
16732    
16733    Example:
16734    
16735    (start code js)
16736      ht.move({ x: 0, y: 0.7 }, {
16737        hideLabels: false
16738      });
16739    (end code)
16740
16741   */
16742   move: function(pos, opt) {
16743     var versor = $C(pos.x, pos.y);
16744     if (this.busy === false && versor.norm() < 1) {
16745       this.busy = true;
16746       var root = this.graph.getClosestNodeToPos(versor), that = this;
16747       this.graph.computeLevels(root.id, 0);
16748       this.controller.onBeforeCompute(root);
16749       opt = $.merge( {
16750         onComplete: $.empty
16751       }, opt || {});
16752       this.fx.animate($.merge( {
16753         modes: [ 'moebius' ],
16754         hideLabels: true
16755       }, opt, {
16756         onComplete: function() {
16757           that.busy = false;
16758           opt.onComplete();
16759         }
16760       }), versor);
16761     }
16762   }
16763 });
16764
16765 $jit.Hypertree.$extend = true;
16766
16767 (function(Hypertree) {
16768
16769   /* 
16770      Class: Hypertree.Op 
16771    
16772      Custom extension of <Graph.Op>.
16773
16774      Extends:
16775
16776      All <Graph.Op> methods
16777      
16778      See also:
16779      
16780      <Graph.Op>
16781
16782   */
16783   Hypertree.Op = new Class( {
16784
16785     Implements: Graph.Op
16786
16787   });
16788
16789   /* 
16790      Class: Hypertree.Plot 
16791    
16792     Custom extension of <Graph.Plot>.
16793   
16794     Extends:
16795   
16796     All <Graph.Plot> methods
16797     
16798     See also:
16799     
16800     <Graph.Plot>
16801   
16802   */
16803   Hypertree.Plot = new Class( {
16804
16805     Implements: Graph.Plot
16806
16807   });
16808
16809   /*
16810     Object: Hypertree.Label
16811
16812     Custom extension of <Graph.Label>. 
16813     Contains custom <Graph.Label.SVG>, <Graph.Label.HTML> and <Graph.Label.Native> extensions.
16814   
16815     Extends:
16816   
16817     All <Graph.Label> methods and subclasses.
16818   
16819     See also:
16820   
16821     <Graph.Label>, <Graph.Label.Native>, <Graph.Label.HTML>, <Graph.Label.SVG>.
16822
16823    */
16824   Hypertree.Label = {};
16825
16826   /*
16827      Hypertree.Label.Native
16828
16829      Custom extension of <Graph.Label.Native>.
16830
16831      Extends:
16832
16833      All <Graph.Label.Native> methods
16834
16835      See also:
16836
16837      <Graph.Label.Native>
16838
16839   */
16840   Hypertree.Label.Native = new Class( {
16841     Implements: Graph.Label.Native,
16842
16843     initialize: function(viz) {
16844       this.viz = viz;
16845     },
16846
16847     renderLabel: function(canvas, node, controller) {
16848       var ctx = canvas.getCtx();
16849       var coord = node.pos.getc(true);
16850       var s = this.viz.getRadius();
16851       ctx.fillText(node.name, coord.x * s, coord.y * s);
16852     }
16853   });
16854
16855   /*
16856      Hypertree.Label.SVG
16857
16858     Custom extension of <Graph.Label.SVG>.
16859   
16860     Extends:
16861   
16862     All <Graph.Label.SVG> methods
16863   
16864     See also:
16865   
16866     <Graph.Label.SVG>
16867   
16868   */
16869   Hypertree.Label.SVG = new Class( {
16870     Implements: Graph.Label.SVG,
16871
16872     initialize: function(viz) {
16873       this.viz = viz;
16874     },
16875
16876     /* 
16877        placeLabel
16878
16879        Overrides abstract method placeLabel in <Graph.Plot>.
16880
16881        Parameters:
16882
16883        tag - A DOM label element.
16884        node - A <Graph.Node>.
16885        controller - A configuration/controller object passed to the visualization.
16886       
16887      */
16888     placeLabel: function(tag, node, controller) {
16889       var pos = node.pos.getc(true), 
16890           canvas = this.viz.canvas,
16891           ox = canvas.translateOffsetX,
16892           oy = canvas.translateOffsetY,
16893           sx = canvas.scaleOffsetX,
16894           sy = canvas.scaleOffsetY,
16895           radius = canvas.getSize(),
16896           r = this.viz.getRadius();
16897       var labelPos = {
16898         x: Math.round((pos.x * sx) * r + ox + radius.width / 2),
16899         y: Math.round((pos.y * sy) * r + oy + radius.height / 2)
16900       };
16901       tag.setAttribute('x', labelPos.x);
16902       tag.setAttribute('y', labelPos.y);
16903       controller.onPlaceLabel(tag, node);
16904     }
16905   });
16906
16907   /*
16908      Hypertree.Label.HTML
16909
16910      Custom extension of <Graph.Label.HTML>.
16911
16912      Extends:
16913
16914      All <Graph.Label.HTML> methods.
16915
16916      See also:
16917
16918      <Graph.Label.HTML>
16919
16920   */
16921   Hypertree.Label.HTML = new Class( {
16922     Implements: Graph.Label.HTML,
16923
16924     initialize: function(viz) {
16925       this.viz = viz;
16926     },
16927     /* 
16928        placeLabel
16929
16930        Overrides abstract method placeLabel in <Graph.Plot>.
16931
16932        Parameters:
16933
16934        tag - A DOM label element.
16935        node - A <Graph.Node>.
16936        controller - A configuration/controller object passed to the visualization.
16937       
16938      */
16939     placeLabel: function(tag, node, controller) {
16940       var pos = node.pos.getc(true), 
16941           canvas = this.viz.canvas,
16942           ox = canvas.translateOffsetX,
16943           oy = canvas.translateOffsetY,
16944           sx = canvas.scaleOffsetX,
16945           sy = canvas.scaleOffsetY,
16946           radius = canvas.getSize(),
16947           r = this.viz.getRadius();
16948       var labelPos = {
16949         x: Math.round((pos.x * sx) * r + ox + radius.width / 2),
16950         y: Math.round((pos.y * sy) * r + oy + radius.height / 2)
16951       };
16952       var style = tag.style;
16953       style.left = labelPos.x + 'px';
16954       style.top = labelPos.y + 'px';
16955       style.display = this.fitsInCanvas(labelPos, canvas) ? '' : 'none';
16956
16957       controller.onPlaceLabel(tag, node);
16958     }
16959   });
16960
16961   /*
16962     Class: Hypertree.Plot.NodeTypes
16963
16964     This class contains a list of <Graph.Node> built-in types. 
16965     Node types implemented are 'none', 'circle', 'triangle', 'rectangle', 'star', 'ellipse' and 'square'.
16966
16967     You can add your custom node types, customizing your visualization to the extreme.
16968
16969     Example:
16970
16971     (start code js)
16972       Hypertree.Plot.NodeTypes.implement({
16973         'mySpecialType': {
16974           'render': function(node, canvas) {
16975             //print your custom node to canvas
16976           },
16977           //optional
16978           'contains': function(node, pos) {
16979             //return true if pos is inside the node or false otherwise
16980           }
16981         }
16982       });
16983     (end code)
16984
16985   */
16986   Hypertree.Plot.NodeTypes = new Class({
16987     'none': {
16988       'render': $.empty,
16989       'contains': $.lambda(false)
16990     },
16991     'circle': {
16992       'render': function(node, canvas) {
16993         var nconfig = this.node,
16994             dim = node.getData('dim'),
16995             p = node.pos.getc();
16996         dim = nconfig.transform? dim * (1 - p.squaredNorm()) : dim;
16997         p.$scale(node.scale);
16998         if (dim > 0.2) {
16999           this.nodeHelper.circle.render('fill', p, dim, canvas);
17000         }
17001       },
17002       'contains': function(node, pos) {
17003         var dim = node.getData('dim'),
17004             npos = node.pos.getc().$scale(node.scale);
17005         return this.nodeHelper.circle.contains(npos, pos, dim);
17006       }
17007     },
17008     'ellipse': {
17009       'render': function(node, canvas) {
17010         var pos = node.pos.getc().$scale(node.scale),
17011             width = node.getData('width'),
17012             height = node.getData('height');
17013         this.nodeHelper.ellipse.render('fill', pos, width, height, canvas);
17014       },
17015       'contains': function(node, pos) {
17016         var width = node.getData('width'),
17017             height = node.getData('height'),
17018             npos = node.pos.getc().$scale(node.scale);
17019         return this.nodeHelper.circle.contains(npos, pos, width, height);
17020       }
17021     },
17022     'square': {
17023       'render': function(node, canvas) {
17024         var nconfig = this.node,
17025             dim = node.getData('dim'),
17026             p = node.pos.getc();
17027         dim = nconfig.transform? dim * (1 - p.squaredNorm()) : dim;
17028         p.$scale(node.scale);
17029         if (dim > 0.2) {
17030           this.nodeHelper.square.render('fill', p, dim, canvas);
17031         }
17032       },
17033       'contains': function(node, pos) {
17034         var dim = node.getData('dim'),
17035             npos = node.pos.getc().$scale(node.scale);
17036         return this.nodeHelper.square.contains(npos, pos, dim);
17037       }
17038     },
17039     'rectangle': {
17040       'render': function(node, canvas) {
17041         var nconfig = this.node,
17042             width = node.getData('width'),
17043             height = node.getData('height'),
17044             pos = node.pos.getc();
17045         width = nconfig.transform? width * (1 - pos.squaredNorm()) : width;
17046         height = nconfig.transform? height * (1 - pos.squaredNorm()) : height;
17047         pos.$scale(node.scale);
17048         if (width > 0.2 && height > 0.2) {
17049           this.nodeHelper.rectangle.render('fill', pos, width, height, canvas);
17050         }
17051       },
17052       'contains': function(node, pos) {
17053         var width = node.getData('width'),
17054             height = node.getData('height'),
17055             npos = node.pos.getc().$scale(node.scale);
17056         return this.nodeHelper.rectangle.contains(npos, pos, width, height);
17057       }
17058     },
17059     'triangle': {
17060       'render': function(node, canvas) {
17061         var nconfig = this.node,
17062             dim = node.getData('dim'),
17063             p = node.pos.getc();
17064         dim = nconfig.transform? dim * (1 - p.squaredNorm()) : dim;
17065         p.$scale(node.scale);
17066         if (dim > 0.2) {
17067           this.nodeHelper.triangle.render('fill', p, dim, canvas);
17068         }
17069       },
17070       'contains': function(node, pos) {
17071         var dim = node.getData('dim'),
17072             npos = node.pos.getc().$scale(node.scale);
17073         return this.nodeHelper.triangle.contains(npos, pos, dim);
17074       }
17075     },
17076     'star': {
17077       'render': function(node, canvas) {
17078         var nconfig = this.node,
17079             dim = node.getData('dim'),
17080             p = node.pos.getc();
17081         dim = nconfig.transform? dim * (1 - p.squaredNorm()) : dim;
17082         p.$scale(node.scale);
17083         if (dim > 0.2) {
17084           this.nodeHelper.star.render('fill', p, dim, canvas);
17085         }
17086       },
17087       'contains': function(node, pos) {
17088         var dim = node.getData('dim'),
17089             npos = node.pos.getc().$scale(node.scale);
17090         return this.nodeHelper.star.contains(npos, pos, dim);
17091       }
17092     }
17093   });
17094
17095   /*
17096    Class: Hypertree.Plot.EdgeTypes
17097
17098     This class contains a list of <Graph.Adjacence> built-in types. 
17099     Edge types implemented are 'none', 'line', 'arrow' and 'hyperline'.
17100   
17101     You can add your custom edge types, customizing your visualization to the extreme.
17102   
17103     Example:
17104   
17105     (start code js)
17106       Hypertree.Plot.EdgeTypes.implement({
17107         'mySpecialType': {
17108           'render': function(adj, canvas) {
17109             //print your custom edge to canvas
17110           },
17111           //optional
17112           'contains': function(adj, pos) {
17113             //return true if pos is inside the arc or false otherwise
17114           }
17115         }
17116       });
17117     (end code)
17118   
17119   */
17120   Hypertree.Plot.EdgeTypes = new Class({
17121     'none': $.empty,
17122     'line': {
17123       'render': function(adj, canvas) {
17124         var from = adj.nodeFrom.pos.getc(true),
17125           to = adj.nodeTo.pos.getc(true),
17126           r = adj.nodeFrom.scale;
17127           this.edgeHelper.line.render({x:from.x*r, y:from.y*r}, {x:to.x*r, y:to.y*r}, canvas);
17128       },
17129       'contains': function(adj, pos) {
17130         var from = adj.nodeFrom.pos.getc(true),
17131             to = adj.nodeTo.pos.getc(true),
17132             r = adj.nodeFrom.scale;
17133             this.edgeHelper.line.contains({x:from.x*r, y:from.y*r}, {x:to.x*r, y:to.y*r}, pos, this.edge.epsilon);
17134       }
17135     },
17136     'arrow': {
17137       'render': function(adj, canvas) {
17138         var from = adj.nodeFrom.pos.getc(true),
17139             to = adj.nodeTo.pos.getc(true),
17140             r = adj.nodeFrom.scale,
17141             dim = adj.getData('dim'),
17142             direction = adj.data.$direction,
17143             inv = (direction && direction.length>1 && direction[0] != adj.nodeFrom.id);
17144         this.edgeHelper.arrow.render({x:from.x*r, y:from.y*r}, {x:to.x*r, y:to.y*r}, dim, inv, canvas);
17145       },
17146       'contains': function(adj, pos) {
17147         var from = adj.nodeFrom.pos.getc(true),
17148             to = adj.nodeTo.pos.getc(true),
17149             r = adj.nodeFrom.scale;
17150         this.edgeHelper.arrow.contains({x:from.x*r, y:from.y*r}, {x:to.x*r, y:to.y*r}, pos, this.edge.epsilon);
17151       }
17152     },
17153     'hyperline': {
17154       'render': function(adj, canvas) {
17155         var from = adj.nodeFrom.pos.getc(),
17156             to = adj.nodeTo.pos.getc(),
17157             dim = this.viz.getRadius();
17158         this.edgeHelper.hyperline.render(from, to, dim, canvas);
17159       },
17160       'contains': $.lambda(false)
17161     }
17162   });
17163
17164 })($jit.Hypertree);
17165
17166
17167
17168
17169  })();