text: #!/usr/bin/env python
  # -*- coding: utf-8 -*-
  import string
  import random
  import json
  from core.alert import messages
  from core.compatible import version
  
  
  def start(graph_flag, language, data, _HOST, _USERNAME, _PASSWORD, _PORT, _TYPE, _DESCRIPTION):
      # define  a normalised_json
      normalisedjson = {
          "name": "Started attack",
          "children": {}
      }
      # get data for normalised_json
      for each_scan in data:
  
          if each_scan['HOST'] not in normalisedjson['children']:
              normalisedjson['children'].update({each_scan['HOST']: {}})
              normalisedjson['children'][each_scan['HOST']].update({each_scan['TYPE']: []})
  
          if each_scan['TYPE'] not in normalisedjson['children'][each_scan['HOST']]:
              normalisedjson['children'][each_scan['HOST']].update({each_scan['TYPE']: []})
  
          normalisedjson['children'][each_scan['HOST']][each_scan['TYPE']].append("HOST: \"%s\", PORT:\"%s\", DESCRIPTION:\"%s\", USERNAME:\"%s\", PASSWORD:\"%s\"" % (
              each_scan['HOST'], each_scan['PORT'], each_scan['DESCRIPTION'], each_scan['USERNAME'], each_scan['PASSWORD']))
  
      # define a dgraph_json
      dgraph = {
          "id": "0",
          "data": [],
          "relation": "",
          "name": "Start Attacking",
          "children": []
      }
  
      # get data for dgraph_json
      n = 1
      for host in normalisedjson['children']:
  
          dgraph['children'].append({"id": str(n), "name": host, "data": {"relation": "Start Attacking"}, "children": [{"id": ''.join(random.choice(
              string.ascii_letters + string.digits) for _ in range(20)), "name": otype, "data": {"band": [description.split(', ')[2].lstrip("DESCRIPTION: ").strip("\"") for description in normalisedjson['children'][host][otype]][0], "relation": [description.split(', ')[1:] for description in normalisedjson['children'][host][otype]]}, "children": [{"children": [], "data":{"band": description.split(', ')[2], "relation": description.split(', ')[1:]}, "id": ''.join(random.choice(
                  string.ascii_letters + string.digits) for _ in range(20)), "name": description.split(', ')[1].lstrip("PORT: ").strip("\"")} for description in normalisedjson['children'][host][otype]]} for otype in normalisedjson['children'][host]]})
          n += 1
  
      data = '''<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  <!-- THIS SAMPLE COPIED AND MODIFIED FROM http://philogb.github.io/jit/static/v20/Jit/Examples/Hypertree/example1.html -->
  <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <title>__html_title_to_replace__</title>
  
  <!-- CSS Files -->
  <!-- MODIFIED FROM http://philogb.github.io/jit/static/v20/Jit/Examples/css/base.css
  \t\t\t\t   http://philogb.github.io/jit/static/v20/Jit/Examples/css/Hypertree.css -->
  <style>
  html, body {
      margina:0;
      padding:0;
      font-family: "Lucida Grande", Verdana;
      font-size: 0.9em;
      text-align: center;
      background-color:#F2F2F2;
  }
  
  input, select {
      font-size:0.9em;
  }
  
  table {
      margin-top:-10px;
      margin-left:7px;
  }
  
  h4 {
      font-size:1.1em;
      text-decoration:none;
      font-weight:normal;
      color:#23A4FF;
  }
  
  a {
      color:#23A4FF;
  }
  
  #container {
      width: 1000px;
      height: 600px;
      margin:0 auto;
      position:relative;
  }
  
  #left-container,
  #right-container,
  #center-container {
      height:600px;
      position:absolute;
      top:0;
  }
  
  #left-container, #right-container {
      width:200px;
      color:#686c70;
      text-align: left;
      overflow: auto;
      background-color:#fff;
      background-repeat:no-repeat;
      border-bottom:1px solid #ddd;
  }
  
  #left-container {
      left:0;
      background-image:url(\'col2.png\');
      background-position:center right;
      border-left:1px solid #ddd;
  
  }
  
  #right-container {
      right:0;
      background-image:url(\'col1.png\');
      background-position:center left;
      border-right:1px solid #ddd;
  }
  
  #right-container h4{
      text-indent:8px;
  }
  
  #center-container {
      width:600px;
      left:200px;
      background-color:#1a1a1a;
      color:#ccc;
  }
  
  .text {
      margin: 7px;
  }
  
  #inner-details {
      font-size:0.8em;
      list-style:none;
      margin:7px;
  }
  
  #log {
      position:absolute;
      top:10px;
      font-size:1.0em;
      font-weight:bold;
      color:#23A4FF;
  }
  
  
  #infovis {
      position:relative;
      width:600px;
      height:600px;
      margin:auto;
      overflow:hidden;
  }
  
  /*TOOLTIPS*/
  .tip {
      color: #111;
      width: 139px;
      background-color: white;
      border:1px solid #ccc;
      -moz-box-shadow:#555 2px 2px 8px;
      -webkit-box-shadow:#555 2px 2px 8px;
      -o-box-shadow:#555 2px 2px 8px;
      box-shadow:#555 2px 2px 8px;
      opacity:0.9;
      filter:alpha(opacity=90);
      font-size:10px;
      font-family:Verdana, Geneva, Arial, Helvetica, sans-serif;
      padding:7px;
  }
  
  #infovis-canvaswidget {
    margin:25px 0 0 25px;
  }
  
  </style>
  <a target="_blank" href="https://github.com/viraintel/OWASP-Nettacker"><h2>OWASP Nettacker</h2></a>
  <!--[if IE]><script language="javascript" type="text/javascript" src="../../Extras/excanvas.js"></script><![endif]-->
  
  <!-- JIT Library File -->
  <script>
  __js_jit_lib_will_locate_here__
  </script>
  <!-- jit library -->
  <script>
  /*
  Copyright (c) 2011 Sencha Inc. - Author: Nicolas Garcia Belmonte (http://philogb.github.com/)
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:
  
  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.
  
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE.
  
   */
  
  (function() {
    /**
     * @param {Object} w
     * @return {undefined}
     */
    window.$jit = function(w) {
      w = w || window;
      var k;
      for (k in $jit) {
        if ($jit[k].$extend) {
          w[k] = $jit[k];
        }
      }
    };
    /** @type {string} */
    $jit.version = "2.0.1";
    /**
     * @param {?} obj
     * @return {?}
     */
    var $ = function(obj) {
      return document.getElementById(obj);
    };
    /**
     * @return {undefined}
     */
    $.empty = function() {
    };
    /**
     * @param {?} obj
     * @param {?} opt_attributes
     * @return {?}
     */
    $.extend = function(obj, opt_attributes) {
      var val;
      for (val in opt_attributes || {}) {
        obj[val] = opt_attributes[val];
      }
      return obj;
    };
    /**
     * @param {boolean} recurring
     * @return {?}
     */
    $.lambda = function(recurring) {
      return typeof recurring == "function" ? recurring : function() {
        return recurring;
      };
    };
    /** @type {function (): number} */
    $.time = Date.now || function() {
      return+new Date;
    };
    /**
     * @param {string} obj
     * @return {?}
     */
    $.splat = function(obj) {
      var type = $.type(obj);
      return type ? type != "array" ? [obj] : obj : [];
    };
    /**
     * @param {Object} obj
     * @return {?}
     */
    $.type = function(obj) {
      /** @type {string} */
      var t = $.type.s.call(obj).match(/^\\[object\\s(.*)\\]$/)[1].toLowerCase();
      if (t != "object") {
        return t;
      }
      if (obj && obj.$$family) {
        return obj.$$family;
      }
      return obj && (obj.nodeName && obj.nodeType == 1) ? "element" : t;
    };
    /** @type {function (this:*): string} */
    $.type.s = Object.prototype.toString;
    /**
     * @param {?} opt_attributes
     * @param {Function} f
     * @return {undefined}
     */
    $.each = function(opt_attributes, f) {
      var value = $.type(opt_attributes);
      if (value == "object") {
        var i;
        for (i in opt_attributes) {
          f(opt_attributes[i], i);
        }
      } else {
        /** @type {number} */
        var m = 0;
        var n = opt_attributes.length;
        for (;m < n;m++) {
          f(opt_attributes[m], m);
        }
      }
    };
    /**
     * @param {Array} arr
     * @param {?} obj
     * @return {?}
     */
    $.indexOf = function(arr, obj) {
      if (Array.indexOf) {
        return arr.indexOf(obj);
      }
      /** @type {number} */
      var i = 0;
      var e = arr.length;
      for (;i < e;i++) {
        if (arr[i] === obj) {
          return i;
        }
      }
      return-1;
    };
    /**
     * @param {?} attributes
     * @param {Function} fun
     * @return {?}
     */
    $.map = function(attributes, fun) {
      /** @type {Array} */
      var fin = [];
      $.each(attributes, function(k, v) {
        fin.push(fun(k, v));
      });
      return fin;
    };
    /**
     * @param {?} arr
     * @param {Function} callback
     * @param {number} mayParseLabeledStatementInstead
     * @return {?}
     */
    $.reduce = function(arr, callback, mayParseLabeledStatementInstead) {
      var j = arr.length;
      if (j == 0) {
        return mayParseLabeledStatementInstead;
      }
      var basis = arguments.length == 3 ? mayParseLabeledStatementInstead : arr[--j];
      for (;j--;) {
        basis = callback(basis, arr[j]);
      }
      return basis;
    };
    /**
     * @return {?}
     */
    $.merge = function() {
      var mix = {};
      /** @type {number} */
      var argsIndex = 0;
      /** @type {number} */
      var argsLength = arguments.length;
      for (;argsIndex < argsLength;argsIndex++) {
        var iterable = arguments[argsIndex];
        if ($.type(iterable) != "object") {
          continue;
        }
        var key;
        for (key in iterable) {
          var op = iterable[key];
          var mp = mix[key];
          mix[key] = mp && ($.type(op) == "object" && $.type(mp) == "object") ? $.merge(mp, op) : $.unlink(op);
        }
      }
      return mix;
    };
    /**
     * @param {Object} object
     * @return {?}
     */
    $.unlink = function(object) {
      var safe;
      switch($.type(object)) {
        case "object":
          safe = {};
          var key;
          for (key in object) {
            safe[key] = $.unlink(object[key]);
          }
          break;
        case "array":
          /** @type {Array} */
          safe = [];
          /** @type {number} */
          var i = 0;
          var length = object.length;
          for (;i < length;i++) {
            safe[i] = $.unlink(object[i]);
          }
          break;
        default:
          return object;
      }
      return safe;
    };
    /**
     * @return {?}
     */
    $.zip = function() {
      if (arguments.length === 0) {
        return[];
      }
      /** @type {number} */
      var j = 0;
      /** @type {Array} */
      var out = [];
      /** @type {number} */
      var argLength = arguments.length;
      var jl = arguments[0].length;
      for (;j < jl;j++) {
        /** @type {number} */
        var i = 0;
        /** @type {Array} */
        var copies = [];
        for (;i < argLength;i++) {
          copies.push(arguments[i][j]);
        }
        out.push(copies);
      }
      return out;
    };
    /**
     * @param {Array} ans
     * @param {boolean} array
     * @return {?}
     */
    $.rgbToHex = function(ans, array) {
      if (ans.length < 3) {
        return null;
      }
      if (ans.length == 4 && (ans[3] == 0 && !array)) {
        return "transparent";
      }
      /** @type {Array} */
      var hex = [];
      /** @type {number} */
      var i = 0;
      for (;i < 3;i++) {
        /** @type {string} */
        var bit = (ans[i] - 0).toString(16);
        hex.push(bit.length == 1 ? "0" + bit : bit);
      }
      return array ? hex : "#" + hex.join("");
    };
    /**
     * @param {string} hex
     * @return {?}
     */
    $.hexToRgb = function(hex) {
      if (hex.length != 7) {
        hex = hex.match(/^#?(\\w{1,2})(\\w{1,2})(\\w{1,2})$/);
        hex.shift();
        if (hex.length != 3) {
          return null;
        }
        /** @type {Array} */
        var rgb = [];
        /** @type {number} */
        var index = 0;
        for (;index < 3;index++) {
          var value = hex[index];
          if (value.length == 1) {
            value += value;
          }
          rgb.push(parseInt(value, 16));
        }
        return rgb;
      } else {
        /** @type {number} */
        hex = parseInt(hex.slice(1), 16);
        return[hex >> 16, hex >> 8 & 255, hex & 255];
      }
    };
    /**
     * @param {Element} elem
     * @return {undefined}
     */
    $.destroy = function(elem) {
      $.clean(elem);
      if (elem.parentNode) {
        elem.parentNode.removeChild(elem);
      }
      if (elem.clearAttributes) {
        elem.clearAttributes();
      }
    };
    /**
     * @param {Element} el
     * @return {undefined}
     */
    $.clean = function(el) {
      var nodes = el.childNodes;
      /** @type {number} */
      var i = 0;
      var len = nodes.length;
      for (;i < len;i++) {
        $.destroy(nodes[i]);
      }
    };
    /**
     * @param {Object} elem
     * @param {string} type
     * @param {Function} cb
     * @return {undefined}
     */
    $.addEvent = function(elem, type, cb) {
      if (elem.addEventListener) {
        elem.addEventListener(type, cb, false);
      } else {
        elem.attachEvent("on" + type, cb);
      }
    };
    /**
     * @param {Object} element
     * @param {Object} events
     * @return {undefined}
     */
    $.addEvents = function(element, events) {
      var type;
      for (type in events) {
        $.addEvent(element, type, events[type]);
      }
    };
    /**
     * @param {Element} domElement
     * @param {string} selector
     * @return {?}
     */
    $.hasClass = function(domElement, selector) {
      return(" " + domElement.className + " ").indexOf(" " + selector + " ") > -1;
    };
    /**
     * @param {Element} domElement
     * @param {string} className
     * @return {undefined}
     */
    $.addClass = function(domElement, className) {
      if (!$.hasClass(domElement, className)) {
        /** @type {string} */
        domElement.className = domElement.className + " " + className;
      }
    };
    /**
     * @param {Element} element
     * @param {string} classNames
     * @return {undefined}
     */
    $.removeClass = function(element, classNames) {
      element.className = element.className.replace(new RegExp("(^|\\\\s)" + classNames + "(?:\\\\s|$)"), "$1");
    };
    /**
     * @param {Object} c
     * @return {?}
     */
    $.getPos = function(c) {
      /**
       * @param {Object} elem
       * @return {?}
       */
      function getOffsets(elem) {
        var offset = {
          x : 0,
          y : 0
        };
        for (;elem && !isBody(elem);) {
          offset.x += elem.offsetLeft;
          offset.y += elem.offsetTop;
          elem = elem.offsetParent;
        }
        return offset;
      }
      /**
       * @param {HTMLElement} element
       * @return {?}
       */
      function getScrolls(element) {
        var position = {
          x : 0,
          y : 0
        };
        for (;element && !isBody(element);) {
          position.x += element.scrollLeft;
          position.y += element.scrollTop;
          element = element.parentNode;
        }
        return position;
      }
      /**
       * @param {Object} element
       * @return {?}
       */
      function isBody(element) {
        return/^(?:body|html)$/i.test(element.tagName);
      }
      var pos = getOffsets(c);
      var e = getScrolls(c);
      return{
        x : pos.x - e.x,
        y : pos.y - e.y
      };
    };
    $.event = {
      /**
       * @param {Object} adj
       * @param {Object} lab
       * @return {?}
       */
      get : function(adj, lab) {
        lab = lab || window;
        return adj || lab.event;
      },
      /**
       * @param {Event} e
       * @return {?}
       */
      getWheel : function(e) {
        return e.wheelDelta ? e.wheelDelta / 120 : -(e.detail || 0) / 3;
      },
      /**
       * @param {Event} event
       * @return {?}
       */
      isRightClick : function(event) {
        return event.which == 3 || event.button == 2;
      },
      /**
       * @param {Object} value
       * @param {Object} win
       * @return {?}
       */
      getPos : function(value, win) {
        win = win || window;
        value = value || win.event;
        var doc = win.document;
        doc = doc.documentElement || doc.body;
        if (value.touches && value.touches.length) {
          value = value.touches[0];
        }
        var pos = {
          x : value.pageX || value.clientX + doc.scrollLeft,
          y : value.pageY || value.clientY + doc.scrollTop
        };
        return pos;
      },
      /**
       * @param {Event} event
       * @return {undefined}
       */
      stop : function(event) {
        if (event.stopPropagation) {
          event.stopPropagation();
        }
        /** @type {boolean} */
        event.cancelBubble = true;
        if (event.preventDefault) {
          event.preventDefault();
        } else {
          /** @type {boolean} */
          event.returnValue = false;
        }
      }
    };
    /** @type {function (?): ?} */
    $jit.util = $jit.id = $;
    /**
     * @param {Object} properties
     * @return {?}
     */
    var Class = function(properties) {
      properties = properties || {};
      /**
       * @return {?}
       */
      var klass = function() {
        var p;
        for (p in this) {
          if (typeof this[p] != "function") {
            this[p] = $.unlink(this[p]);
          }
        }
        /** @type {function (): ?} */
        this.constructor = klass;
        if (Class.prototyping) {
          return this;
        }
        var instance = this.initialize ? this.initialize.apply(this, arguments) : this;
        /** @type {string} */
        this.$$family = "class";
        return instance;
      };
      var mutator;
      for (mutator in Class.Mutators) {
        if (!properties[mutator]) {
          continue;
        }
        properties = Class.Mutators[mutator](properties, properties[mutator]);
        delete properties[mutator];
      }
      $.extend(klass, this);
      /** @type {function (Object): ?} */
      klass.constructor = Class;
      /** @type {Object} */
      klass.prototype = properties;
      return klass;
    };
    Class.Mutators = {
      /**
       * @param {Object} self
       * @param {string} klasses
       * @return {?}
       */
      Implements : function(self, klasses) {
        $.each($.splat(klasses), function(klass) {
          /** @type {string} */
          Class.prototyping = klass;
          var iterable = typeof klass == "function" ? new klass : klass;
          var key;
          for (key in iterable) {
            if (!(key in self)) {
              self[key] = iterable[key];
            }
          }
          delete Class.prototyping;
        });
        return self;
      }
    };
    $.extend(Class, {
      /**
       * @param {Object} object
       * @param {Object} properties
       * @return {?}
       */
      inherit : function(object, properties) {
        var key;
        for (key in properties) {
          var override = properties[key];
          var previous = object[key];
          var type = $.type(override);
          if (previous && type == "function") {
            if (override != previous) {
              Class.override(object, key, override);
            }
          } else {
            if (type == "object") {
              object[key] = $.merge(previous, override);
            } else {
              object[key] = override;
            }
          }
        }
        return object;
      },
      /**
       * @param {Object} object
       * @param {string} name
       * @param {Function} matcherFunction
       * @return {undefined}
       */
      override : function(object, name, matcherFunction) {
        var parent = Class.prototyping;
        if (parent && object[name] != parent[name]) {
          /** @type {null} */
          parent = null;
        }
        /**
         * @return {?}
         */
        var next = function() {
          var tmp = this.parent;
          this.parent = parent ? parent[name] : object[name];
          var rv = matcherFunction.apply(this, arguments);
          this.parent = tmp;
          return rv;
        };
        /** @type {function (): ?} */
        object[name] = next;
      }
    });
    /**
     * @return {?}
     */
    Class.prototype.implement = function() {
      var proto = this.prototype;
      $.each(Array.prototype.slice.call(arguments || []), function(properties) {
        Class.inherit(proto, properties);
      });
      return this;
    };
    /** @type {function (Object): ?} */
    $jit.Class = Class;
    $jit.json = {
      /**
       * @param {?} attributes
       * @param {?} maxLevel
       * @return {undefined}
       */
      prune : function(attributes, maxLevel) {
        this.each(attributes, function(elem, i) {
          if (i == maxLevel && elem.children) {
            delete elem.children;
            /** @type {Array} */
            elem.children = [];
          }
        });
      },
      /**
       * @param {Object} tree
       * @param {?} id
       * @return {?}
       */
      getParent : function(tree, id) {
        if (tree.id == id) {
          return false;
        }
        var ch = tree.children;
        if (ch && ch.length > 0) {
          /** @type {number} */
          var i = 0;
          for (;i < ch.length;i++) {
            if (ch[i].id == id) {
              return tree;
            } else {
              var ans = this.getParent(ch[i], id);
              if (ans) {
                return ans;
              }
            }
          }
        }
        return false;
      },
      /**
       * @param {Object} tree
       * @param {?} id
       * @return {?}
       */
      getSubtree : function(tree, id) {
        if (tree.id == id) {
          return tree;
        }
        /** @type {number} */
        var i = 0;
        var ch = tree.children;
        for (;ch && i < ch.length;i++) {
          var t = this.getSubtree(ch[i], id);
          if (t != null) {
            return t;
          }
        }
        return null;
      },
      /**
       * @param {number} value
       * @param {number} opt_isDefault
       * @param {Function} recurring
       * @param {Function} action
       * @return {undefined}
       */
      eachLevel : function(value, opt_isDefault, recurring, action) {
        if (opt_isDefault <= recurring) {
          action(value, opt_isDefault);
          if (!value.children) {
            return;
          }
          /** @type {number} */
          var i = 0;
          var codeSegments = value.children;
          for (;i < codeSegments.length;i++) {
            this.eachLevel(codeSegments[i], opt_isDefault + 1, recurring, action);
          }
        }
      },
      /**
       * @param {?} opt_attributes
       * @param {Function} action
       * @return {undefined}
       */
      each : function(opt_attributes, action) {
        this.eachLevel(opt_attributes, 0, Number.MAX_VALUE, action);
      }
    };
    $jit.Trans = {
      $extend : true,
      /**
       * @param {?} t
       * @return {?}
       */
      linear : function(t) {
        return t;
      }
    };
    var $cookies = $jit.Trans;
    (function() {
      /**
       * @param {Function} transition
       * @param {Text} params
       * @return {?}
       */
      var makeTrans = function(transition, params) {
        params = $.splat(params);
        return $.extend(transition, {
          /**
           * @param {?} pos
           * @return {?}
           */
          easeIn : function(pos) {
            return transition(pos, params);
          },
          /**
           * @param {number} pos
           * @return {?}
           */
          easeOut : function(pos) {
            return 1 - transition(1 - pos, params);
          },
          /**
           * @param {number} pos
           * @return {?}
           */
          easeInOut : function(pos) {
            return pos <= 0.5 ? transition(2 * pos, params) / 2 : (2 - transition(2 * (1 - pos), params)) / 2;
          }
        });
      };
      var transitions = {
        /**
         * @param {?} p
         * @param {Array} x
         * @return {?}
         */
        Pow : function(p, x) {
          return Math.pow(p, x[0] || 6);
        },
        /**
         * @param {number} p
         * @return {?}
         */
        Expo : function(p) {
          return Math.pow(2, 8 * (p - 1));
        },
        /**
         * @param {?} p
         * @return {?}
         */
        Circ : function(p) {
          return 1 - Math.sin(Math.acos(p));
        },
        /**
         * @param {number} p
         * @return {?}
         */
        Sine : function(p) {
          return 1 - Math.sin((1 - p) * Math.PI / 2);
        },
        /**
         * @param {?} p
         * @param {number} x
         * @return {?}
         */
        Back : function(p, x) {
          x = x[0] || 1.618;
          return Math.pow(p, 2) * ((x + 1) * p - x);
        },
        /**
         * @param {number} p
         * @return {?}
         */
        Bounce : function(p) {
          var value;
          /** @type {number} */
          var a = 0;
          /** @type {number} */
          var b = 1;
          for (;1;a += b, b /= 2) {
            if (p >= (7 - 4 * a) / 11) {
              /** @type {number} */
              value = b * b - Math.pow((11 - 6 * a - 11 * p) / 4, 2);
              break;
            }
          }
          return value;
        },
        /**
         * @param {number} p
         * @param {Array} x
         * @return {?}
         */
        Elastic : function(p, x) {
          return Math.pow(2, 10 * --p) * Math.cos(20 * p * Math.PI * (x[0] || 1) / 3);
        }
      };
      $.each(transitions, function(value, key) {
        $cookies[key] = makeTrans(value);
      });
      $.each(["Quad", "Cubic", "Quart", "Quint"], function(key, dataAndEvents) {
        $cookies[key] = makeTrans(function(pos) {
          return Math.pow(pos, [dataAndEvents + 2]);
        });
      });
    })();
    var Animation = new Class({
      /**
       * @param {Object} options
       * @return {undefined}
       */
      initialize : function(options) {
        this.setOptions(options);
      },
      /**
       * @param {Object} options
       * @return {?}
       */
      setOptions : function(options) {
        var opt = {
          duration : 2500,
          fps : 40,
          transition : $cookies.Quart.easeInOut,
          /** @type {function (): undefined} */
          compute : $.empty,
          /** @type {function (): undefined} */
          complete : $.empty,
          link : "ignore"
        };
        this.opt = $.merge(opt, options || {});
        return this;
      },
      /**
       * @return {undefined}
       */
      step : function() {
        /** @type {number} */
        var time = $.time();
        var opt = this.opt;
        if (time < this.time + opt.duration) {
          var from = opt.transition((time - this.time) / opt.duration);
          opt.compute(from);
        } else {
          this.timer = clearInterval(this.timer);
          opt.compute(1);
          opt.complete();
        }
      },
      /**
       * @return {?}
       */
      start : function() {
        if (!this.check()) {
          return this;
        }
        /** @type {number} */
        this.time = 0;
        this.startTimer();
        return this;
      },
      /**
       * @return {?}
       */
      startTimer : function() {
        var self = this;
        var fps = this.opt.fps;
        if (this.timer) {
          return false;
        }
        /** @type {number} */
        this.time = $.time() - this.time;
        /** @type {number} */
        this.timer = setInterval(function() {
          self.step();
        }, Math.round(1E3 / fps));
        return true;
      },
      /**
       * @return {?}
       */
      pause : function() {
        this.stopTimer();
        return this;
      },
      /**
       * @return {?}
       */
      resume : function() {
        this.startTimer();
        return this;
      },
      /**
       * @return {?}
       */
      stopTimer : function() {
        if (!this.timer) {
          return false;
        }
        /** @type {number} */
        this.time = $.time() - this.time;
        this.timer = clearInterval(this.timer);
        return true;
      },
      /**
       * @return {?}
       */
      check : function() {
        if (!this.timer) {
          return true;
        }
        if (this.opt.link == "cancel") {
          this.stopTimer();
          return true;
        }
        return false;
      }
    });
    /**
     * @return {?}
     */
    var Options = function() {
      /** @type {Arguments} */
      var args = arguments;
      /** @type {number} */
      var i = 0;
      /** @type {number} */
      var argLength = args.length;
      var methods = {};
      for (;i < argLength;i++) {
        var attributes = Options[args[i]];
        if (attributes.$extend) {
          $.extend(methods, attributes);
        } else {
          methods[args[i]] = attributes;
        }
      }
      return methods;
    };
    Options.AreaChart = {
      $extend : true,
      animate : true,
      labelOffset : 3,
      type : "stacked",
      Tips : {
        enable : false,
        /** @type {function (): undefined} */
        onShow : $.empty,
        /** @type {function (): undefined} */
        onHide : $.empty
      },
      Events : {
        enable : false,
        /** @type {function (): undefined} */
        onClick : $.empty
      },
      selectOnHover : true,
      showAggregates : true,
      showLabels : true,
      filterOnClick : false,
      restoreOnRightClick : false
    };
    Options.Margin = {
      $extend : false,
      top : 0,
      left : 0,
      right : 0,
      bottom : 0
    };
    Options.Canvas = {
      $extend : true,
      injectInto : "id",
      type : "2D",
      width : false,
      height : false,
      useCanvas : false,
      withLabels : true,
      background : false,
      Scene : {
        Lighting : {
          enable : false,
          ambient : [1, 1, 1],
          directional : {
            direction : {
              x : -100,
              y : -100,
              z : -100
            },
            color : [0.5, 0.3, 0.1]
          }
        }
      }
    };
    Options.Tree = {
      $extend : true,
      orientation : "left",
      subtreeOffset : 8,
      siblingOffset : 5,
      indent : 10,
      multitree : false,
      align : "center"
    };
    Options.Node = {
      $extend : false,
      overridable : false,
      type : "circle",
      color : "#ccb",
      alpha : 1,
      dim : 3,
      height : 20,
      width : 90,
      autoHeight : false,
      autoWidth : false,
      lineWidth : 1,
      transform : true,
      align : "center",
      angularWidth : 1,
      span : 1,
      CanvasStyles : {}
    };
    Options.Edge = {
      $extend : false,
      overridable : false,
      type : "line",
      color : "#ccb",
      lineWidth : 1,
      dim : 15,
      alpha : 1,
      epsilon : 7,
      CanvasStyles : {}
    };
    Options.Fx = {
      $extend : true,
      fps : 40,
      duration : 2500,
      transition : $jit.Trans.Quart.easeInOut,
      clearCanvas : true
    };
    Options.Label = {
      $extend : false,
      overridable : false,
      type : "HTML",
      style : " ",
      size : 10,
      family : "sans-serif",
      textAlign : "center",
      textBaseline : "alphabetic",
      color : "#fff"
    };
    Options.Tips = {
      $extend : false,
      enable : false,
      type : "auto",
      offsetX : 20,
      offsetY : 20,
      force : false,
      /** @type {function (): undefined} */
      onShow : $.empty,
      /** @type {function (): undefined} */
      onHide : $.empty
    };
    Options.NodeStyles = {
      $extend : false,
      enable : false,
      type : "auto",
      stylesHover : false,
      stylesClick : false
    };
    Options.Events = {
      $extend : false,
      enable : false,
      enableForEdges : false,
      type : "auto",
      /** @type {function (): undefined} */
      onClick : $.empty,
      /** @type {function (): undefined} */
      onRightClick : $.empty,
      /** @type {function (): undefined} */
      onMouseMove : $.empty,
      /** @type {function (): undefined} */
      onMouseEnter : $.empty,
      /** @type {function (): undefined} */
      onMouseLeave : $.empty,
      /** @type {function (): undefined} */
      onDragStart : $.empty,
      /** @type {function (): undefined} */
      onDragMove : $.empty,
      /** @type {function (): undefined} */
      onDragCancel : $.empty,
      /** @type {function (): undefined} */
      onDragEnd : $.empty,
      /** @type {function (): undefined} */
      onTouchStart : $.empty,
      /** @type {function (): undefined} */
      onTouchMove : $.empty,
      /** @type {function (): undefined} */
      onTouchEnd : $.empty,
      /** @type {function (): undefined} */
      onMouseWheel : $.empty
    };
    Options.Navigation = {
      $extend : false,
      enable : false,
      type : "auto",
      panning : false,
      zooming : false
    };
    Options.Controller = {
      $extend : true,
      /** @type {function (): undefined} */
      onBeforeCompute : $.empty,
      /** @type {function (): undefined} */
      onAfterCompute : $.empty,
      /** @type {function (): undefined} */
      onCreateLabel : $.empty,
      /** @type {function (): undefined} */
      onPlaceLabel : $.empty,
      /** @type {function (): undefined} */
      onComplete : $.empty,
      /** @type {function (): undefined} */
      onBeforePlotLine : $.empty,
      /** @type {function (): undefined} */
      onAfterPlotLine : $.empty,
      /** @type {function (): undefined} */
      onBeforePlotNode : $.empty,
      /** @type {function (): undefined} */
      onAfterPlotNode : $.empty,
      request : false
    };
    var Events = {
      /**
       * @param {?} className
       * @param {Object} viz
       * @return {undefined}
       */
      initialize : function(className, viz) {
        /** @type {Object} */
        this.viz = viz;
        this.canvas = viz.canvas;
        this.config = viz.config[className];
        this.nodeTypes = viz.fx.nodeTypes;
        var type = this.config.type;
        /** @type {boolean} */
        this.dom = type == "auto" ? viz.config.Label.type != "Native" : type != "Native";
        this.labelContainer = this.dom && viz.labels.getLabelContainer();
        if (this.isEnabled()) {
          this.initializePost();
        }
      },
      /** @type {function (): undefined} */
      initializePost : $.empty,
      setAsProperty : $.lambda(false),
      /**
       * @return {?}
       */
      isEnabled : function() {
        return this.config.enable;
      },
      /**
       * @param {?} adj
       * @param {?} lab
       * @param {boolean} recurring
       * @return {?}
       */
      isLabel : function(adj, lab, recurring) {
        adj = $.event.get(adj, lab);
        var labelContainer = this.labelContainer;
        var target = adj.target || adj.srcElement;
        var related = adj.relatedTarget;
        if (recurring) {
          return related && (related == this.viz.canvas.getCtx().canvas && (!!target && this.isDescendantOf(target, labelContainer)));
        } else {
          return this.isDescendantOf(target, labelContainer);
        }
      },
      /**
       * @param {HTMLElement} elem
       * @param {?} par
       * @return {?}
       */
      isDescendantOf : function(elem, par) {
        for (;elem && elem.parentNode;) {
          if (elem.parentNode == par) {
            return elem;
          }
          elem = elem.parentNode;
        }
        return false;
      }
    };
    var Aspect = {
      /** @type {function (): undefined} */
      onMouseUp : $.empty,
      /** @type {function (): undefined} */
      onMouseDown : $.empty,
      /** @type {function (): undefined} */
      onMouseMove : $.empty,
      /** @type {function (): undefined} */
      onMouseOver : $.empty,
      /** @type {function (): undefined} */
      onMouseOut : $.empty,
      /** @type {function (): undefined} */
      onMouseWheel : $.empty,
      /** @type {function (): undefined} */
      onTouchStart : $.empty,
      /** @type {function (): undefined} */
      onTouchMove : $.empty,
      /** @type {function (): undefined} */
      onTouchEnd : $.empty,
      /** @type {function (): undefined} */
      onTouchCancel : $.empty
    };
    var Tips = new Class({
      /**
       * @param {?} viz
       * @return {undefined}
       */
      initialize : function(viz) {
        this.viz = viz;
        this.canvas = viz.canvas;
        /** @type {boolean} */
        this.node = false;
        /** @type {boolean} */
        this.edge = false;
        /** @type {Array} */
        this.registeredObjects = [];
        this.attachEvents();
      },
      /**
       * @return {undefined}
       */
      attachEvents : function() {
        var element = this.canvas.getElement();
        var that = this;
        element.oncontextmenu = $.lambda(false);
        $.addEvents(element, {
          /**
           * @param {Object} e
           * @param {Object} win
           * @return {undefined}
           */
          mouseup : function(e, win) {
            var event = $.event.get(e, win);
            that.handleEvent("MouseUp", e, win, that.makeEventObject(e, win), $.event.isRightClick(event));
          },
          /**
           * @param {Object} e
           * @param {Object} win
           * @return {undefined}
           */
          mousedown : function(e, win) {
            var event = $.event.get(e, win);
            that.handleEvent("MouseDown", e, win, that.makeEventObject(e, win), $.event.isRightClick(event));
          },
          /**
           * @param {Object} e
           * @param {?} win
           * @return {undefined}
           */
          mousemove : function(e, win) {
            that.handleEvent("MouseMove", e, win, that.makeEventObject(e, win));
          },
          /**
           * @param {Object} e
           * @param {?} win
           * @return {undefined}
           */
          mouseover : function(e, win) {
            that.handleEvent("MouseOver", e, win, that.makeEventObject(e, win));
          },
          /**
           * @param {Object} e
           * @param {?} win
           * @return {undefined}
           */
          mouseout : function(e, win) {
            that.handleEvent("MouseOut", e, win, that.makeEventObject(e, win));
          },
          /**
           * @param {Object} e
           * @param {?} win
           * @return {undefined}
           */
          touchstart : function(e, win) {
            that.handleEvent("TouchStart", e, win, that.makeEventObject(e, win));
          },
          /**
           * @param {Object} e
           * @param {?} win
           * @return {undefined}
           */
          touchmove : function(e, win) {
            that.handleEvent("TouchMove", e, win, that.makeEventObject(e, win));
          },
          /**
           * @param {Object} e
           * @param {?} win
           * @return {undefined}
           */
          touchend : function(e, win) {
            that.handleEvent("TouchEnd", e, win, that.makeEventObject(e, win));
          }
        });
        /**
         * @param {Object} from
         * @param {Object} win
         * @return {undefined}
         */
        var handleMouseWheel = function(from, win) {
          var event = $.event.get(from, win);
          var wheel = $.event.getWheel(event);
          that.handleEvent("MouseWheel", from, win, wheel);
        };
        if (!document.getBoxObjectFor && window.mozInnerScreenX == null) {
          $.addEvent(element, "mousewheel", handleMouseWheel);
        } else {
          element.addEventListener("DOMMouseScroll", handleMouseWheel, false);
        }
      },
      /**
       * @param {?} obj
       * @return {undefined}
       */
      register : function(obj) {
        this.registeredObjects.push(obj);
      },
      /**
       * @return {undefined}
       */
      handleEvent : function() {
        /** @type {Array.<?>} */
        var args = Array.prototype.slice.call(arguments);
        var type = args.shift();
        /** @type {number} */
        var i = 0;
        var regs = this.registeredObjects;
        var l = regs.length;
        for (;i < l;i++) {
          regs[i]["on" + type].apply(regs[i], args);
        }
      },
      /**
       * @param {Object} prop
       * @param {?} win
       * @return {?}
       */
      makeEventObject : function(prop, win) {
        var that = this;
        var graph = this.viz.graph;
        var fx = this.viz.fx;
        var ntypes = fx.nodeTypes;
        var etypes = fx.edgeTypes;
        return{
          pos : false,
          node : false,
          edge : false,
          contains : false,
          getNodeCalled : false,
          getEdgeCalled : false,
          /**
           * @return {?}
           */
          getPos : function() {
            var canvas = that.viz.canvas;
            var $cont = canvas.getSize();
            var cameraPos = canvas.getPos();
            var ox = canvas.translateOffsetX;
            var oy = canvas.translateOffsetY;
            var sx = canvas.scaleOffsetX;
            var sy = canvas.scaleOffsetY;
            var pos = $.event.getPos(prop, win);
            this.pos = {
              x : (pos.x - cameraPos.x - $cont.width / 2 - ox) * 1 / sx,
              y : (pos.y - cameraPos.y - $cont.height / 2 - oy) * 1 / sy
            };
            return this.pos;
          },
          /**
           * @return {?}
           */
          getNode : function() {
            if (this.getNodeCalled) {
              return this.node;
            }
            /** @type {boolean} */
            this.getNodeCalled = true;
            var id;
            for (id in graph.nodes) {
              var n = graph.nodes[id];
              var geom = n && ntypes[n.getData("type")];
              var contains = geom && (geom.contains && geom.contains.call(fx, n, this.getPos()));
              if (contains) {
                this.contains = contains;
                return that.node = this.node = n;
              }
            }
            return that.node = this.node = false;
          },
          /**
           * @return {?}
           */
          getEdge : function() {
            if (this.getEdgeCalled) {
              return this.edge;
            }
            /** @type {boolean} */
            this.getEdgeCalled = true;
            var hashset = {};
            var id;
            for (id in graph.edges) {
              var edgeFrom = graph.edges[id];
              /** @type {boolean} */
              hashset[id] = true;
              var edgeId;
              for (edgeId in edgeFrom) {
                if (edgeId in hashset) {
                  continue;
                }
                var e = edgeFrom[edgeId];
                var geom = e && etypes[e.getData("type")];
                var contains = geom && (geom.contains && geom.contains.call(fx, e, this.getPos()));
                if (contains) {
                  this.contains = contains;
                  return that.edge = this.edge = e;
                }
              }
            }
            return that.edge = this.edge = false;
          },
          /**
           * @return {?}
           */
          getContains : function() {
            if (this.getNodeCalled) {
              return this.contains;
            }
            this.getNode();
            return this.contains;
          }
        };
      }
    });
    var Extras = {
      /**
       * @return {undefined}
       */
      initializeExtras : function() {
        var doh = new Tips(this);
        var that = this;
        $.each(["NodeStyles", "Tips", "Navigation", "Events"], function(k) {
          var obj = new Extras.Classes[k](k, that);
          if (obj.isEnabled()) {
            doh.register(obj);
          }
          if (obj.setAsProperty()) {
            that[k.toLowerCase()] = obj;
          }
        });
      }
    };
    Extras.Classes = {};
    Extras.Classes.Events = new Class({
      Implements : [Events, Aspect],
      /**
       * @return {undefined}
       */
      initializePost : function() {
        this.fx = this.viz.fx;
        this.ntypes = this.viz.fx.nodeTypes;
        this.etypes = this.viz.fx.edgeTypes;
        /** @type {boolean} */
        this.hovered = false;
        /** @type {boolean} */
        this.pressed = false;
        /** @type {boolean} */
        this.touched = false;
        /** @type {boolean} */
        this.touchMoved = false;
        /** @type {boolean} */
        this.moved = false;
      },
      setAsProperty : $.lambda(true),
      /**
       * @param {?} adj
       * @param {?} lab
       * @param {?} event
       * @param {?} type
       * @return {undefined}
       */
      onMouseUp : function(adj, lab, event, type) {
        var qualifier = $.event.get(adj, lab);
        if (!this.moved) {
          if (type) {
            this.config.onRightClick(this.hovered, event, qualifier);
          } else {
            this.config.onClick(this.pressed, event, qualifier);
          }
        }
        if (this.pressed) {
          if (this.moved) {
            this.config.onDragEnd(this.pressed, event, qualifier);
          } else {
            this.config.onDragCancel(this.pressed, event, qualifier);
          }
          /** @type {boolean} */
          this.pressed = this.moved = false;
        }
      },
      /**
       * @param {?} adj
       * @param {?} lab
       * @param {?} event
       * @return {undefined}
       */
      onMouseOut : function(adj, lab, event) {
        var qualifier = $.event.get(adj, lab);
        var label;
        if (this.dom && (label = this.isLabel(adj, lab, true))) {
          this.config.onMouseLeave(this.viz.graph.getNode(label.id), event, qualifier);
          /** @type {boolean} */
          this.hovered = false;
          return;
        }
        var rt = qualifier.relatedTarget;
        var canvasWidget = this.canvas.getElement();
        for (;rt && rt.parentNode;) {
          if (canvasWidget == rt.parentNode) {
            return;
          }
          rt = rt.parentNode;
        }
        if (this.hovered) {
          this.config.onMouseLeave(this.hovered, event, qualifier);
          /** @type {boolean} */
          this.hovered = false;
        }
      },
      /**
       * @param {?} adj
       * @param {?} lab
       * @param {?} event
       * @return {undefined}
       */
      onMouseOver : function(adj, lab, event) {
        var qualifier = $.event.get(adj, lab);
        var label;
        if (this.dom && (label = this.isLabel(adj, lab, true))) {
          this.hovered = this.viz.graph.getNode(label.id);
          this.config.onMouseEnter(this.hovered, event, qualifier);
        }
      },
      /**
       * @param {?} adj
       * @param {?} lab
       * @param {?} event
       * @return {undefined}
       */
      onMouseMove : function(adj, lab, event) {
        var x;
        var qualifier = $.event.get(adj, lab);
        if (this.pressed) {
          /** @type {boolean} */
          this.moved = true;
          this.config.onDragMove(this.pressed, event, qualifier);
          return;
        }
        if (this.dom) {
          this.config.onMouseMove(this.hovered, event, qualifier);
        } else {
          if (this.hovered) {
            var from = this.hovered;
            var geom = from.nodeFrom ? this.etypes[from.getData("type")] : this.ntypes[from.getData("type")];
            var contains = geom && (geom.contains && geom.contains.call(this.fx, from, event.getPos()));
            if (contains) {
              this.config.onMouseMove(from, event, qualifier);
              return;
            } else {
              this.config.onMouseLeave(from, event, qualifier);
              /** @type {boolean} */
              this.hovered = false;
            }
          }
          if (this.hovered = event.getNode() || this.config.enableForEdges && event.getEdge()) {
            this.config.onMouseEnter(this.hovered, event, qualifier);
          } else {
            this.config.onMouseMove(false, event, qualifier);
          }
        }
      },
      /**
       * @param {?} adj
       * @param {?} lab
       * @param {?} from
       * @return {undefined}
       */
      onMouseWheel : function(adj, lab, from) {
        this.config.onMouseWheel(from, $.event.get(adj, lab));
      },
      /**
       * @param {?} adj
       * @param {?} lab
       * @param {?} event
       * @return {undefined}
       */
      onMouseDown : function(adj, lab, event) {
        var qualifier = $.event.get(adj, lab);
        var label;
        if (this.dom) {
          if (label = this.isLabel(adj, lab)) {
            this.pressed = this.viz.graph.getNode(label.id);
          }
        } else {
          this.pressed = event.getNode() || this.config.enableForEdges && event.getEdge();
        }
        if (this.pressed) {
          this.config.onDragStart(this.pressed, event, qualifier);
        }
      },
      /**
       * @param {?} adj
       * @param {?} lab
       * @param {?} event
       * @return {undefined}
       */
      onTouchStart : function(adj, lab, event) {
        var qualifier = $.event.get(adj, lab);
        var label;
        if (this.dom && (label = this.isLabel(adj, lab))) {
          this.touched = this.viz.graph.getNode(label.id);
        } else {
          this.touched = event.getNode() || this.config.enableForEdges && event.getEdge();
        }
        if (this.touched) {
          this.config.onTouchStart(this.touched, event, qualifier);
        }
      },
      /**
       * @param {?} adj
       * @param {?} lab
       * @param {?} event
       * @return {undefined}
       */
      onTouchMove : function(adj, lab, event) {
        var qualifier = $.event.get(adj, lab);
        if (this.touched) {
          /** @type {boolean} */
          this.touchMoved = true;
          this.config.onTouchMove(this.touched, event, qualifier);
        }
      },
      /**
       * @param {?} adj
       * @param {?} lab
       * @param {?} event
       * @return {undefined}
       */
      onTouchEnd : function(adj, lab, event) {
        var qualifier = $.event.get(adj, lab);
        if (this.touched) {
          if (this.touchMoved) {
            this.config.onTouchEnd(this.touched, event, qualifier);
          } else {
            this.config.onTouchCancel(this.touched, event, qualifier);
          }
          /** @type {boolean} */
          this.touched = this.touchMoved = false;
        }
      }
    });
    Extras.Classes.Tips = new Class({
      Implements : [Events, Aspect],
      /**
       * @return {undefined}
       */
      initializePost : function() {
        if (document.body) {
          var tip = $("_tooltip") || document.createElement("div");
          /** @type {string} */
          tip.id = "_tooltip";
          /** @type {string} */
          tip.className = "tip";
          $.extend(tip.style, {
            position : "absolute",
            display : "none",
            zIndex : 13E3
          });
          document.body.appendChild(tip);
          this.tip = tip;
          /** @type {boolean} */
          this.node = false;
        }
      },
      setAsProperty : $.lambda(true),
      /**
       * @param {?} adj
       * @param {?} lab
       * @return {undefined}
       */
      onMouseOut : function(adj, lab) {
        var orn = $.event.get(adj, lab);
        if (this.dom && this.isLabel(adj, lab, true)) {
          this.hide(true);
          return;
        }
        var rt = adj.relatedTarget;
        var canvasWidget = this.canvas.getElement();
        for (;rt && rt.parentNode;) {
          if (canvasWidget == rt.parentNode) {
            return;
          }
          rt = rt.parentNode;
        }
        this.hide(false);
      },
      /**
       * @param {?} adj
       * @param {?} lab
       * @return {undefined}
       */
      onMouseOver : function(adj, lab) {
        var qualifier;
        if (this.dom && (qualifier = this.isLabel(adj, lab, false))) {
          this.node = this.viz.graph.getNode(qualifier.id);
          this.config.onShow(this.tip, this.node, qualifier);
        }
      },
      /**
       * @param {?} adj
       * @param {?} lab
       * @param {?} event
       * @return {undefined}
       */
      onMouseMove : function(adj, lab, event) {
        if (this.dom && this.isLabel(adj, lab)) {
          this.setTooltipPosition($.event.getPos(adj, lab));
        }
        if (!this.dom) {
          var cycle = event.getNode();
          if (!cycle) {
            this.hide(true);
            return;
          }
          if (this.config.force || (!this.node || this.node.id != cycle.id)) {
            this.node = cycle;
            this.config.onShow(this.tip, cycle, event.getContains());
          }
          this.setTooltipPosition($.event.getPos(adj, lab));
        }
      },
      /**
       * @param {?} pos
       * @return {undefined}
       */
      setTooltipPosition : function(pos) {
        var tip = this.tip;
        var style = tip.style;
        var cont = this.config;
        /** @type {string} */
        style.display = "";
        var win = {
          height : document.body.clientHeight,
          width : document.body.clientWidth
        };
        var obj = {
          width : tip.offsetWidth,
          height : tip.offsetHeight
        };
        var x = cont.offsetX;
        var y = cont.offsetY;
        /** @type {string} */
        style.top = (pos.y + y + obj.height > win.height ? pos.y - obj.height - y : pos.y + y) + "px";
        /** @type {string} */
        style.left = (pos.x + obj.width + x > win.width ? pos.x - obj.width - x : pos.x + x) + "px";
      },
      /**
       * @param {boolean} recurring
       * @return {undefined}
       */
      hide : function(recurring) {
        /** @type {string} */
        this.tip.style.display = "none";
        if (recurring) {
          this.config.onHide();
        }
      }
    });
    Extras.Classes.NodeStyles = new Class({
      Implements : [Events, Aspect],
      /**
       * @return {undefined}
       */
      initializePost : function() {
        this.fx = this.viz.fx;
        this.types = this.viz.fx.nodeTypes;
        this.nStyles = this.config;
        this.nodeStylesOnHover = this.nStyles.stylesHover;
        this.nodeStylesOnClick = this.nStyles.stylesClick;
        /** @type {boolean} */
        this.hoveredNode = false;
        this.fx.nodeFxAnimation = new Animation;
        /** @type {boolean} */
        this.down = false;
        /** @type {boolean} */
        this.move = false;
      },
      /**
       * @param {?} adj
       * @param {?} lab
       * @return {undefined}
       */
      onMouseOut : function(adj, lab) {
        /** @type {boolean} */
        this.down = this.move = false;
        if (!this.hoveredNode) {
          return;
        }
        if (this.dom && this.isLabel(adj, lab, true)) {
          this.toggleStylesOnHover(this.hoveredNode, false);
        }
        var rt = adj.relatedTarget;
        var canvasWidget = this.canvas.getElement();
        for (;rt && rt.parentNode;) {
          if (canvasWidget == rt.parentNode) {
            return;
          }
          rt = rt.parentNode;
        }
        this.toggleStylesOnHover(this.hoveredNode, false);
        /** @type {boolean} */
        this.hoveredNode = false;
      },
      /**
       * @param {?} adj
       * @param {?} lab
       * @return {undefined}
       */
      onMouseOver : function(adj, lab) {
        var label;
        if (this.dom && (label = this.isLabel(adj, lab, true))) {
          var node = this.viz.graph.getNode(label.id);
          if (node.selected) {
            return;
          }
          this.hoveredNode = node;
          this.toggleStylesOnHover(this.hoveredNode, true);
        }
      },
      /**
       * @param {?} adj
       * @param {?} lab
       * @param {?} event
       * @param {?} type
       * @return {undefined}
       */
      onMouseDown : function(adj, lab, event, type) {
        if (type) {
          return;
        }
        var label;
        if (this.dom && (label = this.isLabel(adj, lab))) {
          this.down = this.viz.graph.getNode(label.id);
        } else {
          if (!this.dom) {
            this.down = event.getNode();
          }
        }
        /** @type {boolean} */
        this.move = false;
      },
      /**
       * @param {?} adj
       * @param {?} lab
       * @param {?} event
       * @param {?} type
       * @return {undefined}
       */
      onMouseUp : function(adj, lab, event, type) {
        if (type) {
          return;
        }
        if (!this.move) {
          this.onClick(event.getNode());
        }
        /** @type {boolean} */
        this.down = this.move = false;
      },
      /**
       * @param {Object} node
       * @param {string} type
       * @return {?}
       */
      getRestoredStyles : function(node, type) {
        var restoredStyles = {};
        var source = this["nodeStylesOn" + type];
        var prop;
        for (prop in source) {
          restoredStyles[prop] = node.styles["$" + prop];
        }
        return restoredStyles;
      },
      /**
       * @param {?} node
       * @param {boolean} recurring
       * @return {undefined}
       */
      toggleStylesOnHover : function(node, recurring) {
        if (this.nodeStylesOnHover) {
          this.toggleStylesOn("Hover", node, recurring);
        }
      },
      /**
       * @param {Object} node
       * @param {boolean} recurring
       * @return {undefined}
       */
      toggleStylesOnClick : function(node, recurring) {
        if (this.nodeStylesOnClick) {
          this.toggleStylesOn("Click", node, recurring);
        }
      },
      /**
       * @param {string} type
       * @param {Object} node
       * @param {boolean} recurring
       * @return {undefined}
       */
      toggleStylesOn : function(type, node, recurring) {
        var viz = this.viz;
        var nStyles = this.nStyles;
        if (recurring) {
          var that = this;
          if (!node.styles) {
            node.styles = $.merge(node.data, {});
          }
          var s;
          for (s in this["nodeStylesOn" + type]) {
            /** @type {string} */
            var $s = "$" + s;
            if (!($s in node.styles)) {
              node.styles[$s] = node.getData(s);
            }
          }
          viz.fx.nodeFx($.extend({
            elements : {
              id : node.id,
              properties : that["nodeStylesOn" + type]
            },
            transition : $cookies.Quart.easeOut,
            duration : 300,
            fps : 40
          }, this.config));
        } else {
          var queue = this.getRestoredStyles(node, type);
          viz.fx.nodeFx($.extend({
            elements : {
              id : node.id,
              properties : queue
            },
            transition : $cookies.Quart.easeOut,
            duration : 300,
            fps : 40
          }, this.config));
        }
      },
      /**
       * @param {?} adj
       * @return {undefined}
       */
      onClick : function(adj) {
        if (!adj) {
          return;
        }
        var nStyles = this.nodeStylesOnClick;
        if (!nStyles) {
          return;
        }
        if (adj.selected) {
          this.toggleStylesOnClick(adj, false);
          delete adj.selected;
        } else {
          this.viz.graph.eachNode(function(n) {
            if (n.selected) {
              var s;
              for (s in nStyles) {
                n.setData(s, n.styles["$" + s], "end");
              }
              delete n.selected;
            }
          });
          this.toggleStylesOnClick(adj, true);
          /** @type {boolean} */
          adj.selected = true;
          delete adj.hovered;
          /** @type {boolean} */
          this.hoveredNode = false;
        }
      },
      /**
       * @param {?} adj
       * @param {?} lab
       * @param {?} event
       * @return {undefined}
       */
      onMouseMove : function(adj, lab, event) {
        if (this.down) {
          /** @type {boolean} */
          this.move = true;
        }
        if (this.dom && this.isLabel(adj, lab)) {
          return;
        }
        var nStyles = this.nodeStylesOnHover;
        if (!nStyles) {
          return;
        }
        if (!this.dom) {
          if (this.hoveredNode) {
            var geom = this.types[this.hoveredNode.getData("type")];
            var contains = geom && (geom.contains && geom.contains.call(this.fx, this.hoveredNode, event.getPos()));
            if (contains) {
              return;
            }
          }
          var node = event.getNode();
          if (!this.hoveredNode && !node) {
            return;
          }
          if (node.hovered) {
            return;
          }
          if (node && !node.selected) {
            this.fx.nodeFxAnimation.stopTimer();
            this.viz.graph.eachNode(function(n) {
              if (n.hovered && !n.selected) {
                var s;
                for (s in nStyles) {
                  n.setData(s, n.styles["$" + s], "end");
                }
                delete n.hovered;
              }
            });
            /** @type {boolean} */
            node.hovered = true;
            this.hoveredNode = node;
            this.toggleStylesOnHover(node, true);
          } else {
            if (this.hoveredNode && !this.hoveredNode.selected) {
              this.fx.nodeFxAnimation.stopTimer();
              this.toggleStylesOnHover(this.hoveredNode, false);
              delete this.hoveredNode.hovered;
              /** @type {boolean} */
              this.hoveredNode = false;
            }
          }
        }
      }
    });
    Extras.Classes.Navigation = new Class({
      Implements : [Events, Aspect],
      /**
       * @return {undefined}
       */
      initializePost : function() {
        /** @type {boolean} */
        this.pos = false;
        /** @type {boolean} */
        this.pressed = false;
      },
      /**
       * @param {?} adj
       * @param {?} lab
       * @param {?} event
       * @return {undefined}
       */
      onMouseWheel : function(adj, lab, event) {
        if (!this.config.zooming) {
          return;
        }
        $.event.stop($.event.get(adj, lab));
        /** @type {number} */
        var A = this.config.zooming / 1E3;
        /** @type {number} */
        var scaling = 1 + event * A;
        this.canvas.scale(scaling, scaling);
      },
      /**
       * @param {?} adj
       * @param {?} lab
       * @param {?} event
       * @return {undefined}
       */
      onMouseDown : function(adj, lab, event) {
        if (!this.config.panning) {
          return;
        }
        if (this.config.panning == "avoid nodes" && (this.dom ? this.isLabel(adj, lab) : event.getNode())) {
          return;
        }
        /** @type {boolean} */
        this.pressed = true;
        this.pos = event.getPos();
        var canvas = this.canvas;
        var ox = canvas.translateOffsetX;
        var oy = canvas.translateOffsetY;
        var sx = canvas.scaleOffsetX;
        var sy = canvas.scaleOffsetY;
        this.pos.x *= sx;
        this.pos.x += ox;
        this.pos.y *= sy;
        this.pos.y += oy;
      },
      /**
       * @param {?} adj
       * @param {?} lab
       * @param {?} event
       * @return {undefined}
       */
      onMouseMove : function(adj, lab, event) {
        if (!this.config.panning) {
          return;
        }
        if (!this.pressed) {
          return;
        }
        if (this.config.panning == "avoid nodes" && (this.dom ? this.isLabel(adj, lab) : event.getNode())) {
          return;
        }
        var thispos = this.pos;
        var currentPos = event.getPos();
        var canvas = this.canvas;
        var ox = canvas.translateOffsetX;
        var oy = canvas.translateOffsetY;
        var sx = canvas.scaleOffsetX;
        var sy = canvas.scaleOffsetY;
        currentPos.x *= sx;
        currentPos.y *= sy;
        currentPos.x += ox;
        currentPos.y += oy;
        /** @type {number} */
        var x = currentPos.x - thispos.x;
        /** @type {number} */
        var y = currentPos.y - thispos.y;
        this.pos = currentPos;
        this.canvas.translate(x * 1 / sx, y * 1 / sy);
      },
      /**
       * @param {?} adj
       * @param {?} lab
       * @param {?} event
       * @param {?} type
       * @return {undefined}
       */
      onMouseUp : function(adj, lab, event, type) {
        if (!this.config.panning) {
          return;
        }
        /** @type {boolean} */
        this.pressed = false;
      }
    });
    var Canvas;
    (function() {
      /**
       * @param {string} tag
       * @param {?} props
       * @return {?}
       */
      function $E(tag, props) {
        /** @type {Element} */
        var elem = document.createElement(tag);
        var name;
        for (name in props) {
          if (typeof props[name] == "object") {
            $.extend(elem[name], props[name]);
          } else {
            elem[name] = props[name];
          }
        }
        if (tag == "canvas" && (!supportsCanvas && G_vmlCanvasManager)) {
          elem = G_vmlCanvasManager.initElement(document.body.appendChild(elem));
        }
        return elem;
      }
      /** @type {string} */
      var typeOfCanvas = typeof HTMLCanvasElement;
      /** @type {boolean} */
      var supportsCanvas = typeOfCanvas == "object" || typeOfCanvas == "function";
      $jit.Canvas = Canvas = new Class({
        canvases : [],
        pos : false,
        element : false,
        labelContainer : false,
        translateOffsetX : 0,
        translateOffsetY : 0,
        scaleOffsetX : 1,
        scaleOffsetY : 1,
        /**
         * @param {Object} viz
         * @param {Object} opt
         * @return {undefined}
         */
        initialize : function(viz, opt) {
          /** @type {Object} */
          this.viz = viz;
          this.opt = this.config = opt;
          var id = $.type(opt.injectInto) == "string" ? opt.injectInto : opt.injectInto.id;
          var type = opt.type;
          /** @type {string} */
          var idLabel = id + "-label";
          var wrapper = $(id);
          var originalWidth = opt.width || wrapper.offsetWidth;
          var _height = opt.height || wrapper.offsetHeight;
          this.id = id;
          var canvasOptions = {
            injectInto : id,
            width : originalWidth,
            height : _height
          };
          this.element = $E("div", {
            id : id + "-canvaswidget",
            style : {
              position : "relative",
              width : originalWidth + "px",
              height : _height + "px"
            }
          });
          this.labelContainer = this.createLabelContainer(opt.Label.type, idLabel, canvasOptions);
          this.canvases.push(new Canvas.Base[type]({
            config : $.extend({
              idSuffix : "-canvas"
            }, canvasOptions),
            /**
             * @param {?} opt
             * @return {undefined}
             */
            plot : function(opt) {
              viz.fx.plot();
            },
            /**
             * @return {undefined}
             */
            resize : function() {
              viz.refresh();
            }
          }));
          var back = opt.background;
          if (back) {
            var backCanvas = new Canvas.Background[back.type](viz, $.extend(back, canvasOptions));
            this.canvases.push(new Canvas.Base[type](backCanvas));
          }
          var len = this.canvases.length;
          for (;len--;) {
            this.element.appendChild(this.canvases[len].canvas);
            if (len > 0) {
              this.canvases[len].plot();
            }
          }
          this.element.appendChild(this.labelContainer);
          wrapper.appendChild(this.element);
          /** @type {null} */
          var tref = null;
          var f = this;
          $.addEvent(window, "scroll", function() {
            clearTimeout(tref);
            /** @type {number} */
            tref = setTimeout(function() {
              f.getPos(true);
            }, 500);
          });
        },
        /**
         * @param {number} i
         * @return {?}
         */
        getCtx : function(i) {
          return this.canvases[i || 0].getCtx();
        },
        /**
         * @return {?}
         */
        getConfig : function() {
          return this.opt;
        },
        /**
         * @return {?}
         */
        getElement : function() {
          return this.element;
        },
        /**
         * @param {number} dataAndEvents
         * @return {?}
         */
        getSize : function(dataAndEvents) {
          return this.canvases[dataAndEvents || 0].getSize();
        },
        /**
         * @param {number} w
         * @param {number} height
         * @return {undefined}
         */
        resize : function(w, height) {
          this.getPos(true);
          /** @type {number} */
          this.translateOffsetX = this.translateOffsetY = 0;
          /** @type {number} */
          this.scaleOffsetX = this.scaleOffsetY = 1;
          /** @type {number} */
          var i = 0;
          var l = this.canvases.length;
          for (;i < l;i++) {
            this.canvases[i].resize(w, height);
          }
          var style = this.element.style;
          /** @type {string} */
          style.width = w + "px";
          /** @type {string} */
          style.height = height + "px";
          if (this.labelContainer) {
            /** @type {string} */
            this.labelContainer.style.width = w + "px";
          }
        },
        /**
         * @param {number} x
         * @param {number} y
         * @param {boolean} z
         * @return {undefined}
         */
        translate : function(x, y, z) {
          this.translateOffsetX += x * this.scaleOffsetX;
          this.translateOffsetY += y * this.scaleOffsetY;
          /** @type {number} */
          var i = 0;
          var l = this.canvases.length;
          for (;i < l;i++) {
            this.canvases[i].translate(x, y, z);
          }
        },
        /**
         * @param {number} x
         * @param {number} y
         * @param {boolean} dataAndEvents
         * @return {undefined}
         */
        scale : function(x, y, dataAndEvents) {
          /** @type {number} */
          var px = this.scaleOffsetX * x;
          /** @type {number} */
          var py = this.scaleOffsetY * y;
          /** @type {number} */
          var ll = this.translateOffsetX * (x - 1) / px;
          /** @type {number} */
          var dy = this.translateOffsetY * (y - 1) / py;
          /** @type {number} */
          this.scaleOffsetX = px;
          /** @type {number} */
          this.scaleOffsetY = py;
          /** @type {number} */
          var i = 0;
          var l = this.canvases.length;
          for (;i < l;i++) {
            this.canvases[i].scale(x, y, true);
          }
          this.translate(ll, dy, false);
        },
        /**
         * @param {boolean} expectation
         * @return {?}
         */
        getPos : function(expectation) {
          if (expectation || !this.pos) {
            return this.pos = $.getPos(this.getElement());
          }
          return this.pos;
        },
        /**
         * @param {number} arr
         * @return {undefined}
         */
        clear : function(arr) {
          this.canvases[arr || 0].clear();
        },
        /**
         * @param {?} id
         * @param {?} callback
         * @return {undefined}
         */
        path : function(id, callback) {
          var me = this.canvases[0].getCtx();
          me.beginPath();
          callback(me);
          me[id]();
          me.closePath();
        },
        /**
         * @param {string} type
         * @param {string} idLabel
         * @param {?} dim
         * @return {?}
         */
        createLabelContainer : function(type, idLabel, dim) {
          /** @type {string} */
          var NS = "http://www.w3.org/2000/svg";
          if (type == "HTML" || type == "Native") {
            return $E("div", {
              id : idLabel,
              style : {
                overflow : "visible",
                position : "absolute",
                top : 0,
                left : 0,
                width : dim.width + "px",
                height : 0
              }
            });
          } else {
            if (type == "SVG") {
              /** @type {Element} */
              var svgContainer = document.createElementNS(NS, "svg:svg");
              svgContainer.setAttribute("width", dim.width);
              svgContainer.setAttribute("height", dim.height);
              /** @type {(CSSStyleDeclaration|null)} */
              var style = svgContainer.style;
              /** @type {string} */
              style.position = "absolute";
              /** @type {string} */
              style.left = style.top = "0px";
              /** @type {Element} */
              var labelContainer = document.createElementNS(NS, "svg:g");
              labelContainer.setAttribute("width", dim.width);
              labelContainer.setAttribute("height", dim.height);
              labelContainer.setAttribute("x", 0);
              labelContainer.setAttribute("y", 0);
              labelContainer.setAttribute("id", idLabel);
              svgContainer.appendChild(labelContainer);
              return svgContainer;
            }
          }
        }
      });
      Canvas.Base = {};
      Canvas.Base["2D"] = new Class({
        translateOffsetX : 0,
        translateOffsetY : 0,
        scaleOffsetX : 1,
        scaleOffsetY : 1,
        /**
         * @param {?} viz
         * @return {undefined}
         */
        initialize : function(viz) {
          this.viz = viz;
          this.opt = viz.config;
          /** @type {boolean} */
          this.size = false;
          this.createCanvas();
          this.translateToCenter();
        },
        /**
         * @return {undefined}
         */
        createCanvas : function() {
          var opt = this.opt;
          var width = opt.width;
          var h = opt.height;
          this.canvas = $E("canvas", {
            id : opt.injectInto + opt.idSuffix,
            width : width,
            height : h,
            style : {
              position : "absolute",
              top : 0,
              left : 0,
              width : width + "px",
              height : h + "px"
            }
          });
        },
        /**
         * @return {?}
         */
        getCtx : function() {
          if (!this.ctx) {
            return this.ctx = this.canvas.getContext("2d");
          }
          return this.ctx;
        },
        /**
         * @return {?}
         */
        getSize : function() {
          if (this.size) {
            return this.size;
          }
          var canvas = this.canvas;
          return this.size = {
            width : canvas.width,
            height : canvas.height
          };
        },
        /**
         * @param {boolean} ps
         * @return {undefined}
         */
        translateToCenter : function(ps) {
          var size = this.getSize();
          var width = ps ? size.width - ps.width - this.translateOffsetX * 2 : size.width;
          height = ps ? size.height - ps.height - this.translateOffsetY * 2 : size.height;
          var ctx = this.getCtx();
          if (ps) {
            ctx.scale(1 / this.scaleOffsetX, 1 / this.scaleOffsetY);
          }
          ctx.translate(width / 2, height / 2);
        },
        /**
         * @param {number} width
         * @param {number} height
         * @return {undefined}
         */
        resize : function(width, height) {
          var size = this.getSize();
          var canvas = this.canvas;
          var style = canvas.style;
          /** @type {boolean} */
          this.size = false;
          /** @type {number} */
          canvas.width = width;
          /** @type {number} */
          canvas.height = height;
          /** @type {string} */
          style.width = width + "px";
          /** @type {string} */
          style.height = height + "px";
          if (!supportsCanvas) {
            this.translateToCenter(size);
          } else {
            this.translateToCenter();
          }
          /** @type {number} */
          this.translateOffsetX = this.translateOffsetY = 0;
          /** @type {number} */
          this.scaleOffsetX = this.scaleOffsetY = 1;
          this.clear();
          this.viz.resize(width, height, this);
        },
        /**
         * @param {number} x
         * @param {number} y
         * @param {boolean} z
         * @return {undefined}
         */
        translate : function(x, y, z) {
          var sx = this.scaleOffsetX;
          var sy = this.scaleOffsetY;
          this.translateOffsetX += x * sx;
          this.translateOffsetY += y * sy;
          this.getCtx().translate(x, y);
          if (!z) {
            this.plot();
          }
        },
        /**
         * @param {number} x
         * @param {number} y
         * @param {boolean} dataAndEvents
         * @return {undefined}
         */
        scale : function(x, y, dataAndEvents) {
          this.scaleOffsetX *= x;
          this.scaleOffsetY *= y;
          this.getCtx().scale(x, y);
          if (!dataAndEvents) {
            this.plot();
          }
        },
        /**
         * @return {undefined}
         */
        clear : function() {
          var size = this.getSize();
          var ox = this.translateOffsetX;
          var oy = this.translateOffsetY;
          var sx = this.scaleOffsetX;
          var sy = this.scaleOffsetY;
          this.getCtx().clearRect((-size.width / 2 - ox) * 1 / sx, (-size.height / 2 - oy) * 1 / sy, size.width * 1 / sx, size.height * 1 / sy);
        },
        /**
         * @return {undefined}
         */
        plot : function() {
          this.clear();
          this.viz.plot(this);
        }
      });
      Canvas.Background = {};
      Canvas.Background.Circles = new Class({
        /**
         * @param {?} viz
         * @param {?} options
         * @return {undefined}
         */
        initialize : function(viz, options) {
          this.viz = viz;
          this.config = $.merge({
            idSuffix : "-bkcanvas",
            levelDistance : 100,
            numberOfCircles : 6,
            CanvasStyles : {},
            offset : 0
          }, options);
        },
        /**
         * @param {number} w
         * @param {number} height
         * @param {?} opt
         * @return {undefined}
         */
        resize : function(w, height, opt) {
          this.plot(opt);
        },
        /**
         * @param {?} base
         * @return {undefined}
         */
        plot : function(base) {
          var canvas = base.canvas;
          var ctx = base.getCtx();
          var conf = this.config;
          var styles = conf.CanvasStyles;
          var s;
          for (s in styles) {
            ctx[s] = styles[s];
          }
          var n = conf.numberOfCircles;
          var rho = conf.levelDistance;
          /** @type {number} */
          var i = 1;
          for (;i <= n;i++) {
            ctx.beginPath();
            ctx.arc(0, 0, rho * i, 0, 2 * Math.PI, false);
            ctx.stroke();
            ctx.closePath();
          }
        }
      });
    })();
    /**
     * @param {number} v
     * @param {number} str
     * @return {undefined}
     */
    var Transform = function(v, str) {
      this.theta = v || 0;
      this.rho = str || 0;
    };
    /** @type {function (number, number): undefined} */
    $jit.Polar = Transform;
    Transform.prototype = {
      /**
       * @param {boolean} dataAndEvents
       * @return {?}
       */
      getc : function(dataAndEvents) {
        return this.toComplex(dataAndEvents);
      },
      /**
       * @return {?}
       */
      getp : function() {
        return this;
      },
      /**
       * @param {?} item
       * @return {undefined}
       */
      set : function(item) {
        item = item.getp();
        this.theta = item.theta;
        this.rho = item.rho;
      },
      /**
       * @param {number} x
       * @param {number} y
       * @return {undefined}
       */
      setc : function(x, y) {
        /** @type {number} */
        this.rho = Math.sqrt(x * x + y * y);
        /** @type {number} */
        this.theta = Math.atan2(y, x);
        if (this.theta < 0) {
          this.theta += Math.PI * 2;
        }
      },
      /**
       * @param {number} theta
       * @param {?} dataAndEvents
       * @return {undefined}
       */
      setp : function(theta, dataAndEvents) {
        /** @type {number} */
        this.theta = theta;
        this.rho = dataAndEvents;
      },
      /**
       * @return {?}
       */
      clone : function() {
        return new Transform(this.theta, this.rho);
      },
      /**
       * @param {boolean} dataAndEvents
       * @return {?}
       */
      toComplex : function(dataAndEvents) {
        /** @type {number} */
        var ex = Math.cos(this.theta) * this.rho;
        /** @type {number} */
        var py = Math.sin(this.theta) * this.rho;
        if (dataAndEvents) {
          return{
            x : ex,
            y : py
          };
        }
        return new Vector(ex, py);
      },
      /**
       * @param {?} v2
       * @return {?}
       */
      add : function(v2) {
        return new Transform(this.theta + v2.theta, this.rho + v2.rho);
      },
      /**
       * @param {number} x
       * @return {?}
       */
      scale : function(x) {
        return new Transform(this.theta, this.rho * x);
      },
      /**
       * @param {?} item
       * @return {?}
       */
      equals : function(item) {
        return this.theta == item.theta && this.rho == item.rho;
      },
      /**
       * @param {number} item
       * @return {?}
       */
      $add : function(item) {
        this.theta = this.theta + item.theta;
        this.rho += item.rho;
        return this;
      },
      /**
       * @param {?} item
       * @return {?}
       */
      $madd : function(item) {
        /** @type {number} */
        this.theta = (this.theta + item.theta) % (Math.PI * 2);
        this.rho += item.rho;
        return this;
      },
      /**
       * @param {number} opt_attributes
       * @return {?}
       */
      $scale : function(opt_attributes) {
        this.rho *= opt_attributes;
        return this;
      },
      /**
       * @return {?}
       */
      isZero : function() {
        /** @type {number} */
        var distance = 1E-4;
        /** @type {function (*): number} */
        var abs = Math.abs;
        return abs(this.theta) < distance && abs(this.rho) < distance;
      },
      /**
       * @param {?} item
       * @param {number} x
       * @return {?}
       */
      interpolate : function(item, x) {
        /** @type {number} */
        var end = Math.PI;
        /** @type {number} */
        var base = end * 2;
        /**
         * @param {number} n
         * @return {?}
         */
        var toString = function(n) {
          /** @type {number} */
          var callStr = n < 0 ? n % base + base : n % base;
          return callStr;
        };
        var a = this.theta;
        var b = item.theta;
        var str;
        /** @type {number} */
        var start = Math.abs(a - b);
        if (start == end) {
          if (a > b) {
            str = toString(b + (a - base - b) * x);
          } else {
            str = toString(b - base + (a - b) * x);
          }
        } else {
          if (start >= end) {
            if (a > b) {
              str = toString(b + (a - base - b) * x);
            } else {
              str = toString(b - base + (a - (b - base)) * x);
            }
          } else {
            str = toString(b + (a - b) * x);
          }
        }
        var rho = (this.rho - item.rho) * x + item.rho;
        return{
          theta : str,
          rho : rho
        };
      }
    };
    /**
     * @param {number} x
     * @param {number} recurring
     * @return {?}
     */
    var $P = function(x, recurring) {
      return new Transform(x, recurring);
    };
    Transform.KER = $P(0, 0);
    /**
     * @param {number} x
     * @param {(number|string)} y
     * @return {undefined}
     */
    var Vector = function(x, y) {
      this.x = x || 0;
      this.y = y || 0;
    };
    /** @type {function (number, (number|string)): undefined} */
    $jit.Complex = Vector;
    Vector.prototype = {
      /**
       * @return {?}
       */
      getc : function() {
        return this;
      },
      /**
       * @param {boolean} deepDataAndEvents
       * @return {?}
       */
      getp : function(deepDataAndEvents) {
        return this.toPolar(deepDataAndEvents);
      },
      /**
       * @param {?} pos
       * @return {undefined}
       */
      set : function(pos) {
        pos = pos.getc(true);
        this.x = pos.x;
        this.y = pos.y;
      },
      /**
       * @param {number} x1
       * @param {?} y
       * @return {undefined}
       */
      setc : function(x1, y) {
        /** @type {number} */
        this.x = x1;
        this.y = y;
      },
      /**
       * @param {?} theta2
       * @param {number} fact
       * @return {undefined}
       */
      setp : function(theta2, fact) {
        /** @type {number} */
        this.x = Math.cos(theta2) * fact;
        /** @type {number} */
        this.y = Math.sin(theta2) * fact;
      },
      /**
       * @return {?}
       */
      clone : function() {
        return new Vector(this.x, this.y);
      },
      /**
       * @param {boolean} deepDataAndEvents
       * @return {?}
       */
      toPolar : function(deepDataAndEvents) {
        var rho = this.norm();
        /** @type {number} */
        var x = Math.atan2(this.y, this.x);
        if (x < 0) {
          x += Math.PI * 2;
        }
        if (deepDataAndEvents) {
          return{
            theta : x,
            rho : rho
          };
        }
        return new Transform(x, rho);
      },
      /**
       * @return {?}
       */
      norm : function() {
        return Math.sqrt(this.squaredNorm());
      },
      /**
       * @return {?}
       */
      squaredNorm : function() {
        return this.x * this.x + this.y * this.y;
      },
      /**
       * @param {?} v2
       * @return {?}
       */
      add : function(v2) {
        return new Vector(this.x + v2.x, this.y + v2.y);
      },
      /**
       * @param {?} v
       * @return {?}
       */
      prod : function(v) {
        return new Vector(this.x * v.x - this.y * v.y, this.y * v.x + this.x * v.y);
      },
      /**
       * @return {?}
       */
      conjugate : function() {
        return new Vector(this.x, -this.y);
      },
      /**
       * @param {number} n
       * @return {?}
       */
      scale : function(n) {
        return new Vector(this.x * n, this.y * n);
      },
      /**
       * @param {?} other
       * @return {?}
       */
      equals : function(other) {
        return this.x == other.x && this.y == other.y;
      },
      /**
       * @param {number} vec
       * @return {?}
       */
      $add : function(vec) {
        this.x += vec.x;
        this.y += vec.y;
        return this;
      },
      /**
       * @param {?} c
       * @return {?}
       */
      $prod : function(c) {
        var x = this.x;
        var y = this.y;
        /** @type {number} */
        this.x = x * c.x - y * c.y;
        /** @type {number} */
        this.y = y * c.x + x * c.y;
        return this;
      },
      /**
       * @return {?}
       */
      $conjugate : function() {
        /** @type {number} */
        this.y = -this.y;
        return this;
      },
      /**
       * @param {number} opt_attributes
       * @return {?}
       */
      $scale : function(opt_attributes) {
        this.x *= opt_attributes;
        this.y *= opt_attributes;
        return this;
      },
      /**
       * @param {?} p
       * @return {?}
       */
      $div : function(p) {
        var x = this.x;
        var y = this.y;
        var z = p.squaredNorm();
        /** @type {number} */
        this.x = x * p.x + y * p.y;
        /** @type {number} */
        this.y = y * p.x - x * p.y;
        return this.$scale(1 / z);
      },
      /**
       * @return {?}
       */
      isZero : function() {
        /** @type {number} */
        var distance = 1E-4;
        /** @type {function (*): number} */
        var abs = Math.abs;
        return abs(this.x) < distance && abs(this.y) < distance;
      }
    };
    /**
     * @param {number} recurring
     * @param {number} mayParseLabeledStatementInstead
     * @return {?}
     */
    var getIndex = function(recurring, mayParseLabeledStatementInstead) {
      return new Vector(recurring, mayParseLabeledStatementInstead);
    };
    Vector.KER = getIndex(0, 0);
    $jit.Graph = new Class({
      /**
       * @param {Object} options
       * @param {?} Node
       * @param {?} Edge
       * @param {?} Label
       * @return {undefined}
       */
      initialize : function(options, Node, Edge, Label) {
        var innerOptions = {
          /** @type {function (number, (number|string)): undefined} */
          klass : Vector,
          Node : {}
        };
        this.Node = Node;
        this.Edge = Edge;
        this.Label = Label;
        this.opt = $.merge(innerOptions, options || {});
        this.nodes = {};
        this.edges = {};
        var that = this;
        this.nodeList = {};
        var i;
        for (i in methods) {
          that.nodeList[i] = function(method) {
            return function() {
              /** @type {Array.<?>} */
              var args = Array.prototype.slice.call(arguments);
              that.eachNode(function(reporter) {
                reporter[method].apply(reporter, args);
              });
            };
          }(i);
        }
      },
      /**
       * @param {?} id
       * @return {?}
       */
      getNode : function(id) {
        if (this.hasNode(id)) {
          return this.nodes[id];
        }
        return false;
      },
      /**
       * @param {?} adj
       * @return {?}
       */
      get : function(adj) {
        return this.getNode(adj);
      },
      /**
       * @param {?} name
       * @return {?}
       */
      getByName : function(name) {
        var i2;
        for (i2 in this.nodes) {
          var n = this.nodes[i2];
          if (n.name == name) {
            return n;
          }
        }
        return false;
      },
      /**
       * @param {?} id
       * @param {?} id2
       * @return {?}
       */
      getAdjacence : function(id, id2) {
        if (id in this.edges) {
          return this.edges[id][id2];
        }
        return false;
      },
      /**
       * @param {Object} obj
       * @return {?}
       */
      addNode : function(obj) {
        if (!this.nodes[obj.id]) {
          var edges = this.edges[obj.id] = {};
          this.nodes[obj.id] = new Graph.Node($.extend({
            id : obj.id,
            name : obj.name,
            data : $.merge(obj.data || {}, {}),
            adjacencies : edges
          }, this.opt.Node), this.opt.klass, this.Node, this.Edge, this.Label);
        }
        return this.nodes[obj.id];
      },
      /**
       * @param {Object} obj
       * @param {number} obj2
       * @param {number} data
       * @return {?}
       */
      addAdjacence : function(obj, obj2, data) {
        if (!this.hasNode(obj.id)) {
          this.addNode(obj);
        }
        if (!this.hasNode(obj2.id)) {
          this.addNode(obj2);
        }
        obj = this.nodes[obj.id];
        obj2 = this.nodes[obj2.id];
        if (!obj.adjacentTo(obj2)) {
          var adjsObj = this.edges[obj.id] = this.edges[obj.id] || {};
          var adjsObj2 = this.edges[obj2.id] = this.edges[obj2.id] || {};
          adjsObj[obj2.id] = adjsObj2[obj.id] = new Graph.Adjacence(obj, obj2, data, this.Edge, this.Label);
          return adjsObj[obj2.id];
        }
        return this.edges[obj.id][obj2.id];
      },
      /**
       * @param {Array} id
       * @return {undefined}
       */
      removeNode : function(id) {
        if (this.hasNode(id)) {
          delete this.nodes[id];
          var adjs = this.edges[id];
          var to;
          for (to in adjs) {
            delete this.edges[to][id];
          }
          delete this.edges[id];
        }
      },
      /**
       * @param {?} id2
       * @param {?} id1
       * @return {undefined}
       */
      removeAdjacence : function(id2, id1) {
        delete this.edges[id2][id1];
        delete this.edges[id1][id2];
      },
      /**
       * @param {?} id
       * @return {?}
       */
      hasNode : function(id) {
        return id in this.nodes;
      },
      /**
       * @return {undefined}
       */
      empty : function() {
        this.nodes = {};
        this.edges = {};
      }
    });
    var Graph = $jit.Graph;
    var methods;
    (function() {
      /**
       * @param {string} prefix
       * @param {number} prop
       * @param {string} type
       * @param {?} force
       * @param {Array} prefixConfig
       * @return {?}
       */
      var getDataInternal = function(prefix, prop, type, force, prefixConfig) {
        var data;
        type = type || "current";
        /** @type {string} */
        prefix = "$" + (prefix ? prefix + "-" : "");
        if (type == "current") {
          data = this.data;
        } else {
          if (type == "start") {
            data = this.startData;
          } else {
            if (type == "end") {
              data = this.endData;
            }
          }
        }
        /** @type {string} */
        var dollar = prefix + prop;
        if (force) {
          return data[dollar];
        }
        if (!this.Config.overridable) {
          return prefixConfig[prop] || 0;
        }
        return dollar in data ? data[dollar] : dollar in this.data ? this.data[dollar] : prefixConfig[prop] || 0;
      };
      /**
       * @param {string} prefix
       * @param {string} prop
       * @param {?} value
       * @param {string} type
       * @return {undefined}
       */
      var setDataInternal = function(prefix, prop, value, type) {
        type = type || "current";
        /** @type {string} */
        prefix = "$" + (prefix ? prefix + "-" : "");
        var data;
        if (type == "current") {
          data = this.data;
        } else {
          if (type == "start") {
            data = this.startData;
          } else {
            if (type == "end") {
              data = this.endData;
            }
          }
        }
        data[prefix + prop] = value;
      };
      /**
       * @param {string} prefix
       * @param {?} attributes
       * @return {undefined}
       */
      var removeDataInternal = function(prefix, attributes) {
        /** @type {string} */
        prefix = "$" + (prefix ? prefix + "-" : "");
        var that = this;
        $.each(attributes, function(t) {
          var pref = prefix + t;
          delete that.data[pref];
          delete that.endData[pref];
          delete that.startData[pref];
        });
      };
      methods = {
        /**
         * @param {string} x
         * @param {Object} callback
         * @param {?} force
         * @return {?}
         */
        getData : function(x, callback, force) {
          return getDataInternal.call(this, "", x, callback, force, this.Config);
        },
        /**
         * @param {string} prop
         * @param {number} recurring
         * @param {string} callback
         * @return {undefined}
         */
        setData : function(prop, recurring, callback) {
          setDataInternal.call(this, "", prop, recurring, callback);
        },
        /**
         * @param {(Array|string)} types
         * @param {?} obj
         * @return {undefined}
         */
        setDataset : function(types, obj) {
          types = $.splat(types);
          var attr;
          for (attr in obj) {
            /** @type {number} */
            var i = 0;
            var prevSources = $.splat(obj[attr]);
            var valuesLen = types.length;
            for (;i < valuesLen;i++) {
              this.setData(attr, prevSources[i], types[i]);
            }
          }
        },
        /**
         * @return {undefined}
         */
        removeData : function() {
          removeDataInternal.call(this, "", Array.prototype.slice.call(arguments));
        },
        /**
         * @param {string} prop
         * @param {?} type
         * @param {?} force
         * @return {?}
         */
        getCanvasStyle : function(prop, type, force) {
          return getDataInternal.call(this, "canvas", prop, type, force, this.Config.CanvasStyles);
        },
        /**
         * @param {string} prop
         * @param {?} value
         * @param {?} type
         * @return {undefined}
         */
        setCanvasStyle : function(prop, value, type) {
          setDataInternal.call(this, "canvas", prop, value, type);
        },
        /**
         * @param {(Array|string)} types
         * @param {Object} obj
         * @return {undefined}
         */
        setCanvasStyles : function(types, obj) {
          types = $.splat(types);
          var attr;
          for (attr in obj) {
            /** @type {number} */
            var i = 0;
            var prevSources = $.splat(obj[attr]);
            var valuesLen = types.length;
            for (;i < valuesLen;i++) {
              this.setCanvasStyle(attr, prevSources[i], types[i]);
            }
          }
        },
        /**
         * @return {undefined}
         */
        removeCanvasStyle : function() {
          removeDataInternal.call(this, "canvas", Array.prototype.slice.call(arguments));
        },
        /**
         * @param {string} property
         * @param {?} type
         * @param {?} force
         * @return {?}
         */
        getLabelData : function(property, type, force) {
          return getDataInternal.call(this, "label", property, type, force, this.Label);
        },
        /**
         * @param {string} prop
         * @param {?} value
         * @param {?} type
         * @return {undefined}
         */
        setLabelData : function(prop, value, type) {
          setDataInternal.call(this, "label", prop, value, type);
        },
        /**
         * @param {(Array|string)} types
         * @param {Object} obj
         * @return {undefined}
         */
        setLabelDataset : function(types, obj) {
          types = $.splat(types);
          var attr;
          for (attr in obj) {
            /** @type {number} */
            var i = 0;
            var prevSources = $.splat(obj[attr]);
            var valuesLen = types.length;
            for (;i < valuesLen;i++) {
              this.setLabelData(attr, prevSources[i], types[i]);
            }
          }
        },
        /**
         * @return {undefined}
         */
        removeLabelData : function() {
          removeDataInternal.call(this, "label", Array.prototype.slice.call(arguments));
        }
      };
    })();
    Graph.Node = new Class({
      /**
       * @param {?} attributes
       * @param {?} klass
       * @param {?} Node
       * @param {?} Edge
       * @param {?} Label
       * @return {undefined}
       */
      initialize : function(attributes, klass, Node, Edge, Label) {
        var innerOptions = {
          id : "",
          name : "",
          data : {},
          startData : {},
          endData : {},
          adjacencies : {},
          selected : false,
          drawn : false,
          exist : false,
          angleSpan : {
            begin : 0,
            end : 0
          },
          pos : new klass,
          startPos : new klass,
          endPos : new klass
        };
        $.extend(this, $.extend(innerOptions, attributes));
        this.Config = this.Node = Node;
        this.Edge = Edge;
        this.Label = Label;
      },
      /**
       * @param {Object} node
       * @return {?}
       */
      adjacentTo : function(node) {
        return node.id in this.adjacencies;
      },
      /**
       * @param {?} id
       * @return {?}
       */
      getAdjacency : function(id) {
        return this.adjacencies[id];
      },
      /**
       * @param {string} expectation
       * @return {?}
       */
      getPos : function(expectation) {
        expectation = expectation || "current";
        if (expectation == "current") {
          return this.pos;
        } else {
          if (expectation == "end") {
            return this.endPos;
          } else {
            if (expectation == "start") {
              return this.startPos;
            }
          }
        }
      },
      /**
       * @param {?} x
       * @param {string} type
       * @return {undefined}
       */
      setPos : function(x, type) {
        type = type || "current";
        var pos;
        if (type == "current") {
          pos = this.pos;
        } else {
          if (type == "end") {
            pos = this.endPos;
          } else {
            if (type == "start") {
              pos = this.startPos;
            }
          }
        }
        pos.set(x);
      }
    });
    Graph.Node.implement(methods);
    Graph.Adjacence = new Class({
      /**
       * @param {number} nodeFrom
       * @param {?} nodeTo
       * @param {Object} data
       * @param {?} Edge
       * @param {number} Label
       * @return {undefined}
       */
      initialize : function(nodeFrom, nodeTo, data, Edge, Label) {
        /** @type {number} */
        this.nodeFrom = nodeFrom;
        this.nodeTo = nodeTo;
        this.data = data || {};
        this.startData = {};
        this.endData = {};
        this.Config = this.Edge = Edge;
        /** @type {number} */
        this.Label = Label;
      }
    });
    Graph.Adjacence.implement(methods);
    Graph.Util = {
      /**
       * @param {string} param
       * @return {?}
       */
      filter : function(param) {
        if (!param || !($.type(param) == "string")) {
          return function() {
            return true;
          };
        }
        var codeSegments = param.split(" ");
        return function(searchParams) {
          /** @type {number} */
          var i = 0;
          for (;i < codeSegments.length;i++) {
            if (searchParams[codeSegments[i]]) {
              return false;
            }
          }
          return true;
        };
      },
      /**
       * @param {?} id
       * @param {?} i
       * @return {?}
       */
      getNode : function(id, i) {
        return id.nodes[i];
      },
      /**
       * @param {Function} graph
       * @param {Function} action
       * @param {string} flags
       * @return {undefined}
       */
      eachNode : function(graph, action, flags) {
        var filter = this.filter(flags);
        var i;
        for (i in graph.nodes) {
          if (filter(graph.nodes[i])) {
            action(graph.nodes[i]);
          }
        }
      },
      /**
       * @param {?} opt_attributes
       * @param {Function} action
       * @param {string} flags
       * @return {undefined}
       */
      each : function(opt_attributes, action, flags) {
        this.eachNode(opt_attributes, action, flags);
      },
      /**
       * @param {Function} node
       * @param {Function} action
       * @param {string} flags
       * @return {undefined}
       */
      eachAdjacency : function(node, action, flags) {
        var adj = node.adjacencies;
        var filter = this.filter(flags);
        var id;
        for (id in adj) {
          var a = adj[id];
          if (filter(a)) {
            if (a.nodeFrom != node) {
              var tmp = a.nodeFrom;
              a.nodeFrom = a.nodeTo;
              a.nodeTo = tmp;
            }
            action(a, id);
          }
        }
      },
      /**
       * @param {?} graph
       * @param {number} recurring
       * @param {number} startDepth
       * @param {string} flags
       * @return {undefined}
       */
      computeLevels : function(graph, recurring, startDepth, flags) {
        startDepth = startDepth || 0;
        var filter = this.filter(flags);
        this.eachNode(graph, function(node) {
          /** @type {boolean} */
          node._flag = false;
          /** @type {number} */
          node._depth = -1;
        }, flags);
        var root = graph.getNode(recurring);
        /** @type {number} */
        root._depth = startDepth;
        /** @type {Array} */
        var queue = [root];
        for (;queue.length != 0;) {
          var node = queue.pop();
          /** @type {boolean} */
          node._flag = true;
          this.eachAdjacency(node, function(adj) {
            var n = adj.nodeTo;
            if (n._flag == false && filter(n)) {
              if (n._depth < 0) {
                n._depth = node._depth + 1 + startDepth;
              }
              queue.unshift(n);
            }
          }, flags);
        }
      },
      /**
       * @param {Element} graph
       * @param {Function} id
       * @param {string} action
       * @param {string} flags
       * @return {undefined}
       */
      eachBFS : function(graph, id, action, flags) {
        var filter = this.filter(flags);
        this.clean(graph);
        /** @type {Array} */
        var lines = [graph.getNode(id)];
        for (;lines.length != 0;) {
          var node = lines.pop();
          /** @type {boolean} */
          node._flag = true;
          action(node, node._depth);
          this.eachAdjacency(node, function(adj) {
            var n = adj.nodeTo;
            if (n._flag == false && filter(n)) {
              /** @type {boolean} */
              n._flag = true;
              lines.unshift(n);
            }
          }, flags);
        }
      },
      /**
       * @param {number} b
       * @param {number} opt_isDefault
       * @param {Object} recurring
       * @param {Function} action
       * @param {string} flags
       * @return {undefined}
       */
      eachLevel : function(b, opt_isDefault, recurring, action, flags) {
        var d = b._depth;
        var filter = this.filter(flags);
        var root = this;
        recurring = recurring === false ? Number.MAX_VALUE - d : recurring;
        (function loopLevel(node, levelBegin, levelEnd) {
          var d = node._depth;
          if (d >= levelBegin && (d <= levelEnd && filter(node))) {
            action(node, d);
          }
          if (d < levelEnd) {
            root.eachAdjacency(node, function(adj) {
              var n = adj.nodeTo;
              if (n._depth > d) {
                loopLevel(n, levelBegin, levelEnd);
              }
            });
          }
        })(b, opt_isDefault + d, recurring + d);
      },
      /**
       * @param {Object} node
       * @param {Function} action
       * @param {string} flags
       * @return {undefined}
       */
      eachSubgraph : function(node, action, flags) {
        this.eachLevel(node, 0, false, action, flags);
      },
      /**
       * @param {Function} node
       * @param {Function} action
       * @param {string} flags
       * @return {undefined}
       */
      eachSubnode : function(node, action, flags) {
        this.eachLevel(node, 1, 1, action, flags);
      },
      /**
       * @param {?} node
       * @param {Object} options
       * @param {string} flags
       * @return {?}
       */
      anySubnode : function(node, options, flags) {
        /** @type {boolean} */
        var flag = false;
        options = options || $.lambda(true);
        var opts = $.type(options) == "string" ? function(deepDataAndEvents) {
          return deepDataAndEvents[options];
        } : options;
        this.eachSubnode(node, function(deepDataAndEvents) {
          if (opts(deepDataAndEvents)) {
            /** @type {boolean} */
            flag = true;
          }
        }, flags);
        return flag;
      },
      /**
       * @param {?} node
       * @param {number} options
       * @param {string} flags
       * @return {?}
       */
      getSubnodes : function(node, options, flags) {
        /** @type {Array} */
        var assigns = [];
        var A = this;
        options = options || 0;
        var isDefault;
        var recurring;
        if ($.type(options) == "array") {
          isDefault = options[0];
          recurring = options[1];
        } else {
          /** @type {number} */
          isDefault = options;
          /** @type {number} */
          recurring = Number.MAX_VALUE - node._depth;
        }
        this.eachLevel(node, isDefault, recurring, function(vvar) {
          assigns.push(vvar);
        }, flags);
        return assigns;
      },
      /**
       * @param {Function} node
       * @return {?}
       */
      getParents : function(node) {
        /** @type {Array} */
        var matched = [];
        this.eachAdjacency(node, function(adj) {
          var n = adj.nodeTo;
          if (n._depth < node._depth) {
            matched.push(n);
          }
        });
        return matched;
      },
      /**
       * @param {?} node
       * @param {?} id
       * @return {?}
       */
      isDescendantOf : function(node, id) {
        if (node.id == id) {
          return true;
        }
        var codeSegments = this.getParents(node);
        /** @type {boolean} */
        var ans = false;
        /** @type {number} */
        var i = 0;
        for (;!ans && i < codeSegments.length;i++) {
          ans = ans || this.isDescendantOf(codeSegments[i], id);
        }
        return ans;
      },
      /**
       * @param {?} graph
       * @return {undefined}
       */
      clean : function(graph) {
        this.eachNode(graph, function(v) {
          /** @type {boolean} */
          v._flag = false;
        });
      },
      /**
       * @param {?} graph
       * @param {string} prop
       * @param {string} flags
       * @return {?}
       */
      getClosestNodeToOrigin : function(graph, prop, flags) {
        return this.getClosestNodeToPos(graph, Transform.KER, prop, flags);
      },
      /**
       * @param {?} graph
       * @param {?} pos
       * @param {string} prop
       * @param {string} flags
       * @return {?}
       */
      getClosestNodeToPos : function(graph, pos, prop, flags) {
        /** @type {null} */
        var node = null;
        prop = prop || "current";
        pos = pos && pos.getc(true) || Vector.KER;
        /**
         * @param {?} a
         * @param {?} b
         * @return {?}
         */
        var distance = function(a, b) {
          /** @type {number} */
          var z0 = a.x - b.x;
          /** @type {number} */
          var z1 = a.y - b.y;
          return z0 * z0 + z1 * z1;
        };
        this.eachNode(graph, function(elem) {
          node = node == null || distance(elem.getPos(prop).getc(true), pos) < distance(node.getPos(prop).getc(true), pos) ? elem : node;
        }, flags);
        return node;
      }
    };
    $.each(["get", "getNode", "each", "eachNode", "computeLevels", "eachBFS", "clean", "getClosestNodeToPos", "getClosestNodeToOrigin"], function(m) {
      /**
       * @return {?}
       */
      Graph.prototype[m] = function() {
        return Graph.Util[m].apply(Graph.Util, [this].concat(Array.prototype.slice.call(arguments)));
      };
    });
    $.each(["eachAdjacency", "eachLevel", "eachSubgraph", "eachSubnode", "anySubnode", "getSubnodes", "getParents", "isDescendantOf"], function(m) {
      /**
       * @return {?}
       */
      Graph.Node.prototype[m] = function() {
        return Graph.Util[m].apply(Graph.Util, [this].concat(Array.prototype.slice.call(arguments)));
      };
    });
    Graph.Op = {
      options : {
        type : "nothing",
        duration : 2E3,
        hideLabels : true,
        fps : 30
      },
      /**
       * @param {?} viz
       * @return {undefined}
       */
      initialize : function(viz) {
        this.viz = viz;
      },
      /**
       * @param {(Array|string)} node
       * @param {?} opt_attributes
       * @return {undefined}
       */
      removeNode : function(node, opt_attributes) {
        var viz = this.viz;
        var options = $.merge(this.options, viz.controller, opt_attributes);
        var n = $.splat(node);
        var i;
        var element;
        var nodeObj;
        switch(options.type) {
          case "nothing":
            /** @type {number} */
            i = 0;
            for (;i < n.length;i++) {
              viz.graph.removeNode(n[i]);
            }
            break;
          case "replot":
            this.removeNode(n, {
              type : "nothing"
            });
            viz.labels.clearLabels();
            viz.refresh(true);
            break;
          case "fade:seq":
          ;
          case "fade":
            element = this;
            /** @type {number} */
            i = 0;
            for (;i < n.length;i++) {
              nodeObj = viz.graph.getNode(n[i]);
              nodeObj.setData("alpha", 0, "end");
            }
            viz.fx.animate($.merge(options, {
              modes : ["node-property:alpha"],
              /**
               * @return {undefined}
               */
              onComplete : function() {
                element.removeNode(n, {
                  type : "nothing"
                });
                viz.labels.clearLabels();
                viz.reposition();
                viz.fx.animate($.merge(options, {
                  modes : ["linear"]
                }));
              }
            }));
            break;
          case "fade:con":
            element = this;
            /** @type {number} */
            i = 0;
            for (;i < n.length;i++) {
              nodeObj = viz.graph.getNode(n[i]);
              nodeObj.setData("alpha", 0, "end");
              /** @type {boolean} */
              nodeObj.ignore = true;
            }
            viz.reposition();
            viz.fx.animate($.merge(options, {
              modes : ["node-property:alpha", "linear"],
              /**
               * @return {undefined}
               */
              onComplete : function() {
                element.removeNode(n, {
                  type : "nothing"
                });
                if (options.onComplete) {
                  options.onComplete();
                }
              }
            }));
            break;
          case "iter":
            element = this;
            viz.fx.sequence({
              /**
               * @return {?}
               */
              condition : function() {
                return n.length != 0;
              },
              /**
               * @return {undefined}
               */
              step : function() {
                element.removeNode(n.shift(), {
                  type : "nothing"
                });
                viz.labels.clearLabels();
              },
              /**
               * @return {undefined}
               */
              onComplete : function() {
                if (options.onComplete) {
                  options.onComplete();
                }
              },
              duration : Math.ceil(options.duration / n.length)
            });
            break;
          default:
            this.doError();
        }
      },
      /**
       * @param {?} vertex
       * @param {?} opt_attributes
       * @return {undefined}
       */
      removeEdge : function(vertex, opt_attributes) {
        var viz = this.viz;
        var options = $.merge(this.options, viz.controller, opt_attributes);
        var v = $.type(vertex[0]) == "string" ? [vertex] : vertex;
        var ii;
        var that;
        var nodeObj;
        switch(options.type) {
          case "nothing":
            /** @type {number} */
            ii = 0;
            for (;ii < v.length;ii++) {
              viz.graph.removeAdjacence(v[ii][0], v[ii][1]);
            }
            break;
          case "replot":
            this.removeEdge(v, {
              type : "nothing"
            });
            viz.refresh(true);
            break;
          case "fade:seq":
          ;
          case "fade":
            that = this;
            /** @type {number} */
            ii = 0;
            for (;ii < v.length;ii++) {
              nodeObj = viz.graph.getAdjacence(v[ii][0], v[ii][1]);
              if (nodeObj) {
                nodeObj.setData("alpha", 0, "end");
              }
            }
            viz.fx.animate($.merge(options, {
              modes : ["edge-property:alpha"],
              /**
               * @return {undefined}
               */
              onComplete : function() {
                that.removeEdge(v, {
                  type : "nothing"
                });
                viz.reposition();
                viz.fx.animate($.merge(options, {
                  modes : ["linear"]
                }));
              }
            }));
            break;
          case "fade:con":
            that = this;
            /** @type {number} */
            ii = 0;
            for (;ii < v.length;ii++) {
              nodeObj = viz.graph.getAdjacence(v[ii][0], v[ii][1]);
              if (nodeObj) {
                nodeObj.setData("alpha", 0, "end");
                /** @type {boolean} */
                nodeObj.ignore = true;
              }
            }
            viz.reposition();
            viz.fx.animate($.merge(options, {
              modes : ["edge-property:alpha", "linear"],
              /**
               * @return {undefined}
               */
              onComplete : function() {
                that.removeEdge(v, {
                  type : "nothing"
                });
                if (options.onComplete) {
                  options.onComplete();
                }
              }
            }));
            break;
          case "iter":
            that = this;
            viz.fx.sequence({
              /**
               * @return {?}
               */
              condition : function() {
                return v.length != 0;
              },
              /**
               * @return {undefined}
               */
              step : function() {
                that.removeEdge(v.shift(), {
                  type : "nothing"
                });
                viz.labels.clearLabels();
              },
              /**
               * @return {undefined}
               */
              onComplete : function() {
                options.onComplete();
              },
              duration : Math.ceil(options.duration / v.length)
            });
            break;
          default:
            this.doError();
        }
      },
      /**
       * @param {?} json
       * @param {?} opt
       * @return {undefined}
       */
      sum : function(json, opt) {
        var viz = this.viz;
        var options = $.merge(this.options, viz.controller, opt);
        var root = viz.root;
        var graph;
        viz.root = opt.id || viz.root;
        switch(options.type) {
          case "nothing":
            graph = viz.construct(json);
            graph.eachNode(function(rt) {
              rt.eachAdjacency(function(adj) {
                viz.graph.addAdjacence(adj.nodeFrom, adj.nodeTo, adj.data);
              });
            });
            break;
          case "replot":
            viz.refresh(true);
            this.sum(json, {
              type : "nothing"
            });
            viz.refresh(true);
            break;
          case "fade:seq":
          ;
          case "fade":
          ;
          case "fade:con":
            that = this;
            graph = viz.construct(json);
            var fadeEdges = this.preprocessSum(graph);
            /** @type {Array} */
            var modes = !fadeEdges ? ["node-property:alpha"] : ["node-property:alpha", "edge-property:alpha"];
            viz.reposition();
            if (options.type != "fade:con") {
              viz.fx.animate($.merge(options, {
                modes : ["linear"],
                /**
                 * @return {undefined}
                 */
                onComplete : function() {
                  viz.fx.animate($.merge(options, {
                    modes : modes,
                    /**
                     * @return {undefined}
                     */
                    onComplete : function() {
                      options.onComplete();
                    }
                  }));
                }
              }));
            } else {
              viz.graph.eachNode(function(elem) {
                if (elem.id != root && elem.pos.isZero()) {
                  elem.pos.set(elem.endPos);
                  elem.startPos.set(elem.endPos);
                }
              });
              viz.fx.animate($.merge(options, {
                modes : ["linear"].concat(modes)
              }));
            }
            break;
          default:
            this.doError();
        }
      },
      /**
       * @param {(Error|string)} json
       * @param {Element} opt
       * @param {Object} extraModes
       * @return {undefined}
       */
      morph : function(json, opt, extraModes) {
        extraModes = extraModes || {};
        var viz = this.viz;
        var options = $.merge(this.options, viz.controller, opt);
        var root = viz.root;
        var graph;
        viz.root = opt.id || viz.root;
        switch(options.type) {
          case "nothing":
            graph = viz.construct(json);
            graph.eachNode(function(elem) {
              var H = viz.graph.hasNode(elem.id);
              elem.eachAdjacency(function(adj) {
                /** @type {boolean} */
                var L = !!viz.graph.getAdjacence(adj.nodeFrom.id, adj.nodeTo.id);
                viz.graph.addAdjacence(adj.nodeFrom, adj.nodeTo, adj.data);
                if (L) {
                  var event = viz.graph.getAdjacence(adj.nodeFrom.id, adj.nodeTo.id);
                  var prop;
                  for (prop in adj.data || {}) {
                    event.data[prop] = adj.data[prop];
                  }
                }
              });
              if (H) {
                var jQuery = viz.graph.getNode(elem.id);
                var name;
                for (name in elem.data || {}) {
                  jQuery.data[name] = elem.data[name];
                }
              }
            });
            viz.graph.eachNode(function(elem) {
              elem.eachAdjacency(function(adj) {
                if (!graph.getAdjacence(adj.nodeFrom.id, adj.nodeTo.id)) {
                  viz.graph.removeAdjacence(adj.nodeFrom.id, adj.nodeTo.id);
                }
              });
              if (!graph.hasNode(elem.id)) {
                viz.graph.removeNode(elem.id);
              }
            });
            break;
          case "replot":
            viz.labels.clearLabels(true);
            this.morph(json, {
              type : "nothing"
            });
            viz.refresh(true);
            viz.refresh(true);
            break;
          case "fade:seq":
          ;
          case "fade":
          ;
          case "fade:con":
            that = this;
            graph = viz.construct(json);
            var selection = "node-property" in extraModes && $.map($.splat(extraModes["node-property"]), function(type) {
              return "$" + type;
            });
            viz.graph.eachNode(function(elem) {
              var response = graph.getNode(elem.id);
              if (!response) {
                elem.setData("alpha", 1);
                elem.setData("alpha", 1, "start");
                elem.setData("alpha", 0, "end");
                /** @type {boolean} */
                elem.ignore = true;
              } else {
                var template = response.data;
                var prop;
                for (prop in template) {
                  if (selection && $.indexOf(selection, prop) > -1) {
                    elem.endData[prop] = template[prop];
                  } else {
                    elem.data[prop] = template[prop];
                  }
                }
              }
            });
            viz.graph.eachNode(function(node) {
              if (node.ignore) {
                return;
              }
              node.eachAdjacency(function(adj) {
                if (adj.nodeFrom.ignore || adj.nodeTo.ignore) {
                  return;
                }
                var nodeFrom = graph.getNode(adj.nodeFrom.id);
                var nodeTo = graph.getNode(adj.nodeTo.id);
                if (!nodeFrom.adjacentTo(nodeTo)) {
                  adj = viz.graph.getAdjacence(nodeFrom.id, nodeTo.id);
                  /** @type {boolean} */
                  fadeEdges = true;
                  adj.setData("alpha", 1);
                  adj.setData("alpha", 1, "start");
                  adj.setData("alpha", 0, "end");
                }
              });
            });
            var fadeEdges = this.preprocessSum(graph);
            /** @type {Array} */
            var modes = !fadeEdges ? ["node-property:alpha"] : ["node-property:alpha", "edge-property:alpha"];
            modes[0] = modes[0] + ("node-property" in extraModes ? ":" + $.splat(extraModes["node-property"]).join(":") : "");
            modes[1] = (modes[1] || "edge-property:alpha") + ("edge-property" in extraModes ? ":" + $.splat(extraModes["edge-property"]).join(":") : "");
            if ("label-property" in extraModes) {
              modes.push("label-property:" + $.splat(extraModes["label-property"]).join(":"));
            }
            if (viz.reposition) {
              viz.reposition();
            } else {
              viz.compute("end");
            }
            viz.graph.eachNode(function(elem) {
              if (elem.id != root && elem.pos.getp().equals(Transform.KER)) {
                elem.pos.set(elem.endPos);
                elem.startPos.set(elem.endPos);
              }
            });
            viz.fx.animate($.merge(options, {
              modes : [extraModes.position || "polar"].concat(modes),
              /**
               * @return {undefined}
               */
              onComplete : function() {
                viz.graph.eachNode(function(elem) {
                  if (elem.ignore) {
                    viz.graph.removeNode(elem.id);
                  }
                });
                viz.graph.eachNode(function(rt) {
                  rt.eachAdjacency(function(adj) {
                    if (adj.ignore) {
                      viz.graph.removeAdjacence(adj.nodeFrom.id, adj.nodeTo.id);
                    }
                  });
                });
                options.onComplete();
              }
            }));
            break;
          default:
          ;
        }
      },
      /**
       * @param {?} node
       * @param {Object} opt
       * @return {undefined}
       */
      contract : function(node, opt) {
        var viz = this.viz;
        if (node.collapsed || !node.anySubnode($.lambda(true))) {
          return;
        }
        opt = $.merge(this.options, viz.config, opt || {}, {
          modes : ["node-property:alpha:span", "linear"]
        });
        /** @type {boolean} */
        node.collapsed = true;
        (function subn(n) {
          n.eachSubnode(function(ch) {
            /** @type {boolean} */
            ch.ignore = true;
            ch.setData("alpha", 0, opt.type == "animate" ? "end" : "current");
            subn(ch);
          });
        })(node);
        if (opt.type == "animate") {
          viz.compute("end");
          if (viz.rotated) {
            viz.rotate(viz.rotated, "none", {
              property : "end"
            });
          }
          (function subn(n) {
            n.eachSubnode(function(ch) {
              ch.setPos(node.getPos("end"), "end");
              subn(ch);
            });
          })(node);
          viz.fx.animate(opt);
        } else {
          if (opt.type == "replot") {
            viz.refresh();
          }
        }
      },
      /**
       * @param {Object} node
       * @param {Object} opt
       * @return {undefined}
       */
      expand : function(node, opt) {
        if (!("collapsed" in node)) {
          return;
        }
        var viz = this.viz;
        opt = $.merge(this.options, viz.config, opt || {}, {
          modes : ["node-property:alpha:span", "linear"]
        });
        delete node.collapsed;
        (function subn(n) {
          n.eachSubnode(function(ch) {
            delete ch.ignore;
            ch.setData("alpha", 1, opt.type == "animate" ? "end" : "current");
            subn(ch);
          });
        })(node);
        if (opt.type == "animate") {
          viz.compute("end");
          if (viz.rotated) {
            viz.rotate(viz.rotated, "none", {
              property : "end"
            });
          }
          viz.fx.animate(opt);
        } else {
          if (opt.type == "replot") {
            viz.refresh();
          }
        }
      },
      /**
       * @param {?} graph
       * @return {?}
       */
      preprocessSum : function(graph) {
        var viz = this.viz;
        graph.eachNode(function(elem) {
          if (!viz.graph.hasNode(elem.id)) {
            viz.graph.addNode(elem);
            var n = viz.graph.getNode(elem.id);
            n.setData("alpha", 0);
            n.setData("alpha", 0, "start");
            n.setData("alpha", 1, "end");
          }
        });
        /** @type {boolean} */
        var fadeEdges = false;
        graph.eachNode(function(rt) {
          rt.eachAdjacency(function(adj) {
            var nodeFrom = viz.graph.getNode(adj.nodeFrom.id);
            var nodeTo = viz.graph.getNode(adj.nodeTo.id);
            if (!nodeFrom.adjacentTo(nodeTo)) {
              adj = viz.graph.addAdjacence(nodeFrom, nodeTo, adj.data);
              if (nodeFrom.startAlpha == nodeFrom.endAlpha && nodeTo.startAlpha == nodeTo.endAlpha) {
                /** @type {boolean} */
                fadeEdges = true;
                adj.setData("alpha", 0);
                adj.setData("alpha", 0, "start");
                adj.setData("alpha", 1, "end");
              }
            }
          });
        });
        return fadeEdges;
      }
    };
    var self = {
      none : {
        /** @type {function (): undefined} */
        render : $.empty,
        contains : $.lambda(false)
      },
      circle : {
        /**
         * @param {?} adj
         * @param {?} lab
         * @param {?} event
         * @param {?} type
         * @return {undefined}
         */
        render : function(adj, lab, event, type) {
          var that = type.getCtx();
          that.beginPath();
          that.arc(lab.x, lab.y, event, 0, Math.PI * 2, true);
          that.closePath();
          that[adj]();
        },
        /**
         * @param {?} opt_attributes
         * @param {?} value
         * @param {?} testName
         * @return {?}
         */
        contains : function(opt_attributes, value, testName) {
          /** @type {number} */
          var z0 = opt_attributes.x - value.x;
          /** @type {number} */
          var z1 = opt_attributes.y - value.y;
          /** @type {number} */
          var z = z0 * z0 + z1 * z1;
          return z <= testName * testName;
        }
      },
      ellipse : {
        /**
         * @param {?} adj
         * @param {?} lab
         * @param {?} event
         * @param {?} type
         * @param {?} keepData
         * @return {undefined}
         */
        render : function(adj, lab, event, type, keepData) {
          var ctx = keepData.getCtx();
          /** @type {number} */
          var c = 1;
          /** @type {number} */
          var scaleY = 1;
          /** @type {number} */
          var scaleposx = 1;
          /** @type {number} */
          var scaleposy = 1;
          /** @type {number} */
          var diameter = 0;
          if (event > type) {
            /** @type {number} */
            diameter = event / 2;
            /** @type {number} */
            scaleY = type / event;
            /** @type {number} */
            scaleposy = event / type;
          } else {
            /** @type {number} */
            diameter = type / 2;
            /** @type {number} */
            c = event / type;
            /** @type {number} */
            scaleposx = type / event;
          }
          ctx.save();
          ctx.scale(c, scaleY);
          ctx.beginPath();
          ctx.arc(lab.x * scaleposx, lab.y * scaleposy, diameter, 0, Math.PI * 2, true);
          ctx.closePath();
          ctx[adj]();
          ctx.restore();
        },
        /**
         * @param {?} opt_attributes
         * @param {?} value
         * @param {?} testName
         * @param {?} epsilon
         * @return {?}
         */
        contains : function(opt_attributes, value, testName, epsilon) {
          /** @type {number} */
          var radii = 0;
          /** @type {number} */
          var B = 1;
          /** @type {number} */
          var t = 1;
          /** @type {number} */
          var z0 = 0;
          /** @type {number} */
          var z1 = 0;
          /** @type {number} */
          var distSq = 0;
          if (testName > epsilon) {
            /** @type {number} */
            radii = testName / 2;
            /** @type {number} */
            t = epsilon / testName;
          } else {
            /** @type {number} */
            radii = epsilon / 2;
            /** @type {number} */
            B = testName / epsilon;
          }
          /** @type {number} */
          z0 = (opt_attributes.x - value.x) * (1 / B);
          /** @type {number} */
          z1 = (opt_attributes.y - value.y) * (1 / t);
          /** @type {number} */
          distSq = z0 * z0 + z1 * z1;
          return distSq <= radii * radii;
        }
      },
      square : {
        /**
         * @param {?} adj
         * @param {?} lab
         * @param {?} event
         * @param {?} type
         * @return {undefined}
         */
        render : function(adj, lab, event, type) {
          type.getCtx()[adj + "Rect"](lab.x - event, lab.y - event, 2 * event, 2 * event);
        },
        /**
         * @param {?} opt_attributes
         * @param {?} value
         * @param {?} testName
         * @return {?}
         */
        contains : function(opt_attributes, value, testName) {
          return Math.abs(value.x - opt_attributes.x) <= testName && Math.abs(value.y - opt_attributes.y) <= testName;
        }
      },
      rectangle : {
        /**
         * @param {?} adj
         * @param {?} lab
         * @param {?} event
         * @param {?} type
         * @param {?} keepData
         * @return {undefined}
         */
        render : function(adj, lab, event, type, keepData) {
          keepData.getCtx()[adj + "Rect"](lab.x - event / 2, lab.y - type / 2, event, type);
        },
        /**
         * @param {?} opt_attributes
         * @param {?} value
         * @param {?} testName
         * @param {?} epsilon
         * @return {?}
         */
        contains : function(opt_attributes, value, testName, epsilon) {
          return Math.abs(value.x - opt_attributes.x) <= testName / 2 && Math.abs(value.y - opt_attributes.y) <= epsilon / 2;
        }
      },
      triangle : {
        /**
         * @param {?} adj
         * @param {?} lab
         * @param {?} event
         * @param {?} type
         * @return {undefined}
         */
        render : function(adj, lab, event, type) {
          var context = type.getCtx();
          var left = lab.x;
          /** @type {number} */
          var fromY = lab.y - event;
          /** @type {number} */
          var vLine2 = left - event;
          var hly = lab.y + event;
          var centerX = left + event;
          var gy = hly;
          context.beginPath();
          context.moveTo(left, fromY);
          context.lineTo(vLine2, hly);
          context.lineTo(centerX, gy);
          context.closePath();
          context[adj]();
        },
        /**
         * @param {?} opt_attributes
         * @param {?} value
         * @param {?} testName
         * @return {?}
         */
        contains : function(opt_attributes, value, testName) {
          return self.circle.contains(opt_attributes, value, testName);
        }
      },
      star : {
        /**
         * @param {?} adj
         * @param {?} lab
         * @param {?} event
         * @param {?} type
         * @return {undefined}
         */
        render : function(adj, lab, event, type) {
          var ctx = type.getCtx();
          /** @type {number} */
          var thetap = Math.PI / 5;
          ctx.save();
          ctx.translate(lab.x, lab.y);
          ctx.beginPath();
          ctx.moveTo(event, 0);
          /** @type {number} */
          var y = 0;
          for (;y < 9;y++) {
            ctx.rotate(thetap);
            if (y % 2 == 0) {
              ctx.lineTo(event / 0.525731 * 0.200811, 0);
            } else {
              ctx.lineTo(event, 0);
            }
          }
          ctx.closePath();
          ctx[adj]();
          ctx.restore();
        },
        /**
         * @param {?} opt_attributes
         * @param {?} value
         * @param {?} testName
         * @return {?}
         */
        contains : function(opt_attributes, value, testName) {
          return self.circle.contains(opt_attributes, value, testName);
        }
      }
    };
    var element = {
      line : {
        /**
         * @param {?} adj
         * @param {?} lab
         * @param {?} event
         * @return {undefined}
         */
        render : function(adj, lab, event) {
          var ctx = event.getCtx();
          ctx.beginPath();
          ctx.moveTo(adj.x, adj.y);
          ctx.lineTo(lab.x, lab.y);
          ctx.stroke();
        },
        /**
         * @param {?} opt_attributes
         * @param {?} value
         * @param {?} testName
         * @param {?} epsilon
         * @return {?}
         */
        contains : function(opt_attributes, value, testName, epsilon) {
          /** @type {function (...[*]): number} */
          var min = Math.min;
          /** @type {function (...[*]): number} */
          var max = Math.max;
          /** @type {number} */
          var minX = min(opt_attributes.x, value.x);
          /** @type {number} */
          var x = max(opt_attributes.x, value.x);
          /** @type {number} */
          var minY = min(opt_attributes.y, value.y);
          /** @type {number} */
          var maxY = max(opt_attributes.y, value.y);
          if (testName.x >= minX && (testName.x <= x && (testName.y >= minY && testName.y <= maxY))) {
            if (Math.abs(value.x - opt_attributes.x) <= epsilon) {
              return true;
            }
            var newTop = (value.y - opt_attributes.y) / (value.x - opt_attributes.x) * (testName.x - opt_attributes.x) + opt_attributes.y;
            return Math.abs(newTop - testName.y) <= epsilon;
          }
          return false;
        }
      },
      arrow : {
        /**
         * @param {?} adj
         * @param {?} lab
         * @param {?} event
         * @param {?} type
         * @param {?} keepData
         * @return {undefined}
         */
        render : function(adj, lab, event, type, keepData) {
          var ctx = keepData.getCtx();
          if (type) {
            var fx = adj;
            adj = lab;
            lab = fx;
          }
          var p = new Vector(lab.x - adj.x, lab.y - adj.y);
          p.$scale(event / p.norm());
          var collection = new Vector(lab.x - p.x, lab.y - p.y);
          var to = new Vector(-p.y / 2, p.x / 2);
          var d = collection.add(to);
          var p4coord = collection.$add(to.$scale(-1));
          ctx.beginPath();
          ctx.moveTo(adj.x, adj.y);
          ctx.lineTo(lab.x, lab.y);
          ctx.stroke();
          ctx.beginPath();
          ctx.moveTo(d.x, d.y);
          ctx.lineTo(p4coord.x, p4coord.y);
          ctx.lineTo(lab.x, lab.y);
          ctx.closePath();
          ctx.fill();
        },
        /**
         * @param {?} opt_attributes
         * @param {?} value
         * @param {?} testName
         * @param {?} epsilon
         * @return {?}
         */
        contains : function(opt_attributes, value, testName, epsilon) {
          return element.line.contains(opt_attributes, value, testName, epsilon);
        }
      },
      hyperline : {
        /**
         * @param {?} adj
         * @param {?} lab
         * @param {?} event
         * @param {?} type
         * @return {undefined}
         */
        render : function(adj, lab, event, type) {
          /**
           * @param {?} p1
           * @param {?} p2
           * @return {?}
           */
          function computeArcThroughTwoPoints(p1, p2) {
            /** @type {number} */
            var YY_START = p1.x * p2.y - p1.y * p2.x;
            /** @type {number} */
            var YYSTATE = YY_START;
            var b3 = p1.squaredNorm();
            var b1 = p2.squaredNorm();
            if (YY_START == 0) {
              return{
                x : 0,
                y : 0,
                ratio : -1
              };
            }
            /** @type {number} */
            var z0 = (p1.y * b1 - p2.y * b3 + p1.y - p2.y) / YY_START;
            /** @type {number} */
            var z1 = (p2.x * b3 - p1.x * b1 + p2.x - p1.x) / YYSTATE;
            /** @type {number} */
            var moveX = -z0 / 2;
            /** @type {number} */
            var moveY = -z1 / 2;
            /** @type {number} */
            var squaredRatio = (z0 * z0 + z1 * z1) / 4 - 1;
            if (squaredRatio < 0) {
              return{
                x : 0,
                y : 0,
                ratio : -1
              };
            }
            /** @type {number} */
            var ratio = Math.sqrt(squaredRatio);
            var out = {
              x : moveX,
              y : moveY,
              ratio : ratio > 1E3 ? -1 : ratio,
              a : z0,
              b : z1
            };
            return out;
          }
          /**
           * @param {number} angleBegin
           * @param {number} angleEnd
           * @return {?}
           */
          function sense(angleBegin, angleEnd) {
            return angleBegin < angleEnd ? angleBegin + Math.PI > angleEnd ? false : true : angleEnd + Math.PI > angleBegin ? true : false;
          }
          var ctx = type.getCtx();
          var centerOfCircle = computeArcThroughTwoPoints(adj, lab);
          if (centerOfCircle.a > 1E3 || (centerOfCircle.b > 1E3 || centerOfCircle.ratio < 0)) {
            ctx.beginPath();
            ctx.moveTo(adj.x * event, adj.y * event);
            ctx.lineTo(lab.x * event, lab.y * event);
            ctx.stroke();
          } else {
            /** @type {number} */
            var angleBegin = Math.atan2(lab.y - centerOfCircle.y, lab.x - centerOfCircle.x);
            /** @type {number} */
            var angleEnd = Math.atan2(adj.y - centerOfCircle.y, adj.x - centerOfCircle.x);
            sense = sense(angleBegin, angleEnd);
            ctx.beginPath();
            ctx.arc(centerOfCircle.x * event, centerOfCircle.y * event, centerOfCircle.ratio * event, angleBegin, angleEnd, sense);
            ctx.stroke();
          }
        },
        contains : $.lambda(false)
      }
    };
    Graph.Plot = {
      /**
       * @param {Object} viz
       * @param {?} klass
       * @return {undefined}
       */
      initialize : function(viz, klass) {
        /** @type {Object} */
        this.viz = viz;
        this.config = viz.config;
        this.node = viz.config.Node;
        this.edge = viz.config.Edge;
        this.animation = new Animation;
        this.nodeTypes = new klass.Plot.NodeTypes;
        this.edgeTypes = new klass.Plot.EdgeTypes;
        this.labels = viz.labels;
      },
      nodeHelper : self,
      edgeHelper : element,
      Interpolator : {
        map : {
          border : "color",
          color : "color",
          width : "number",
          height : "number",
          dim : "number",
          alpha : "number",
          lineWidth : "number",
          angularWidth : "number",
          span : "number",
          valueArray : "array-number",
          dimArray : "array-number"
        },
        canvas : {
          globalAlpha : "number",
          fillStyle : "color",
          strokeStyle : "color",
          lineWidth : "number",
          shadowBlur : "number",
          shadowColor : "color",
          shadowOffsetX : "number",
          shadowOffsetY : "number",
          miterLimit : "number"
        },
        label : {
          size : "number",
          color : "color"
        },
        /**
         * @param {?} adj
         * @param {?} lab
         * @param {?} event
         * @return {?}
         */
        compute : function(adj, lab, event) {
          return adj + (lab - adj) * event;
        },
        /**
         * @param {Object} elem
         * @param {?} dataAndEvents
         * @param {?} delta
         * @param {Object} vector
         * @return {undefined}
         */
        moebius : function(elem, dataAndEvents, delta, vector) {
          var v = vector.scale(-delta);
          if (v.norm() < 1) {
            var x = v.x;
            var y = v.y;
            var pos = elem.startPos.getc().moebiusTransformation(v);
            elem.pos.setc(pos.x, pos.y);
            v.x = x;
            v.y = y;
          }
        },
        /**
         * @param {Object} n
         * @param {?} diff
         * @param {?} qualifier
         * @return {undefined}
         */
        linear : function(n, diff, qualifier) {
          var from = n.startPos.getc(true);
          var to = n.endPos.getc(true);
          n.pos.setc(this.compute(from.x, to.x, qualifier), this.compute(from.y, to.y, qualifier));
        },
        /**
         * @param {Object} elem
         * @param {?} r
         * @param {number} delta
         * @return {undefined}
         */
        polar : function(elem, r, delta) {
          var from = elem.startPos.getp(true);
          var to = elem.endPos.getp();
          var ans = to.interpolate(from, delta);
          elem.pos.setp(ans.theta, ans.rho);
        },
        /**
         * @param {Array} elem
         * @param {?} prop
         * @param {?} qualifier
         * @param {number} getter
         * @param {number} setter
         * @return {undefined}
         */
        number : function(elem, prop, qualifier, getter, setter) {
          var from = elem[getter](prop, "start");
          var lab = elem[getter](prop, "end");
          elem[setter](prop, this.compute(from, lab, qualifier));
        },
        /**
         * @param {Array} elem
         * @param {?} prop
         * @param {?} qualifier
         * @param {number} getter
         * @param {number} setter
         * @return {undefined}
         */
        color : function(elem, prop, qualifier, getter, setter) {
          var arr = $.hexToRgb(elem[getter](prop, "start"));
          var to = $.hexToRgb(elem[getter](prop, "end"));
          var comp = this.compute;
          var val = $.rgbToHex([parseInt(comp(arr[0], to[0], qualifier)), parseInt(comp(arr[1], to[1], qualifier)), parseInt(comp(arr[2], to[2], qualifier))]);
          elem[setter](prop, val);
        },
        /**
         * @param {Array} elem
         * @param {?} prop
         * @param {?} qualifier
         * @param {number} getter
         * @param {number} setter
         * @return {undefined}
         */
        "array-number" : function(elem, prop, qualifier, getter, setter) {
          var v = elem[getter](prop, "start");
          var parts = elem[getter](prop, "end");
          /** @type {Array} */
          var cur = [];
          /** @type {number} */
          var id = 0;
          var pad = v.length;
          for (;id < pad;id++) {
            var from = v[id];
            var lab = parts[id];
            if (from.length) {
              /** @type {number} */
              var i = 0;
              var len = from.length;
              /** @type {Array} */
              var curi = [];
              for (;i < len;i++) {
                curi.push(this.compute(from[i], lab[i], qualifier));
              }
              cur.push(curi);
            } else {
              cur.push(this.compute(from, lab, qualifier));
            }
          }
          elem[setter](prop, cur);
        },
        /**
         * @param {?} opt_attributes
         * @param {?} value
         * @param {?} testName
         * @param {?} epsilon
         * @param {?} node
         * @param {?} namespaces
         * @return {undefined}
         */
        node : function(opt_attributes, value, testName, epsilon, node, namespaces) {
          epsilon = this[epsilon];
          if (value) {
            var len = value.length;
            /** @type {number} */
            var status = 0;
            for (;status < len;status++) {
              var msg = value[status];
              this[epsilon[msg]](opt_attributes, msg, testName, node, namespaces);
            }
          } else {
            for (msg in epsilon) {
              this[epsilon[msg]](opt_attributes, msg, testName, node, namespaces);
            }
          }
        },
        /**
         * @param {?} opt_attributes
         * @param {?} value
         * @param {?} testName
         * @param {?} epsilon
         * @param {?} getter
         * @param {?} setter
         * @return {undefined}
         */
        edge : function(opt_attributes, value, testName, epsilon, getter, setter) {
          var adjs = opt_attributes.adjacencies;
          var id;
          for (id in adjs) {
            this["node"](adjs[id], value, testName, epsilon, getter, setter);
          }
        },
        /**
         * @param {?} elem
         * @param {?} props
         * @param {?} delta
         * @return {undefined}
         */
        "node-property" : function(elem, props, delta) {
          this["node"](elem, props, delta, "map", "getData", "setData");
        },
        /**
         * @param {?} elem
         * @param {?} props
         * @param {?} delta
         * @return {undefined}
         */
        "edge-property" : function(elem, props, delta) {
          this["edge"](elem, props, delta, "map", "getData", "setData");
        },
        /**
         * @param {?} elem
         * @param {?} props
         * @param {?} delta
         * @return {undefined}
         */
        "label-property" : function(elem, props, delta) {
          this["node"](elem, props, delta, "label", "getLabelData", "setLabelData");
        },
        /**
         * @param {?} elem
         * @param {?} props
         * @param {?} delta
         * @return {undefined}
         */
        "node-style" : function(elem, props, delta) {
          this["node"](elem, props, delta, "canvas", "getCanvasStyle", "setCanvasStyle");
        },
        /**
         * @param {?} elem
         * @param {?} props
         * @param {?} delta
         * @return {undefined}
         */
        "edge-style" : function(elem, props, delta) {
          this["edge"](elem, props, delta, "canvas", "getCanvasStyle", "setCanvasStyle");
        }
      },
      /**
       * @param {Object} options
       * @return {undefined}
       */
      sequence : function(options) {
        var that = this;
        options = $.merge({
          condition : $.lambda(false),
          /** @type {function (): undefined} */
          step : $.empty,
          /** @type {function (): undefined} */
          onComplete : $.empty,
          duration : 200
        }, options || {});
        /** @type {number} */
        var poll = setInterval(function() {
          if (options.condition()) {
            options.step();
          } else {
            clearInterval(poll);
            options.onComplete();
          }
          that.viz.refresh(true);
        }, options.duration);
      },
      /**
       * @param {Object} obj
       * @return {?}
       */
      prepare : function(obj) {
        var graph = this.viz.graph;
        var accessors = {
          "node-property" : {
            getter : "getData",
            setter : "setData"
          },
          "edge-property" : {
            getter : "getData",
            setter : "setData"
          },
          "node-style" : {
            getter : "getCanvasStyle",
            setter : "setCanvasStyle"
          },
          "edge-style" : {
            getter : "getCanvasStyle",
            setter : "setCanvasStyle"
          }
        };
        var self = {};
        if ($.type(obj) == "array") {
          /** @type {number} */
          var i = 0;
          var l = obj.length;
          for (;i < l;i++) {
            var e = obj[i].split(":");
            self[e.shift()] = e;
          }
        } else {
          var p;
          for (p in obj) {
            if (p == "position") {
              /** @type {Array} */
              self[obj.position] = [];
            } else {
              self[p] = $.splat(obj[p]);
            }
          }
        }
        graph.eachNode(function(node) {
          node.startPos.set(node.pos);
          $.each(["node-property", "node-style"], function(name) {
            if (name in self) {
              var events = self[name];
              /** @type {number} */
              var i = 0;
              var l = events.length;
              for (;i < l;i++) {
                node[accessors[name].setter](events[i], node[accessors[name].getter](events[i]), "start");
              }
            }
          });
          $.each(["edge-property", "edge-style"], function(name) {
            if (name in self) {
              var events = self[name];
              node.eachAdjacency(function(adj) {
                /** @type {number} */
                var i = 0;
                var l = events.length;
                for (;i < l;i++) {
                  adj[accessors[name].setter](events[i], adj[accessors[name].getter](events[i]), "start");
                }
              });
            }
          });
        });
        return self;
      },
      /**
       * @param {?} opt
       * @param {?} versor
       * @return {undefined}
       */
      animate : function(opt, versor) {
        opt = $.merge(this.viz.config, opt || {});
        var that = this;
        var viz = this.viz;
        var graph = viz.graph;
        var interp = this.Interpolator;
        var animation = opt.type === "nodefx" ? this.nodeFxAnimation : this.animation;
        var m = this.prepare(opt.modes);
        if (opt.hideLabels) {
          this.labels.hideLabels(true);
        }
        animation.setOptions($.extend(opt, {
          $animating : false,
          /**
           * @param {?} adj
           * @return {undefined}
           */
          compute : function(adj) {
            graph.eachNode(function(node) {
              var p;
              for (p in m) {
                interp[p](node, m[p], adj, versor);
              }
            });
            that.plot(opt, this.$animating, adj);
            /** @type {boolean} */
            this.$animating = true;
          },
          /**
           * @return {undefined}
           */
          complete : function() {
            if (opt.hideLabels) {
              that.labels.hideLabels(false);
            }
            that.plot(opt);
            opt.onComplete();
          }
        })).start();
      },
      /**
       * @param {Object} opt
       * @return {undefined}
       */
      nodeFx : function(opt) {
        var viz = this.viz;
        var graph = viz.graph;
        var animation = this.nodeFxAnimation;
        var nodes = $.merge(this.viz.config, {
          elements : {
            id : false,
            properties : {}
          },
          reposition : false
        });
        opt = $.merge(nodes, opt || {}, {
          /** @type {function (): undefined} */
          onBeforeCompute : $.empty,
          /** @type {function (): undefined} */
          onAfterCompute : $.empty
        });
        animation.stopTimer();
        var props = opt.elements.properties;
        if (!opt.elements.id) {
          graph.eachNode(function(n) {
            var prop;
            for (prop in props) {
              n.setData(prop, props[prop], "end");
            }
          });
        } else {
          var attributes = $.splat(opt.elements.id);
          $.each(attributes, function(node) {
            var n = graph.getNode(node);
            if (n) {
              var prop;
              for (prop in props) {
                n.setData(prop, props[prop], "end");
              }
            }
          });
        }
        /** @type {Array} */
        var assigns = [];
        var vvar;
        for (vvar in props) {
          assigns.push(vvar);
        }
        /** @type {Array} */
        var modes = ["node-property:" + assigns.join(":")];
        if (opt.reposition) {
          modes.push("linear");
          viz.compute("end");
        }
        this.animate($.merge(opt, {
          modes : modes,
          type : "nodefx"
        }));
      },
      /**
       * @param {?} opt
       * @param {boolean} animating
       * @return {undefined}
       */
      plot : function(opt, animating) {
        var viz = this.viz;
        var graph = viz.graph;
        var canvas = viz.canvas;
        var id = viz.root;
        var that = this;
        var F = canvas.getCtx();
        /** @type {function (...[*]): number} */
        var min = Math.min;
        opt = opt || this.viz.controller;
        if (opt.clearCanvas) {
          canvas.clear();
        }
        var n = graph.getNode(id);
        if (!n) {
          return;
        }
        /** @type {boolean} */
        var T = !!n.visited;
        graph.eachNode(function(from) {
          var nodeAlpha = from.getData("alpha");
          from.eachAdjacency(function(adj) {
            var nodeTo = adj.nodeTo;
            if (!!nodeTo.visited === T && (from.drawn && nodeTo.drawn)) {
              if (!animating) {
                opt.onBeforePlotLine(adj);
              }
              that.plotLine(adj, canvas, animating);
              if (!animating) {
                opt.onAfterPlotLine(adj);
              }
            }
          });
          if (from.drawn) {
            if (!animating) {
              opt.onBeforePlotNode(from);
            }
            that.plotNode(from, canvas, animating);
            if (!animating) {
              opt.onAfterPlotNode(from);
            }
          }
          if (!that.labelsHidden && opt.withLabels) {
            if (from.drawn && nodeAlpha >= 0.95) {
              that.labels.plotLabel(canvas, from, opt);
            } else {
              that.labels.hideLabel(from, false);
            }
          }
          /** @type {boolean} */
          from.visited = !T;
        });
      },
      /**
       * @param {?} npos
       * @param {?} opt
       * @param {boolean} animating
       * @return {undefined}
       */
      plotTree : function(npos, opt, animating) {
        var that = this;
        var viz = this.viz;
        var canvas = viz.canvas;
        var config = this.config;
        var D = canvas.getCtx();
        var nodeAlpha = npos.getData("alpha");
        npos.eachSubnode(function(elem) {
          if (opt.plotSubtree(npos, elem) && (elem.exist && elem.drawn)) {
            var from = npos.getAdjacency(elem.id);
            if (!animating) {
              opt.onBeforePlotLine(from);
            }
            that.plotLine(from, canvas, animating);
            if (!animating) {
              opt.onAfterPlotLine(from);
            }
            that.plotTree(elem, opt, animating);
          }
        });
        if (npos.drawn) {
          if (!animating) {
            opt.onBeforePlotNode(npos);
          }
          this.plotNode(npos, canvas, animating);
          if (!animating) {
            opt.onAfterPlotNode(npos);
          }
          if (!opt.hideLabels && (opt.withLabels && nodeAlpha >= 0.95)) {
            this.labels.plotLabel(canvas, npos, opt);
          } else {
            this.labels.hideLabel(npos, false);
          }
        } else {
          this.labels.hideLabel(npos, true);
        }
      },
      /**
       * @param {?} node
       * @param {?} canvas
       * @param {boolean} animating
       * @return {undefined}
       */
      plotNode : function(node, canvas, animating) {
        var f = node.getData("type");
        var ctxObj = this.node.CanvasStyles;
        if (f != "none") {
          var width = node.getData("lineWidth");
          var color = node.getData("color");
          var alpha = node.getData("alpha");
          var ctx = canvas.getCtx();
          ctx.save();
          ctx.lineWidth = width;
          ctx.fillStyle = ctx.strokeStyle = color;
          ctx.globalAlpha = alpha;
          var s;
          for (s in ctxObj) {
            ctx[s] = node.getCanvasStyle(s);
          }
          this.nodeTypes[f].render.call(this, node, canvas, animating);
          ctx.restore();
        }
      },
      /**
       * @param {?} adj
       * @param {?} canvas
       * @param {boolean} animating
       * @return {undefined}
       */
      plotLine : function(adj, canvas, animating) {
        var f = adj.getData("type");
        var ctxObj = this.edge.CanvasStyles;
        if (f != "none") {
          var width = adj.getData("lineWidth");
          var color = adj.getData("color");
          var ctx = canvas.getCtx();
          var nodeFrom = adj.nodeFrom;
          var nodeTo = adj.nodeTo;
          ctx.save();
          ctx.lineWidth = width;
          ctx.fillStyle = ctx.strokeStyle = color;
          /** @type {number} */
          ctx.globalAlpha = Math.min(nodeFrom.getData("alpha"), nodeTo.getData("alpha"), adj.getData("alpha"));
          var s;
          for (s in ctxObj) {
            ctx[s] = adj.getCanvasStyle(s);
          }
          this.edgeTypes[f].render.call(this, adj, canvas, animating);
          ctx.restore();
        }
      }
    };
    Graph.Plot3D = $.merge(Graph.Plot, {
      Interpolator : {
        /**
         * @param {Object} n
         * @param {?} diff
         * @param {?} qualifier
         * @return {undefined}
         */
        linear : function(n, diff, qualifier) {
          var from = n.startPos.getc(true);
          var to = n.endPos.getc(true);
          n.pos.setc(this.compute(from.x, to.x, qualifier), this.compute(from.y, to.y, qualifier), this.compute(from.z, to.z, qualifier));
        }
      },
      /**
       * @param {Object} node
       * @param {Object} canvas
       * @return {undefined}
       */
      plotNode : function(node, canvas) {
        if (node.getData("type") == "none") {
          return;
        }
        this.plotElement(node, canvas, {
          /**
           * @return {?}
           */
          getAlpha : function() {
            return node.getData("alpha");
          }
        });
      },
      /**
       * @param {Object} adj
       * @param {Object} canvas
       * @return {undefined}
       */
      plotLine : function(adj, canvas) {
        if (adj.getData("type") == "none") {
          return;
        }
        this.plotElement(adj, canvas, {
          /**
           * @return {?}
           */
          getAlpha : function() {
            return Math.min(adj.nodeFrom.getData("alpha"), adj.nodeTo.getData("alpha"), adj.getData("alpha"));
          }
        });
      },
      /**
       * @param {Object} elem
       * @param {Object} canvas
       * @param {?} opt_attributes
       * @return {undefined}
       */
      plotElement : function(elem, canvas, opt_attributes) {
        var gl = canvas.getCtx();
        var viewMatrix = new Matrix4;
        var lighting = canvas.config.Scene.Lighting;
        var wcanvas = canvas.canvases[0];
        var program = wcanvas.program;
        var camera = wcanvas.camera;
        if (!elem.geometry) {
          elem.geometry = new (O3D[elem.getData("type")]);
        }
        elem.geometry.update(elem);
        if (!elem.webGLVertexBuffer) {
          /** @type {Array} */
          var normals = [];
          /** @type {Array} */
          var faces = [];
          /** @type {Array} */
          var positions = [];
          /** @type {number} */
          var vertexIndex = 0;
          var geom = elem.geometry;
          /** @type {number} */
          var i = 0;
          var vs = geom.vertices;
          var fs = geom.faces;
          var len = fs.length;
          for (;i < len;i++) {
            var face = fs[i];
            var normal = vs[face.a];
            var n = vs[face.b];
            var v1 = vs[face.c];
            var p0 = face.d ? vs[face.d] : false;
            var position = face.normal;
            normals.push(normal.x, normal.y, normal.z);
            normals.push(n.x, n.y, n.z);
            normals.push(v1.x, v1.y, v1.z);
            if (p0) {
              normals.push(p0.x, p0.y, p0.z);
            }
            positions.push(position.x, position.y, position.z);
            positions.push(position.x, position.y, position.z);
            positions.push(position.x, position.y, position.z);
            if (p0) {
              positions.push(position.x, position.y, position.z);
            }
            faces.push(vertexIndex, vertexIndex + 1, vertexIndex + 2);
            if (p0) {
              faces.push(vertexIndex, vertexIndex + 2, vertexIndex + 3);
              vertexIndex += 4;
            } else {
              vertexIndex += 3;
            }
          }
          elem.webGLVertexBuffer = gl.createBuffer();
          gl.bindBuffer(gl.ARRAY_BUFFER, elem.webGLVertexBuffer);
          gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(normals), gl.STATIC_DRAW);
          elem.webGLFaceBuffer = gl.createBuffer();
          gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elem.webGLFaceBuffer);
          gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(faces), gl.STATIC_DRAW);
          /** @type {number} */
          elem.webGLFaceCount = faces.length;
          elem.webGLNormalBuffer = gl.createBuffer();
          gl.bindBuffer(gl.ARRAY_BUFFER, elem.webGLNormalBuffer);
          gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
        }
        viewMatrix.multiply(camera.matrix, elem.geometry.matrix);
        gl.uniformMatrix4fv(program.viewMatrix, false, viewMatrix.flatten());
        gl.uniformMatrix4fv(program.projectionMatrix, false, camera.projectionMatrix.flatten());
        var normalMatrix = Matrix4.makeInvert(viewMatrix);
        normalMatrix.$transpose();
        gl.uniformMatrix4fv(program.normalMatrix, false, normalMatrix.flatten());
        var color = $.hexToRgb(elem.getData("color"));
        color.push(opt_attributes.getAlpha());
        gl.uniform4f(program.color, color[0] / 255, color[1] / 255, color[2] / 255, color[3]);
        gl.uniform1i(program.enableLighting, lighting.enable);
        if (lighting.enable) {
          if (lighting.ambient) {
            var acolor = lighting.ambient;
            gl.uniform3f(program.ambientColor, acolor[0], acolor[1], acolor[2]);
          }
          if (lighting.directional) {
            var dir = lighting.directional;
            color = dir.color;
            var pos = dir.direction;
            var vd = (new Vector3(pos.x, pos.y, pos.z)).normalize().$scale(-1);
            gl.uniform3f(program.lightingDirection, vd.x, vd.y, vd.z);
            gl.uniform3f(program.directionalColor, color[0], color[1], color[2]);
          }
        }
        gl.bindBuffer(gl.ARRAY_BUFFER, elem.webGLVertexBuffer);
        gl.vertexAttribPointer(program.position, 3, gl.FLOAT, false, 0, 0);
        gl.bindBuffer(gl.ARRAY_BUFFER, elem.webGLNormalBuffer);
        gl.vertexAttribPointer(program.normal, 3, gl.FLOAT, false, 0, 0);
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elem.webGLFaceBuffer);
        gl.drawElements(gl.TRIANGLES, elem.webGLFaceCount, gl.UNSIGNED_SHORT, 0);
      }
    });
    Graph.Label = {};
    Graph.Label.Native = new Class({
      /**
       * @param {?} viz
       * @return {undefined}
       */
      initialize : function(viz) {
        this.viz = viz;
      },
      /**
       * @param {?} canvas
       * @param {Object} node
       * @param {?} opt
       * @return {undefined}
       */
      plotLabel : function(canvas, node, opt) {
        var ctx = canvas.getCtx();
        var A = node.pos.getc(true);
        ctx.font = node.getLabelData("style") + " " + node.getLabelData("size") + "px " + node.getLabelData("family");
        ctx.textAlign = node.getLabelData("textAlign");
        ctx.fillStyle = ctx.strokeStyle = node.getLabelData("color");
        ctx.textBaseline = node.getLabelData("textBaseline");
        this.renderLabel(canvas, node, opt);
      },
      /**
       * @param {?} canvas
       * @param {Object} node
       * @param {?} opt
       * @return {undefined}
       */
      renderLabel : function(canvas, node, opt) {
        var ctx = canvas.getCtx();
        var pt = node.pos.getc(true);
        ctx.fillText(node.name, pt.x, pt.y + node.getData("height") / 2);
      },
      /** @type {function (): undefined} */
      hideLabel : $.empty,
      /** @type {function (): undefined} */
      hideLabels : $.empty
    });
    Graph.Label.DOM = new Class({
      labelsHidden : false,
      labelContainer : false,
      labels : {},
      /**
       * @return {?}
       */
      getLabelContainer : function() {
        return this.labelContainer ? this.labelContainer : this.labelContainer = document.getElementById(this.viz.config.labelContainer);
      },
      /**
       * @param {?} id
       * @return {?}
       */
      getLabel : function(id) {
        return id in this.labels && this.labels[id] != null ? this.labels[id] : this.labels[id] = document.getElementById(id);
      },
      /**
       * @param {?} adj
       * @return {undefined}
       */
      hideLabels : function(adj) {
        var container = this.getLabelContainer();
        if (adj) {
          /** @type {string} */
          container.style.display = "none";
        } else {
          /** @type {string} */
          container.style.display = "";
        }
        this.labelsHidden = adj;
      },
      /**
       * @param {boolean} dataAndEvents
       * @return {undefined}
       */
      clearLabels : function(dataAndEvents) {
        var id;
        for (id in this.labels) {
          if (dataAndEvents || !this.viz.graph.hasNode(id)) {
            this.disposeLabel(id);
            delete this.labels[id];
          }
        }
      },
      /**
       * @param {string} id
       * @return {undefined}
       */
      disposeLabel : function(id) {
        var elem = this.getLabel(id);
        if (elem && elem.parentNode) {
          elem.parentNode.removeChild(elem);
        }
      },
      /**
       * @param {?} adj
       * @param {?} lab
       * @return {undefined}
       */
      hideLabel : function(adj, lab) {
        adj = $.splat(adj);
        /** @type {string} */
        var disp = lab ? "" : "none";
        var y;
        var that = this;
        $.each(adj, function(n) {
          var testElement = that.getLabel(n.id);
          if (testElement) {
            /** @type {string} */
            testElement.style.display = disp;
          }
        });
      },
      /**
       * @param {Object} pos
       * @param {?} canvas
       * @return {?}
       */
      fitsInCanvas : function(pos, canvas) {
        var size = canvas.getSize();
        if (pos.x >= size.width || (pos.x < 0 || (pos.y >= size.height || pos.y < 0))) {
          return false;
        }
        return true;
      }
    });
    Graph.Label.HTML = new Class({
      Implements : Graph.Label.DOM,
      /**
       * @param {?} canvas
       * @param {?} lab
       * @param {?} pending
       * @return {undefined}
       */
      plotLabel : function(canvas, lab, pending) {
        var id = lab.id;
        var from = this.getLabel(id);
        if (!from && !(from = document.getElementById(id))) {
          /** @type {Element} */
          from = document.createElement("div");
          var container = this.getLabelContainer();
          from.id = id;
          /** @type {string} */
          from.className = "node";
          /** @type {string} */
          from.style.position = "absolute";
          pending.onCreateLabel(from, lab);
          container.appendChild(from);
          /** @type {Element} */
          this.labels[lab.id] = from;
        }
        this.placeLabel(from, lab, pending);
      }
    });
    Graph.Label.SVG = new Class({
      Implements : Graph.Label.DOM,
      /**
       * @param {?} canvas
       * @param {?} lab
       * @param {?} pending
       * @return {undefined}
       */
      plotLabel : function(canvas, lab, pending) {
        var id = lab.id;
        var from = this.getLabel(id);
        if (!from && !(from = document.getElementById(id))) {
          /** @type {string} */
          var ns = "http://www.w3.org/2000/svg";
          /** @type {Element} */
          from = document.createElementNS(ns, "svg:text");
          /** @type {Element} */
          var p = document.createElementNS(ns, "svg:tspan");
          from.appendChild(p);
          var container = this.getLabelContainer();
          from.setAttribute("id", id);
          from.setAttribute("class", "node");
          container.appendChild(from);
          pending.onCreateLabel(from, lab);
          /** @type {Element} */
          this.labels[lab.id] = from;
        }
        this.placeLabel(from, lab, pending);
      }
    });
    Graph.Geom = new Class({
      /**
       * @param {?} viz
       * @return {undefined}
       */
      initialize : function(viz) {
        this.viz = viz;
        this.config = viz.config;
        this.node = viz.config.Node;
        this.edge = viz.config.Edge;
      },
      /**
       * @param {number} x
       * @param {?} attributes
       * @return {undefined}
       */
      translate : function(x, attributes) {
        attributes = $.splat(attributes);
        this.viz.graph.eachNode(function(obj) {
          $.each(attributes, function(prop) {
            obj.getPos(prop).$add(x);
          });
        });
      },
      /**
       * @param {?} node
       * @param {?} canvas
       * @param {Object} callback
       * @return {undefined}
       */
      setRightLevelToShow : function(node, canvas, callback) {
        var level = this.getRightLevelToShow(node, canvas);
        var fx = this.viz.labels;
        var opt = $.merge({
          execShow : true,
          execHide : true,
          /** @type {function (): undefined} */
          onHide : $.empty,
          /** @type {function (): undefined} */
          onShow : $.empty
        }, callback || {});
        node.eachLevel(0, this.config.levelsToShow, function(from) {
          /** @type {number} */
          var flowLevel = from._depth - node._depth;
          if (flowLevel > level) {
            opt.onHide(from);
            if (opt.execHide) {
              /** @type {boolean} */
              from.drawn = false;
              /** @type {boolean} */
              from.exist = false;
              fx.hideLabel(from, false);
            }
          } else {
            opt.onShow(from);
            if (opt.execShow) {
              /** @type {boolean} */
              from.exist = true;
            }
          }
        });
        /** @type {boolean} */
        node.drawn = true;
      },
      /**
       * @param {Object} node
       * @param {?} canvas
       * @return {?}
       */
      getRightLevelToShow : function(node, canvas) {
        var config = this.config;
        var level = config.levelsToShow;
        var constrained = config.constrained;
        if (!constrained) {
          return level;
        }
        for (;!this.treeFitsInCanvas(node, canvas, level) && level > 1;) {
          level--;
        }
        return level;
      }
    });
    var valid = {
      /**
       * @param {?} json
       * @return {?}
       */
      construct : function(json) {
        /** @type {boolean} */
        var isArray = $.type(json) == "array";
        var ans = new Graph(this.graphOptions, this.config.Node, this.config.Edge, this.config.Label);
        if (!isArray) {
          (function(ans, json) {
            ans.addNode(json);
            if (json.children) {
              /** @type {number} */
              var i = 0;
              var ch = json.children;
              for (;i < ch.length;i++) {
                ans.addAdjacence(json, ch[i]);
                arguments.callee(ans, ch[i]);
              }
            }
          })(ans, json);
        } else {
          (function(ans, json) {
            /**
             * @param {string} key
             * @return {?}
             */
            var getNode = function(key) {
              /** @type {number} */
              var i = 0;
              var len = json.length;
              for (;i < len;i++) {
                if (json[i].id == key) {
                  return json[i];
                }
              }
              var newNode = {
                id : key,
                name : key
              };
              return ans.addNode(newNode);
            };
            /** @type {number} */
            var i = 0;
            var len = json.length;
            for (;i < len;i++) {
              ans.addNode(json[i]);
              var a = json[i].adjacencies;
              if (a) {
                /** @type {number} */
                var j = 0;
                var al = a.length;
                for (;j < al;j++) {
                  var node = a[j];
                  var pdataCur = {};
                  if (typeof a[j] != "string") {
                    pdataCur = $.merge(node.data, {});
                    node = node.nodeTo;
                  }
                  ans.addAdjacence(json[i], getNode(node), pdataCur);
                }
              }
            }
          })(ans, json);
        }
        return ans;
      },
      /**
       * @param {Object} json
       * @param {number} i
       * @return {undefined}
       */
      loadJSON : function(json, i) {
        /** @type {Object} */
        this.json = json;
        if (this.labels && this.labels.clearLabels) {
          this.labels.clearLabels(true);
        }
        this.graph = this.construct(json);
        if ($.type(json) != "array") {
          this.root = json.id;
        } else {
          this.root = json[i ? i : 0].id;
        }
      },
      /**
       * @param {string} type
       * @return {?}
       */
      toJSON : function(type) {
        type = type || "tree";
        if (type == "tree") {
          var ans = {};
          var rootNode = this.graph.getNode(this.root);
          ans = function recTree(node) {
            var ans = {};
            ans.id = node.id;
            ans.name = node.name;
            ans.data = node.data;
            /** @type {Array} */
            var ch = [];
            node.eachSubnode(function(n) {
              ch.push(recTree(n));
            });
            /** @type {Array} */
            ans.children = ch;
            return ans;
          }(rootNode);
          return ans;
        } else {
          /** @type {Array} */
          ans = [];
          /** @type {boolean} */
          var T = !!this.graph.getNode(this.root).visited;
          this.graph.eachNode(function(node) {
            var ansNode = {};
            ansNode.id = node.id;
            ansNode.name = node.name;
            ansNode.data = node.data;
            /** @type {Array} */
            var adjs = [];
            node.eachAdjacency(function(adj) {
              var nodeTo = adj.nodeTo;
              if (!!nodeTo.visited === T) {
                var ansAdj = {};
                ansAdj.nodeTo = nodeTo.id;
                ansAdj.data = adj.data;
                adjs.push(ansAdj);
              }
            });
            /** @type {Array} */
            ansNode.adjacencies = adjs;
            ans.push(ansNode);
            /** @type {boolean} */
            node.visited = !T;
          });
          return ans;
        }
      }
    };
    var Layout = $jit.Layouts = {};
    var column = {
      label : null,
      /**
       * @param {?} adj
       * @param {?} lab
       * @param {?} event
       * @return {undefined}
       */
      compute : function(adj, lab, event) {
        this.initializeLabel(event);
        var label = this.label;
        var style = label.style;
        adj.eachNode(function(n) {
          var autoWidth = n.getData("autoWidth");
          var autoHeight = n.getData("autoHeight");
          if (autoWidth || autoHeight) {
            delete n.data.$width;
            delete n.data.$height;
            delete n.data.$dim;
            var width = n.getData("width");
            var height = n.getData("height");
            /** @type {string} */
            style.width = autoWidth ? "auto" : width + "px";
            /** @type {string} */
            style.height = autoHeight ? "auto" : height + "px";
            label.innerHTML = n.name;
            var offsetWidth = label.offsetWidth;
            var offsetHeight = label.offsetHeight;
            var nodes = n.getData("type");
            if ($.indexOf(["circle", "square", "triangle", "star"], nodes) === -1) {
              n.setData("width", offsetWidth);
              n.setData("height", offsetHeight);
            } else {
              var dim = offsetWidth > offsetHeight ? offsetWidth : offsetHeight;
              n.setData("width", dim);
              n.setData("height", dim);
              n.setData("dim", dim);
            }
          }
        });
      },
      /**
       * @param {?} opt
       * @return {undefined}
       */
      initializeLabel : function(opt) {
        if (!this.label) {
          /** @type {Element} */
          this.label = document.createElement("div");
          document.body.appendChild(this.label);
        }
        this.setLabelStyles(opt);
      },
      /**
       * @param {?} opt
       * @return {undefined}
       */
      setLabelStyles : function(opt) {
        $.extend(this.label.style, {
          visibility : "hidden",
          position : "absolute",
          width : "auto",
          height : "auto"
        });
        /** @type {string} */
        this.label.className = "jit-autoadjust-label";
      }
    };
    Layout.Tree = function() {
      /**
       * @param {?} graph
       * @param {Node} config
       * @param {?} level
       * @param {string} orn
       * @param {Object} prop
       * @return {?}
       */
      function getBoundaries(graph, config, level, orn, prop) {
        var dim = config.Node;
        var multitree = config.multitree;
        if (dim.overridable) {
          /** @type {number} */
          var w = -1;
          /** @type {number} */
          var h = -1;
          graph.eachNode(function(n) {
            if (n._depth == level && (!multitree || "$orn" in n.data && n.data.$orn == orn)) {
              var dw = n.getData("width", prop);
              var dh = n.getData("height", prop);
              w = w < dw ? dw : w;
              h = h < dh ? dh : h;
            }
          });
          return{
            width : w < 0 ? dim.width : w,
            height : h < 0 ? dim.height : h
          };
        } else {
          return dim;
        }
      }
      /**
       * @param {?} node
       * @param {Object} prop
       * @param {?} val
       * @param {string} orn
       * @return {undefined}
       */
      function movetree(node, prop, val, orn) {
        /** @type {string} */
        var p = orn == "left" || orn == "right" ? "y" : "x";
        node.getPos(prop)[p] += val;
      }
      /**
       * @param {?} attributes
       * @param {?} ans
       * @return {?}
       */
      function moveextent(attributes, ans) {
        /** @type {Array} */
        var a = [];
        $.each(attributes, function(next_scope) {
          /** @type {Array.<?>} */
          next_scope = next_callback.call(next_scope);
          next_scope[0] += ans;
          next_scope[1] += ans;
          a.push(next_scope);
        });
        return a;
      }
      /**
       * @param {Array} ps
       * @param {Array} qs
       * @return {?}
       */
      function merge(ps, qs) {
        if (ps.length == 0) {
          return qs;
        }
        if (qs.length == 0) {
          return ps;
        }
        var J = ps.shift();
        var I = qs.shift();
        return[[J[0], I[1]]].concat(merge(ps, qs));
      }
      /**
       * @param {Array} ls
       * @param {Array} def
       * @return {?}
       */
      function mergelist(ls, def) {
        def = def || [];
        if (ls.length == 0) {
          return def;
        }
        var ps = ls.pop();
        return mergelist(ls, merge(ps, def));
      }
      /**
       * @param {Array} ext1
       * @param {Array} ext2
       * @param {?} subtreeOffset
       * @param {number} siblingOffset
       * @param {number} i
       * @return {?}
       */
      function fit(ext1, ext2, subtreeOffset, siblingOffset, i) {
        if (ext1.length <= i || ext2.length <= i) {
          return 0;
        }
        var p = ext1[i][1];
        var q = ext2[i][0];
        return Math.max(fit(ext1, ext2, subtreeOffset, siblingOffset, ++i) + subtreeOffset, p - q + siblingOffset);
      }
      /**
       * @param {?} es
       * @param {?} subtreeOffset
       * @param {number} siblingOffset
       * @return {?}
       */
      function fitlistl(es, subtreeOffset, siblingOffset) {
        /**
         * @param {Array} acc
         * @param {Array} es
         * @param {number} i
         * @return {?}
         */
        function $fitlistl(acc, es, i) {
          if (es.length <= i) {
            return[];
          }
          var e = es[i];
          var ans = fit(acc, e, subtreeOffset, siblingOffset, 0);
          return[ans].concat($fitlistl(merge(acc, moveextent(e, ans)), es, ++i));
        }
        return $fitlistl([], es, 0);
      }
      /**
       * @param {Array} es
       * @param {?} subtreeOffset
       * @param {number} siblingOffset
       * @return {?}
       */
      function fitlistr(es, subtreeOffset, siblingOffset) {
        /**
         * @param {?} acc
         * @param {Array} es
         * @param {number} i
         * @return {?}
         */
        function $fitlistr(acc, es, i) {
          if (es.length <= i) {
            return[];
          }
          var e = es[i];
          /** @type {number} */
          var ans = -fit(e, acc, subtreeOffset, siblingOffset, 0);
          return[ans].concat($fitlistr(merge(moveextent(e, ans), acc), es, ++i));
        }
        /** @type {Array.<?>} */
        es = next_callback.call(es);
        var ans = $fitlistr([], es.reverse(), 0);
        return ans.reverse();
      }
      /**
       * @param {?} es
       * @param {?} subtreeOffset
       * @param {number} siblingOffset
       * @param {string} align
       * @return {?}
       */
      function fitlist(es, subtreeOffset, siblingOffset, align) {
        var esl = fitlistl(es, subtreeOffset, siblingOffset);
        var esr = fitlistr(es, subtreeOffset, siblingOffset);
        if (align == "left") {
          esr = esl;
        } else {
          if (align == "right") {
            esl = esr;
          }
        }
        /** @type {number} */
        var i = 0;
        /** @type {Array} */
        var prevSources = [];
        for (;i < esl.length;i++) {
          /** @type {number} */
          prevSources[i] = (esl[i] + esr[i]) / 2;
        }
        return prevSources;
      }
      /**
       * @param {?} graph
       * @param {?} node
       * @param {Object} prop
       * @param {Element} config
       * @param {string} orn
       * @return {undefined}
       */
      function design(graph, node, prop, config, orn) {
        /**
         * @param {Object} node
         * @param {boolean} maxsize
         * @param {number} acum
         * @return {?}
         */
        function $design(node, maxsize, acum) {
          var r = node.getData(s, prop);
          var notsval = maxsize || node.getData(nots, prop);
          /** @type {Array} */
          var trees = [];
          /** @type {Array} */
          var extents = [];
          /** @type {boolean} */
          var chmaxsize = false;
          var chacum = notsval + config.levelDistance;
          node.eachSubnode(function(n) {
            if (n.exist && (!multitree || "$orn" in n.data && n.data.$orn == orn)) {
              if (!chmaxsize) {
                chmaxsize = getBoundaries(graph, config, n._depth, orn, prop);
              }
              var s = $design(n, chmaxsize[nots], acum + chacum);
              trees.push(s.tree);
              extents.push(s.extent);
            }
          });
          var positions = fitlist(extents, subtreeOffset, siblingOffset, align);
          /** @type {number} */
          var i = 0;
          /** @type {Array} */
          var ac = [];
          /** @type {Array} */
          var pextents = [];
          for (;i < trees.length;i++) {
            movetree(trees[i], prop, positions[i], orn);
            pextents.push(moveextent(extents[i], positions[i]));
          }
          /** @type {Array} */
          var extent = [[-r / 2, r / 2]].concat(mergelist(pextents));
          /** @type {number} */
          node.getPos(prop)[p] = 0;
          if (orn == "top" || orn == "left") {
            /** @type {number} */
            node.getPos(prop)[notp] = acum;
          } else {
            /** @type {number} */
            node.getPos(prop)[notp] = -acum;
          }
          return{
            tree : node,
            extent : extent
          };
        }
        var multitree = config.multitree;
        /** @type {Array} */
        var auxp = ["x", "y"];
        /** @type {Array} */
        var auxs = ["width", "height"];
        /** @type {number} */
        var ind = +(orn == "left" || orn == "right");
        var p = auxp[ind];
        var notp = auxp[1 - ind];
        var cnode = config.Node;
        var s = auxs[ind];
        var nots = auxs[1 - ind];
        var siblingOffset = config.siblingOffset;
        var subtreeOffset = config.subtreeOffset;
        var align = config.align;
        $design(node, false, 0);
      }
      /** @type {function (this:(Array.<T>|string|{length: number}), *=, *=): Array.<T>} */
      var next_callback = Array.prototype.slice;
      return new Class({
        /**
         * @param {number} adj
         * @param {?} type
         * @return {undefined}
         */
        compute : function(adj, type) {
          var lab = adj || "start";
          var node = this.graph.getNode(this.root);
          $.extend(node, {
            drawn : true,
            exist : true,
            selected : true
          });
          column.compute(this.graph, lab, this.config);
          if (!!type || !("_depth" in node)) {
            this.graph.computeLevels(this.root, 0, "ignore");
          }
          this.computePositions(node, lab);
        },
        /**
         * @param {?} node
         * @param {Object} prop
         * @return {undefined}
         */
        computePositions : function(node, prop) {
          var config = this.config;
          var multitree = config.multitree;
          var align = config.align;
          var indent = align !== "center" && config.indent;
          var orn = config.orientation;
          /** @type {Array} */
          var attributes = multitree ? ["top", "right", "bottom", "left"] : [orn];
          var that = this;
          $.each(attributes, function(orn) {
            design(that.graph, node, prop, that.config, orn, prop);
            var i = ["x", "y"][+(orn == "left" || orn == "right")];
            (function red(node) {
              node.eachSubnode(function(n) {
                if (n.exist && (!multitree || "$orn" in n.data && n.data.$orn == orn)) {
                  n.getPos(prop)[i] += node.getPos(prop)[i];
                  if (indent) {
                    n.getPos(prop)[i] += align == "left" ? indent : -indent;
                  }
                  red(n);
                }
              });
            })(node);
          });
        }
      });
    }();
    $jit.ST = function() {
      /**
       * @param {Object} node
       * @return {?}
       */
      function getNodesToHide(node) {
        node = node || this.clickedNode;
        if (!this.config.constrained) {
          return[];
        }
        var Geom = this.geom;
        var graph = this.graph;
        var canvas = this.canvas;
        var level = node._depth;
        /** @type {Array} */
        var matched = [];
        graph.eachNode(function(n) {
          if (n.exist && !n.selected) {
            if (n.isDescendantOf(node.id)) {
              if (n._depth <= level) {
                matched.push(n);
              }
            } else {
              matched.push(n);
            }
          }
        });
        var leafLevel = Geom.getRightLevelToShow(node, canvas);
        node.eachLevel(leafLevel, leafLevel, function(n) {
          if (n.exist && !n.selected) {
            matched.push(n);
          }
        });
        /** @type {number} */
        var i = 0;
        for (;i < nodesInPath.length;i++) {
          var n = this.graph.getNode(nodesInPath[i]);
          if (!n.isDescendantOf(node.id)) {
            matched.push(n);
          }
        }
        return matched;
      }
      /**
       * @param {(number|string)} node
       * @return {?}
       */
      function getNodesToShow(node) {
        /** @type {Array} */
        var matched = [];
        var config = this.config;
        node = node || this.clickedNode;
        this.clickedNode.eachLevel(0, config.levelsToShow, function(n) {
          if (config.multitree && (!("$orn" in n.data) && n.anySubnode(function(n) {
            return n.exist && !n.drawn;
          }))) {
            matched.push(n);
          } else {
            if (n.drawn && !n.anySubnode("drawn")) {
              matched.push(n);
            }
          }
        });
        return matched;
      }
      /** @type {Array} */
      var nodesInPath = [];
      return new Class({
        Implements : [valid, Extras, Layout.Tree],
        /**
         * @param {?} controller
         * @return {undefined}
         */
        initialize : function(controller) {
          var $ST = $jit.ST;
          var config = {
            levelsToShow : 2,
            levelDistance : 30,
            constrained : true,
            Node : {
              type : "rectangle"
            },
            duration : 700,
            offsetX : 0,
            offsetY : 0
          };
          this.controller = this.config = $.merge(Options("Canvas", "Fx", "Tree", "Node", "Edge", "Controller", "Tips", "NodeStyles", "Events", "Navigation", "Label"), config, controller);
          var canvasConfig = this.config;
          if (canvasConfig.useCanvas) {
            this.canvas = canvasConfig.useCanvas;
            /** @type {string} */
            this.config.labelContainer = this.canvas.id + "-label";
          } else {
            if (canvasConfig.background) {
              canvasConfig.background = $.merge({
                type : "Circles"
              }, canvasConfig.background);
            }
            this.canvas = new Canvas(this, canvasConfig);
            /** @type {string} */
            this.config.labelContainer = (typeof canvasConfig.injectInto == "string" ? canvasConfig.injectInto : canvasConfig.injectInto.id) + "-label";
          }
          this.graphOptions = {
            /** @type {function (number, (number|string)): undefined} */
            klass : Vector
          };
          this.graph = new Graph(this.graphOptions, this.config.Node, this.config.Edge);
          this.labels = new $ST.Label[canvasConfig.Label.type](this);
          this.fx = new $ST.Plot(this, $ST);
          this.op = new $ST.Op(this);
          this.group = new $ST.Group(this);
          this.geom = new $ST.Geom(this);
          /** @type {null} */
          this.clickedNode = null;
          this.initializeExtras();
        },
        /**
         * @return {undefined}
         */
        plot : function() {
          this.fx.plot(this.controller);
        },
        /**
         * @param {number} pos
         * @param {string} method
         * @param {?} lab
         * @return {undefined}
         */
        switchPosition : function(pos, method, lab) {
          var Geom = this.geom;
          var Plot = this.fx;
          var that = this;
          if (!Plot.busy) {
            /** @type {boolean} */
            Plot.busy = true;
            this.contract({
              /**
               * @return {undefined}
               */
              onComplete : function() {
                Geom.switchOrientation(pos);
                that.compute("end", false);
                /** @type {boolean} */
                Plot.busy = false;
                if (method == "animate") {
                  that.onClick(that.clickedNode.id, lab);
                } else {
                  if (method == "replot") {
                    that.select(that.clickedNode.id, lab);
                  }
                }
              }
            }, pos);
          }
        },
        /**
         * @param {number} align
         * @param {string} method
         * @param {?} lab
         * @return {undefined}
         */
        switchAlignment : function(align, method, lab) {
          /** @type {number} */
          this.config.align = align;
          if (method == "animate") {
            this.select(this.clickedNode.id, lab);
          } else {
            if (method == "replot") {
              this.onClick(this.clickedNode.id, lab);
            }
          }
        },
        /**
         * @param {?} id
         * @return {undefined}
         */
        addNodeInPath : function(id) {
          nodesInPath.push(id);
          this.select(this.clickedNode && this.clickedNode.id || this.root);
        },
        /**
         * @param {?} dataAndEvents
         * @return {undefined}
         */
        clearNodesInPath : function(dataAndEvents) {
          /** @type {number} */
          nodesInPath.length = 0;
          this.select(this.clickedNode && this.clickedNode.id || this.root);
        },
        /**
         * @return {undefined}
         */
        refresh : function() {
          this.reposition();
          this.select(this.clickedNode && this.clickedNode.id || this.root);
        },
        /**
         * @return {undefined}
         */
        reposition : function() {
          this.graph.computeLevels(this.root, 0, "ignore");
          this.geom.setRightLevelToShow(this.clickedNode, this.canvas);
          this.graph.eachNode(function(n) {
            if (n.exist) {
              /** @type {boolean} */
              n.drawn = true;
            }
          });
          this.compute("end");
        },
        /**
         * @param {Array} node
         * @param {?} onComplete
         * @return {undefined}
         */
        requestNodes : function(node, onComplete) {
          var handler = $.merge(this.controller, onComplete);
          var lev = this.config.levelsToShow;
          if (handler.request) {
            /** @type {Array} */
            var leaves = [];
            var d = node._depth;
            node.eachLevel(0, lev, function(n) {
              if (n.drawn && !n.anySubnode()) {
                leaves.push(n);
                /** @type {number} */
                n._level = lev - (n._depth - d);
              }
            });
            this.group.requestNodes(leaves, handler);
          } else {
            handler.onComplete();
          }
        },
        /**
         * @param {?} onComplete
         * @param {number} switched
         * @return {undefined}
         */
        contract : function(onComplete, switched) {
          var orn = this.config.orientation;
          var Geom = this.geom;
          var Group = this.group;
          if (switched) {
            Geom.switchOrientation(switched);
          }
          var nodes = getNodesToHide.call(this);
          if (switched) {
            Geom.switchOrientation(orn);
          }
          Group.contract(nodes, $.merge(this.controller, onComplete));
        },
        /**
         * @param {?} node
         * @param {?} onComplete
         * @return {undefined}
         */
        move : function(node, onComplete) {
          this.compute("end", false);
          var move = onComplete.Move;
          var offset = {
            x : move.offsetX,
            y : move.offsetY
          };
          if (move.enable) {
            this.geom.translate(node.endPos.add(offset).$scale(-1), "end");
          }
          this.fx.animate($.merge(this.controller, {
            modes : ["linear"]
          }, onComplete));
        },
        /**
         * @param {?} node
         * @param {?} onComplete
         * @return {undefined}
         */
        expand : function(node, onComplete) {
          var vvar = getNodesToShow.call(this, node);
          this.group.expand(vvar, $.merge(this.controller, onComplete));
        },
        /**
         * @param {?} node
         * @return {undefined}
         */
        selectPath : function(node) {
          /**
           * @param {Object} node
           * @return {undefined}
           */
          function path(node) {
            if (node == null || node.selected) {
              return;
            }
            /** @type {boolean} */
            node.selected = true;
            $.each(that.group.getSiblings([node])[node.id], function(n) {
              /** @type {boolean} */
              n.exist = true;
              /** @type {boolean} */
              n.drawn = true;
            });
            var indents = node.getParents();
            indents = indents.length > 0 ? indents[0] : null;
            path(indents);
          }
          var that = this;
          this.graph.eachNode(function(pane) {
            /** @type {boolean} */
            pane.selected = false;
          });
          /** @type {number} */
          var i = 0;
          /** @type {Array} */
          var codeSegments = [node.id].concat(nodesInPath);
          for (;i < codeSegments.length;i++) {
            path(this.graph.getNode(codeSegments[i]));
          }
        },
        /**
         * @param {?} from
         * @param {string} method
         * @param {Object} onComplete
         * @return {undefined}
         */
        setRoot : function(from, method, onComplete) {
          /**
           * @return {undefined}
           */
          function $setRoot() {
            if (this.config.multitree && clickedNode.data.$orn) {
              var orn = clickedNode.data.$orn;
              var opp = {
                left : "right",
                right : "left",
                top : "bottom",
                bottom : "top"
              }[orn];
              rootNode.data.$orn = opp;
              (function tag(node) {
                node.eachSubnode(function(n) {
                  if (n.id != from) {
                    n.data.$orn = opp;
                    tag(n);
                  }
                });
              })(rootNode);
              delete clickedNode.data.$orn;
            }
            this.root = from;
            this.clickedNode = clickedNode;
            this.graph.computeLevels(this.root, 0, "ignore");
            this.geom.setRightLevelToShow(clickedNode, canvas, {
              execHide : false,
              /**
               * @param {?} adj
               * @return {undefined}
               */
              onShow : function(adj) {
                if (!adj.drawn) {
                  /** @type {boolean} */
                  adj.drawn = true;
                  adj.setData("alpha", 1, "end");
                  adj.setData("alpha", 0);
                  adj.pos.setc(clickedNode.pos.x, clickedNode.pos.y);
                }
              }
            });
            this.compute("end");
            /** @type {boolean} */
            this.busy = true;
            this.fx.animate({
              modes : ["linear", "node-property:alpha"],
              /**
               * @return {undefined}
               */
              onComplete : function() {
                /** @type {boolean} */
                that.busy = false;
                that.onClick(from, {
                  /**
                   * @return {undefined}
                   */
                  onComplete : function() {
                    if (onComplete) {
                      onComplete.onComplete();
                    }
                  }
                });
              }
            });
          }
          if (this.busy) {
            return;
          }
          /** @type {boolean} */
          this.busy = true;
          var that = this;
          var canvas = this.canvas;
          var rootNode = this.graph.getNode(this.root);
          var clickedNode = this.graph.getNode(from);
          delete rootNode.data.$orns;
          if (method == "animate") {
            $setRoot.call(this);
            that.selectPath(clickedNode);
          } else {
            if (method == "replot") {
              $setRoot.call(this);
              this.select(this.root);
            }
          }
        },
        /**
         * @param {?} subtree
         * @param {string} method
         * @param {Object} onComplete
         * @return {undefined}
         */
        addSubtree : function(subtree, method, onComplete) {
          if (method == "replot") {
            this.op.sum(subtree, $.extend({
              type : "replot"
            }, onComplete || {}));
          } else {
            if (method == "animate") {
              this.op.sum(subtree, $.extend({
                type : "fade:seq"
              }, onComplete || {}));
            }
          }
        },
        /**
         * @param {?} id
         * @param {?} removeRoot
         * @param {string} method
         * @param {Object} onComplete
         * @return {undefined}
         */
        removeSubtree : function(id, removeRoot, method, onComplete) {
          var n = this.graph.getNode(id);
          /** @type {Array} */
          var selected = [];
          n.eachLevel(+!removeRoot, false, function(row) {
            selected.push(row.id);
          });
          if (method == "replot") {
            this.op.removeNode(selected, $.extend({
              type : "replot"
            }, onComplete || {}));
          } else {
            if (method == "animate") {
              this.op.removeNode(selected, $.extend({
                type : "fade:seq"
              }, onComplete || {}));
            }
          }
        },
        /**
         * @param {boolean} id
         * @param {boolean} lab
         * @return {undefined}
         */
        select : function(id, lab) {
          var group = this.group;
          var geom = this.geom;
          var from = this.graph.getNode(id);
          var canvas = this.canvas;
          var root = this.graph.getNode(this.root);
          var complete = $.merge(this.controller, lab);
          var that = this;
          complete.onBeforeCompute(from);
          this.selectPath(from);
          this.clickedNode = from;
          this.requestNodes(from, {
            /**
             * @return {undefined}
             */
            onComplete : function() {
              group.hide(group.prepare(getNodesToHide.call(that)), complete);
              geom.setRightLevelToShow(from, canvas);
              that.compute("current");
              that.graph.eachNode(function(node) {
                var pos = node.pos.getc(true);
                node.startPos.setc(pos.x, pos.y);
                node.endPos.setc(pos.x, pos.y);
                /** @type {boolean} */
                node.visited = false;
              });
              var offset = {
                x : complete.offsetX,
                y : complete.offsetY
              };
              that.geom.translate(from.endPos.add(offset).$scale(-1), ["start", "current", "end"]);
              group.show(getNodesToShow.call(that));
              that.plot();
              complete.onAfterCompute(that.clickedNode);
              complete.onComplete();
            }
          });
        },
        /**
         * @param {?} adj
         * @param {?} lab
         * @return {undefined}
         */
        onClick : function(adj, lab) {
          var canvas = this.canvas;
          var that = this;
          var Geom = this.geom;
          var config = this.config;
          var innerController = {
            Move : {
              enable : true,
              offsetX : config.offsetX || 0,
              offsetY : config.offsetY || 0
            },
            setRightLevelToShowConfig : false,
            /** @type {function (): undefined} */
            onBeforeRequest : $.empty,
            /** @type {function (): undefined} */
            onBeforeContract : $.empty,
            /** @type {function (): undefined} */
            onBeforeMove : $.empty,
            /** @type {function (): undefined} */
            onBeforeExpand : $.empty
          };
          var complete = $.merge(this.controller, innerController, lab);
          if (!this.busy) {
            /** @type {boolean} */
            this.busy = true;
            var from = this.graph.getNode(adj);
            this.selectPath(from, this.clickedNode);
            this.clickedNode = from;
            complete.onBeforeCompute(from);
            complete.onBeforeRequest(from);
            this.requestNodes(from, {
              /**
               * @return {undefined}
               */
              onComplete : function() {
                complete.onBeforeContract(from);
                that.contract({
                  /**
                   * @return {undefined}
                   */
                  onComplete : function() {
                    Geom.setRightLevelToShow(from, canvas, complete.setRightLevelToShowConfig);
                    complete.onBeforeMove(from);
                    that.move(from, {
                      Move : complete.Move,
                      /**
                       * @return {undefined}
                       */
                      onComplete : function() {
                        complete.onBeforeExpand(from);
                        that.expand(from, {
                          /**
                           * @return {undefined}
                           */
                          onComplete : function() {
                            /** @type {boolean} */
                            that.busy = false;
                            complete.onAfterCompute(adj);
                            complete.onComplete();
                          }
                        });
                      }
                    });
                  }
                });
              }
            });
          }
        }
      });
    }();
    /** @type {boolean} */
    $jit.ST.$extend = true;
    $jit.ST.Op = new Class({
      Implements : Graph.Op
    });
    $jit.ST.Group = new Class({
      /**
       * @param {Object} viz
       * @return {undefined}
       */
      initialize : function(viz) {
        /** @type {Object} */
        this.viz = viz;
        this.canvas = viz.canvas;
        this.config = viz.config;
        this.animation = new Animation;
        /** @type {null} */
        this.nodes = null;
      },
      /**
       * @param {Array} nodes
       * @param {?} controller
       * @return {undefined}
       */
      requestNodes : function(nodes, controller) {
        /** @type {number} */
        var counter = 0;
        var len = nodes.length;
        var nodeSelected = {};
        /**
         * @return {undefined}
         */
        var complete = function() {
          controller.onComplete();
        };
        var viz = this.viz;
        if (len == 0) {
          complete();
        }
        /** @type {number} */
        var i = 0;
        for (;i < len;i++) {
          nodeSelected[nodes[i].id] = nodes[i];
          controller.request(nodes[i].id, nodes[i]._level, {
            /**
             * @param {?} adj
             * @param {?} lab
             * @return {undefined}
             */
            onComplete : function(adj, lab) {
              if (lab && lab.children) {
                lab.id = adj;
                viz.op.sum(lab, {
                  type : "nothing"
                });
              }
              if (++counter == len) {
                viz.graph.computeLevels(viz.root, 0);
                complete();
              }
            }
          });
        }
      },
      /**
       * @param {Object} nodes
       * @param {Object} controller
       * @return {undefined}
       */
      contract : function(nodes, controller) {
        var viz = this.viz;
        var that = this;
        nodes = this.prepare(nodes);
        this.animation.setOptions($.merge(controller, {
          $animating : false,
          /**
           * @param {?} adj
           * @return {undefined}
           */
          compute : function(adj) {
            if (adj == 1) {
              /** @type {number} */
              adj = 0.99;
            }
            that.plotStep(1 - adj, controller, this.$animating);
            /** @type {string} */
            this.$animating = "contract";
          },
          /**
           * @return {undefined}
           */
          complete : function() {
            that.hide(nodes, controller);
          }
        })).start();
      },
      /**
       * @param {Object} nodes
       * @param {Object} controller
       * @return {undefined}
       */
      hide : function(nodes, controller) {
        var viz = this.viz;
        /** @type {number} */
        var i = 0;
        for (;i < nodes.length;i++) {
          if (true || (!controller || !controller.request)) {
            nodes[i].eachLevel(1, false, function(owner) {
              if (owner.exist) {
                $.extend(owner, {
                  drawn : false,
                  exist : false
                });
              }
            });
          } else {
            /** @type {Array} */
            var selected = [];
            nodes[i].eachLevel(1, false, function(row) {
              selected.push(row.id);
            });
            viz.op.removeNode(selected, {
              type : "nothing"
            });
            viz.labels.clearLabels();
          }
        }
        controller.onComplete();
      },
      /**
       * @param {?} name
       * @param {Object} controller
       * @return {undefined}
       */
      expand : function(name, controller) {
        var that = this;
        this.show(name);
        this.animation.setOptions($.merge(controller, {
          $animating : false,
          /**
           * @param {?} adj
           * @return {undefined}
           */
          compute : function(adj) {
            that.plotStep(adj, controller, this.$animating);
            /** @type {string} */
            this.$animating = "expand";
          },
          /**
           * @return {undefined}
           */
          complete : function() {
            that.plotStep(undefined, controller, false);
            controller.onComplete();
          }
        })).start();
      },
      /**
       * @param {?} attributes
       * @return {undefined}
       */
      show : function(attributes) {
        var config = this.config;
        this.prepare(attributes);
        $.each(attributes, function(n) {
          if (config.multitree && !("$orn" in n.data)) {
            delete n.data.$orns;
            /** @type {string} */
            var orns = " ";
            n.eachSubnode(function(n) {
              if ("$orn" in n.data && (orns.indexOf(n.data.$orn) < 0 && (n.exist && !n.drawn))) {
                orns += n.data.$orn + " ";
              }
            });
            n.data.$orns = orns;
          }
          n.eachLevel(0, config.levelsToShow, function(n) {
            if (n.exist) {
              /** @type {boolean} */
              n.drawn = true;
            }
          });
        });
      },
      /**
       * @param {?} nodes
       * @return {?}
       */
      prepare : function(nodes) {
        this.nodes = this.getNodesWithChildren(nodes);
        return this.nodes;
      },
      /**
       * @param {Array} nodes
       * @return {?}
       */
      getNodesWithChildren : function(nodes) {
        /** @type {Array} */
        var ans = [];
        var config = this.config;
        var root = this.viz.root;
        nodes.sort(function(a, b) {
          return(a._depth <= b._depth) - (a._depth >= b._depth);
        });
        /** @type {number} */
        var i = 0;
        for (;i < nodes.length;i++) {
          if (nodes[i].anySubnode("exist")) {
            /** @type {number} */
            var j = i + 1;
            /** @type {boolean} */
            var found = false;
            for (;!found && j < nodes.length;j++) {
              if (!config.multitree || "$orn" in nodes[j].data) {
                found = found || nodes[i].isDescendantOf(nodes[j].id);
              }
            }
            if (!found) {
              ans.push(nodes[i]);
            }
          }
        }
        return ans;
      },
      /**
       * @param {number} delta
       * @param {Object} controller
       * @param {boolean} animating
       * @return {undefined}
       */
      plotStep : function(delta, controller, animating) {
        var viz = this.viz;
        var config = this.config;
        var canvas = viz.canvas;
        var cctx = canvas.getCtx();
        var nodes = this.nodes;
        var i;
        var node;
        var nds = {};
        /** @type {number} */
        i = 0;
        for (;i < nodes.length;i++) {
          node = nodes[i];
          /** @type {Array} */
          nds[node.id] = [];
          var root = config.multitree && !("$orn" in node.data);
          var orns = root && node.data.$orns;
          node.eachSubgraph(function(n) {
            if (root && (orns && (orns.indexOf(n.data.$orn) > 0 && n.drawn))) {
              /** @type {boolean} */
              n.drawn = false;
              nds[node.id].push(n);
            } else {
              if ((!root || !orns) && n.drawn) {
                /** @type {boolean} */
                n.drawn = false;
                nds[node.id].push(n);
              }
            }
          });
          /** @type {boolean} */
          node.drawn = true;
        }
        if (nodes.length > 0) {
          viz.fx.plot();
        }
        for (i in nds) {
          $.each(nds[i], function(n) {
            /** @type {boolean} */
            n.drawn = true;
          });
        }
        /** @type {number} */
        i = 0;
        for (;i < nodes.length;i++) {
          node = nodes[i];
          cctx.save();
          viz.fx.plotSubtree(node, controller, delta, animating);
          cctx.restore();
        }
      },
      /**
       * @param {?} attributes
       * @return {?}
       */
      getSiblings : function(attributes) {
        var siblings = {};
        $.each(attributes, function(n) {
          var par = n.getParents();
          if (par.length == 0) {
            /** @type {Array} */
            siblings[n.id] = [n];
          } else {
            /** @type {Array} */
            var assigns = [];
            par[0].eachSubnode(function(vvar) {
              assigns.push(vvar);
            });
            /** @type {Array} */
            siblings[n.id] = assigns;
          }
        });
        return siblings;
      }
    });
    $jit.ST.Geom = new Class({
      Implements : Graph.Geom,
      /**
       * @param {number} orn
       * @return {undefined}
       */
      switchOrientation : function(orn) {
        /** @type {number} */
        this.config.orientation = orn;
      },
      /**
       * @return {?}
       */
      dispatch : function() {
        /** @type {Array.<?>} */
        var args = Array.prototype.slice.call(arguments);
        var s = args.shift();
        /** @type {number} */
        var args_length = args.length;
        /**
         * @param {?} a
         * @return {?}
         */
        var val = function(a) {
          return typeof a == "function" ? a() : a;
        };
        if (args_length == 2) {
          return s == "top" || s == "bottom" ? val(args[0]) : val(args[1]);
        } else {
          if (args_length == 4) {
            switch(s) {
              case "top":
                return val(args[0]);
              case "right":
                return val(args[1]);
              case "bottom":
                return val(args[2]);
              case "left":
                return val(args[3]);
            }
          }
        }
        return undefined;
      },
      /**
       * @param {Object} node
       * @param {boolean} dataAndEvents
       * @return {?}
       */
      getSize : function(node, dataAndEvents) {
        var data = node.data;
        var config = this.config;
        var siblingOffset = config.siblingOffset;
        var route = config.multitree && ("$orn" in data && data.$orn) || config.orientation;
        var h = node.getData("width") + siblingOffset;
        var w = node.getData("height") + siblingOffset;
        if (!dataAndEvents) {
          return this.dispatch(route, w, h);
        } else {
          return this.dispatch(route, h, w);
        }
      },
      /**
       * @param {?} node
       * @param {number} level
       * @param {Function} leaf
       * @return {?}
       */
      getTreeBaseSize : function(node, level, leaf) {
        var size = this.getSize(node, true);
        /** @type {number} */
        var baseHeight = 0;
        var that = this;
        if (leaf(level, node)) {
          return size;
        }
        if (level === 0) {
          return 0;
        }
        node.eachSubnode(function(elem) {
          baseHeight += that.getTreeBaseSize(elem, level - 1, leaf);
        });
        return(size > baseHeight ? size : baseHeight) + this.config.subtreeOffset;
      },
      /**
       * @param {?} node
       * @param {string} type
       * @param {?} s
       * @return {?}
       */
      getEdge : function(node, type, s) {
        /**
         * @param {number} mayParseLabeledStatementInstead
         * @param {number} recurring
         * @return {?}
         */
        var $C = function(mayParseLabeledStatementInstead, recurring) {
          return function() {
            return node.pos.add(new Vector(mayParseLabeledStatementInstead, recurring));
          };
        };
        var dim = this.node;
        var w = node.getData("width");
        var h = node.getData("height");
        if (type == "begin") {
          if (dim.align == "center") {
            return this.dispatch(s, $C(0, h / 2), $C(-w / 2, 0), $C(0, -h / 2), $C(w / 2, 0));
          } else {
            if (dim.align == "left") {
              return this.dispatch(s, $C(0, h), $C(0, 0), $C(0, 0), $C(w, 0));
            } else {
              if (dim.align == "right") {
                return this.dispatch(s, $C(0, 0), $C(-w, 0), $C(0, -h), $C(0, 0));
              } else {
                throw "align: not implemented";
              }
            }
          }
        } else {
          if (type == "end") {
            if (dim.align == "center") {
              return this.dispatch(s, $C(0, -h / 2), $C(w / 2, 0), $C(0, h / 2), $C(-w / 2, 0));
            } else {
              if (dim.align == "left") {
                return this.dispatch(s, $C(0, 0), $C(w, 0), $C(0, h), $C(0, 0));
              } else {
                if (dim.align == "right") {
                  return this.dispatch(s, $C(0, -h), $C(0, 0), $C(0, 0), $C(-w, 0));
                } else {
                  throw "align: not implemented";
                }
              }
            }
          }
        }
      },
      /**
       * @param {Object} node
       * @param {number} scale
       * @return {?}
       */
      getScaledTreePosition : function(node, scale) {
        var dim = this.node;
        var w = node.getData("width");
        var h = node.getData("height");
        var route = this.config.multitree && ("$orn" in node.data && node.data.$orn) || this.config.orientation;
        /**
         * @param {number} mayParseLabeledStatementInstead
         * @param {number} recurring
         * @return {?}
         */
        var $C = function(mayParseLabeledStatementInstead, recurring) {
          return function() {
            return node.pos.add(new Vector(mayParseLabeledStatementInstead, recurring)).$scale(1 - scale);
          };
        };
        if (dim.align == "left") {
          return this.dispatch(route, $C(0, h), $C(0, 0), $C(0, 0), $C(w, 0));
        } else {
          if (dim.align == "center") {
            return this.dispatch(route, $C(0, h / 2), $C(-w / 2, 0), $C(0, -h / 2), $C(w / 2, 0));
          } else {
            if (dim.align == "right") {
              return this.dispatch(route, $C(0, 0), $C(-w, 0), $C(0, -h), $C(0, 0));
            } else {
              throw "align: not implemented";
            }
          }
        }
      },
      /**
       * @param {Object} node
       * @param {?} canvas
       * @param {number} level
       * @return {?}
       */
      treeFitsInCanvas : function(node, canvas, level) {
        var csize = canvas.getSize();
        var s = this.config.multitree && ("$orn" in node.data && node.data.$orn) || this.config.orientation;
        var size = this.dispatch(s, csize.width, csize.height);
        var baseSize = this.getTreeBaseSize(node, level, function(deepDataAndEvents, dataAndEvents) {
          return deepDataAndEvents === 0 || !dataAndEvents.anySubnode();
        });
        return baseSize < size;
      }
    });
    $jit.ST.Plot = new Class({
      Implements : Graph.Plot,
      /**
       * @param {Object} node
       * @param {Object} opt
       * @param {number} scale
       * @param {boolean} animating
       * @return {undefined}
       */
      plotSubtree : function(node, opt, scale, animating) {
        var viz = this.viz;
        var canvas = viz.canvas;
        var config = viz.config;
        /** @type {number} */
        scale = Math.min(Math.max(0.001, scale), 1);
        if (scale >= 0) {
          /** @type {boolean} */
          node.drawn = false;
          var ctx = canvas.getCtx();
          var diff = viz.geom.getScaledTreePosition(node, scale);
          ctx.translate(diff.x, diff.y);
          ctx.scale(scale, scale);
        }
        this.plotTree(node, $.merge(opt, {
          withLabels : true,
          hideLabels : !!scale,
          /**
           * @param {?} dataAndEvents
           * @param {Object} x
           * @return {?}
           */
          plotSubtree : function(dataAndEvents, x) {
            var root = config.multitree && !("$orn" in node.data);
            var orns = root && node.getData("orns");
            return!root || orns.indexOf(node.getData("orn")) > -1;
          }
        }), animating);
        if (scale >= 0) {
          /** @type {boolean} */
          node.drawn = true;
        }
      },
      /**
       * @param {?} pos
       * @param {number} width
       * @param {number} height
       * @return {?}
       */
      getAlignedPos : function(pos, width, height) {
        var dim = this.node;
        var square;
        var orn;
        if (dim.align == "center") {
          square = {
            x : pos.x - width / 2,
            y : pos.y - height / 2
          };
        } else {
          if (dim.align == "left") {
            orn = this.config.orientation;
            if (orn == "bottom" || orn == "top") {
              square = {
                x : pos.x - width / 2,
                y : pos.y
              };
            } else {
              square = {
                x : pos.x,
                y : pos.y - height / 2
              };
            }
          } else {
            if (dim.align == "right") {
              orn = this.config.orientation;
              if (orn == "bottom" || orn == "top") {
                square = {
                  x : pos.x - width / 2,
                  y : pos.y - height
                };
              } else {
                square = {
                  x : pos.x - width,
                  y : pos.y - height / 2
                };
              }
            } else {
              throw "align: not implemented";
            }
          }
        }
        return square;
      },
      /**
       * @param {?} adj
       * @return {?}
       */
      getOrientation : function(adj) {
        var config = this.config;
        var orn = config.orientation;
        if (config.multitree) {
          var nodeFrom = adj.nodeFrom;
          var nodeTo = adj.nodeTo;
          orn = "$orn" in nodeFrom.data && nodeFrom.data.$orn || "$orn" in nodeTo.data && nodeTo.data.$orn;
        }
        return orn;
      }
    });
    $jit.ST.Label = {};
    $jit.ST.Label.Native = new Class({
      Implements : Graph.Label.Native,
      /**
       * @param {?} canvas
       * @param {Object} node
       * @param {?} opt
       * @return {undefined}
       */
      renderLabel : function(canvas, node, opt) {
        var ctx = canvas.getCtx();
        var coord = node.pos.getc(true);
        var width = node.getData("width");
        var height = node.getData("height");
        var pos = this.viz.fx.getAlignedPos(coord, width, height);
        ctx.fillText(node.name, pos.x + width / 2, pos.y + height / 2);
      }
    });
    $jit.ST.Label.DOM = new Class({
      Implements : Graph.Label.DOM,
      /**
       * @param {?} from
       * @param {?} lab
       * @param {?} options
       * @return {undefined}
       */
      placeLabel : function(from, lab, options) {
        var pos = lab.pos.getc(true);
        var config = this.viz.config;
        var dim = config.Node;
        var canvas = this.viz.canvas;
        var w = lab.getData("width");
        var h = lab.getData("height");
        var $cont = canvas.getSize();
        var labelPos;
        var orn;
        var ox = canvas.translateOffsetX;
        var oy = canvas.translateOffsetY;
        var sx = canvas.scaleOffsetX;
        var sy = canvas.scaleOffsetY;
        var posx = pos.x * sx + ox;
        var posy = pos.y * sy + oy;
        if (dim.align == "center") {
          labelPos = {
            x : Math.round(posx - w / 2 + $cont.width / 2),
            y : Math.round(posy - h / 2 + $cont.height / 2)
          };
        } else {
          if (dim.align == "left") {
            orn = config.orientation;
            if (orn == "bottom" || orn == "top") {
              labelPos = {
                x : Math.round(posx - w / 2 + $cont.width / 2),
                y : Math.round(posy + $cont.height / 2)
              };
            } else {
              labelPos = {
                x : Math.round(posx + $cont.width / 2),
                y : Math.round(posy - h / 2 + $cont.height / 2)
              };
            }
          } else {
            if (dim.align == "right") {
              orn = config.orientation;
              if (orn == "bottom" || orn == "top") {
                labelPos = {
                  x : Math.round(posx - w / 2 + $cont.width / 2),
                  y : Math.round(posy - h + $cont.height / 2)
                };
              } else {
                labelPos = {
                  x : Math.round(posx - w + $cont.width / 2),
                  y : Math.round(posy - h / 2 + $cont.height / 2)
                };
              }
            } else {
              throw "align: not implemented";
            }
          }
        }
        var style = from.style;
        /** @type {string} */
        style.left = labelPos.x + "px";
        /** @type {string} */
        style.top = labelPos.y + "px";
        /** @type {string} */
        style.display = this.fitsInCanvas(labelPos, canvas) ? "" : "none";
        options.onPlaceLabel(from, lab);
      }
    });
    $jit.ST.Label.SVG = new Class({
      Implements : [$jit.ST.Label.DOM, Graph.Label.SVG],
      /**
       * @param {?} viz
       * @return {undefined}
       */
      initialize : function(viz) {
        this.viz = viz;
      }
    });
    $jit.ST.Label.HTML = new Class({
      Implements : [$jit.ST.Label.DOM, Graph.Label.HTML],
      /**
       * @param {?} viz
       * @return {undefined}
       */
      initialize : function(viz) {
        this.viz = viz;
      }
    });
    $jit.ST.Plot.NodeTypes = new Class({
      none : {
        /** @type {function (): undefined} */
        render : $.empty,
        contains : $.lambda(false)
      },
      circle : {
        /**
         * @param {?} adj
         * @param {?} lab
         * @return {undefined}
         */
        render : function(adj, lab) {
          var dim = adj.getData("dim");
          var pos = this.getAlignedPos(adj.pos.getc(true), dim, dim);
          /** @type {number} */
          var qualifier = dim / 2;
          this.nodeHelper.circle.render("fill", {
            x : pos.x + qualifier,
            y : pos.y + qualifier
          }, qualifier, lab);
        },
        /**
         * @param {?} opt_attributes
         * @param {?} value
         * @return {undefined}
         */
        contains : function(opt_attributes, value) {
          var dim = opt_attributes.getData("dim");
          var pos = this.getAlignedPos(opt_attributes.pos.getc(true), dim, dim);
          /** @type {number} */
          var actual = dim / 2;
          this.nodeHelper.circle.contains({
            x : pos.x + actual,
            y : pos.y + actual
          }, value, actual);
        }
      },
      square : {
        /**
         * @param {?} adj
         * @param {?} lab
         * @return {undefined}
         */
        render : function(adj, lab) {
          var dim = adj.getData("dim");
          /** @type {number} */
          var qualifier = dim / 2;
          var pos = this.getAlignedPos(adj.pos.getc(true), dim, dim);
          this.nodeHelper.square.render("fill", {
            x : pos.x + qualifier,
            y : pos.y + qualifier
          }, qualifier, lab);
        },
        /**
         * @param {?} opt_attributes
         * @param {?} value
         * @return {undefined}
         */
        contains : function(opt_attributes, value) {
          var dim = opt_attributes.getData("dim");
          var pos = this.getAlignedPos(opt_attributes.pos.getc(true), dim, dim);
          /** @type {number} */
          var actual = dim / 2;
          this.nodeHelper.square.contains({
            x : pos.x + actual,
            y : pos.y + actual
          }, value, actual);
        }
      },
      ellipse : {
        /**
         * @param {?} adj
         * @param {?} lab
         * @return {undefined}
         */
        render : function(adj, lab) {
          var qualifier = adj.getData("width");
          var cycle = adj.getData("height");
          var pos = this.getAlignedPos(adj.pos.getc(true), qualifier, cycle);
          this.nodeHelper.ellipse.render("fill", {
            x : pos.x + qualifier / 2,
            y : pos.y + cycle / 2
          }, qualifier, cycle, lab);
        },
        /**
         * @param {?} opt_attributes
         * @param {?} value
         * @return {undefined}
         */
        contains : function(opt_attributes, value) {
          var actual = opt_attributes.getData("width");
          var epsilon = opt_attributes.getData("height");
          var pos = this.getAlignedPos(opt_attributes.pos.getc(true), actual, epsilon);
          this.nodeHelper.ellipse.contains({
            x : pos.x + actual / 2,
            y : pos.y + epsilon / 2
          }, value, actual, epsilon);
        }
      },
      rectangle : {
        /**
         * @param {?} adj
         * @param {?} lab
         * @return {undefined}
         */
        render : function(adj, lab) {
          var qualifier = adj.getData("width");
          var cycle = adj.getData("height");
          var pos = this.getAlignedPos(adj.pos.getc(true), qualifier, cycle);
          this.nodeHelper.rectangle.render("fill", {
            x : pos.x + qualifier / 2,
            y : pos.y + cycle / 2
          }, qualifier, cycle, lab);
        },
        /**
         * @param {?} opt_attributes
         * @param {?} value
         * @return {undefined}
         */
        contains : function(opt_attributes, value) {
          var actual = opt_attributes.getData("width");
          var epsilon = opt_attributes.getData("height");
          var pos = this.getAlignedPos(opt_attributes.pos.getc(true), actual, epsilon);
          this.nodeHelper.rectangle.contains({
            x : pos.x + actual / 2,
            y : pos.y + epsilon / 2
          }, value, actual, epsilon);
        }
      }
    });
    $jit.ST.Plot.EdgeTypes = new Class({
      /** @type {function (): undefined} */
      none : $.empty,
      line : {
        /**
         * @param {?} adj
         * @param {?} type
         * @return {undefined}
         */
        render : function(adj, type) {
          var orn = this.getOrientation(adj);
          var nodeFrom = adj.nodeFrom;
          var nodeTo = adj.nodeTo;
          /** @type {boolean} */
          var rel = nodeFrom._depth < nodeTo._depth;
          var from = this.viz.geom.getEdge(rel ? nodeFrom : nodeTo, "begin", orn);
          var lab = this.viz.geom.getEdge(rel ? nodeTo : nodeFrom, "end", orn);
          this.edgeHelper.line.render(from, lab, type);
        },
        /**
         * @param {?} opt_attributes
         * @param {?} value
         * @return {?}
         */
        contains : function(opt_attributes, value) {
          var orn = this.getOrientation(opt_attributes);
          var nodeFrom = opt_attributes.nodeFrom;
          var nodeTo = opt_attributes.nodeTo;
          /** @type {boolean} */
          var rel = nodeFrom._depth < nodeTo._depth;
          var attributes = this.viz.geom.getEdge(rel ? nodeFrom : nodeTo, "begin", orn);
          var pdataOld = this.viz.geom.getEdge(rel ? nodeTo : nodeFrom, "end", orn);
          return this.edgeHelper.line.contains(attributes, pdataOld, value, this.edge.epsilon);
        }
      },
      arrow : {
        /**
         * @param {?} adj
         * @param {?} type
         * @return {undefined}
         */
        render : function(adj, type) {
          var orn = this.getOrientation(adj);
          var node = adj.nodeFrom;
          var child = adj.nodeTo;
          var qualifier = adj.getData("dim");
          var from = this.viz.geom.getEdge(node, "begin", orn);
          var lab = this.viz.geom.getEdge(child, "end", orn);
          var direction = adj.data.$direction;
          var cycle = direction && (direction.length > 1 && direction[0] != node.id);
          this.edgeHelper.arrow.render(from, lab, qualifier, cycle, type);
        },
        /**
         * @param {?} opt_attributes
         * @param {?} value
         * @return {?}
         */
        contains : function(opt_attributes, value) {
          var orn = this.getOrientation(opt_attributes);
          var nodeFrom = opt_attributes.nodeFrom;
          var nodeTo = opt_attributes.nodeTo;
          /** @type {boolean} */
          var rel = nodeFrom._depth < nodeTo._depth;
          var attributes = this.viz.geom.getEdge(rel ? nodeFrom : nodeTo, "begin", orn);
          var pdataOld = this.viz.geom.getEdge(rel ? nodeTo : nodeFrom, "end", orn);
          return this.edgeHelper.arrow.contains(attributes, pdataOld, value, this.edge.epsilon);
        }
      },
      "quadratic:begin" : {
        /**
         * @param {?} adj
         * @param {?} lab
         * @return {undefined}
         */
        render : function(adj, lab) {
          var orn = this.getOrientation(adj);
          var nodeFrom = adj.nodeFrom;
          var nodeTo = adj.nodeTo;
          /** @type {boolean} */
          var rel = nodeFrom._depth < nodeTo._depth;
          var begin = this.viz.geom.getEdge(rel ? nodeFrom : nodeTo, "begin", orn);
          var end = this.viz.geom.getEdge(rel ? nodeTo : nodeFrom, "end", orn);
          var dim = adj.getData("dim");
          var ctx = lab.getCtx();
          ctx.beginPath();
          ctx.moveTo(begin.x, begin.y);
          switch(orn) {
            case "left":
              ctx.quadraticCurveTo(begin.x + dim, begin.y, end.x, end.y);
              break;
            case "right":
              ctx.quadraticCurveTo(begin.x - dim, begin.y, end.x, end.y);
              break;
            case "top":
              ctx.quadraticCurveTo(begin.x, begin.y + dim, end.x, end.y);
              break;
            case "bottom":
              ctx.quadraticCurveTo(begin.x, begin.y - dim, end.x, end.y);
              break;
          }
          ctx.stroke();
        }
      },
      "quadratic:end" : {
        /**
         * @param {?} adj
         * @param {?} lab
         * @return {undefined}
         */
        render : function(adj, lab) {
          var orn = this.getOrientation(adj);
          var nodeFrom = adj.nodeFrom;
          var nodeTo = adj.nodeTo;
          /** @type {boolean} */
          var rel = nodeFrom._depth < nodeTo._depth;
          var begin = this.viz.geom.getEdge(rel ? nodeFrom : nodeTo, "begin", orn);
          var end = this.viz.geom.getEdge(rel ? nodeTo : nodeFrom, "end", orn);
          var dim = adj.getData("dim");
          var ctx = lab.getCtx();
          ctx.beginPath();
          ctx.moveTo(begin.x, begin.y);
          switch(orn) {
            case "left":
              ctx.quadraticCurveTo(end.x - dim, end.y, end.x, end.y);
              break;
            case "right":
              ctx.quadraticCurveTo(end.x + dim, end.y, end.x, end.y);
              break;
            case "top":
              ctx.quadraticCurveTo(end.x, end.y - dim, end.x, end.y);
              break;
            case "bottom":
              ctx.quadraticCurveTo(end.x, end.y + dim, end.x, end.y);
              break;
          }
          ctx.stroke();
        }
      },
      bezier : {
        /**
         * @param {?} adj
         * @param {?} lab
         * @return {undefined}
         */
        render : function(adj, lab) {
          var orn = this.getOrientation(adj);
          var nodeFrom = adj.nodeFrom;
          var nodeTo = adj.nodeTo;
          /** @type {boolean} */
          var rel = nodeFrom._depth < nodeTo._depth;
          var begin = this.viz.geom.getEdge(rel ? nodeFrom : nodeTo, "begin", orn);
          var end = this.viz.geom.getEdge(rel ? nodeTo : nodeFrom, "end", orn);
          var dim = adj.getData("dim");
          var ctx = lab.getCtx();
          ctx.beginPath();
          ctx.moveTo(begin.x, begin.y);
          switch(orn) {
            case "left":
              ctx.bezierCurveTo(begin.x + dim, begin.y, end.x - dim, end.y, end.x, end.y);
              break;
            case "right":
              ctx.bezierCurveTo(begin.x - dim, begin.y, end.x + dim, end.y, end.x, end.y);
              break;
            case "top":
              ctx.bezierCurveTo(begin.x, begin.y + dim, end.x, end.y - dim, end.x, end.y);
              break;
            case "bottom":
              ctx.bezierCurveTo(begin.x, begin.y - dim, end.x, end.y + dim, end.x, end.y);
              break;
          }
          ctx.stroke();
        }
      }
    });
    $jit.ST.Plot.NodeTypes.implement({
      "areachart-stacked" : {
        /**
         * @param {?} node
         * @param {?} lab
         * @return {undefined}
         */
        render : function(node, lab) {
          var pos = node.pos.getc(true);
          var width = node.getData("width");
          var height = node.getData("height");
          var algnPos = this.getAlignedPos(pos, width, height);
          var x = algnPos.x;
          var y = algnPos.y;
          var stringArray = node.getData("stringArray");
          var dimArray = node.getData("dimArray");
          var valArray = node.getData("valueArray");
          var valLeft = $.reduce(valArray, function(dataAndEvents, deepDataAndEvents) {
            return dataAndEvents + deepDataAndEvents[0];
          }, 0);
          var valRight = $.reduce(valArray, function(dataAndEvents, deepDataAndEvents) {
            return dataAndEvents + deepDataAndEvents[1];
          }, 0);
          var colorArray = node.getData("colorArray");
          var colorLength = colorArray.length;
          var config = node.getData("config");
          var gradient = node.getData("gradient");
          var showLabels = config.showLabels;
          var aggregates = config.showAggregates;
          var label = config.Label;
          var prev = node.getData("prev");
          var ctx = lab.getCtx();
          var border = node.getData("border");
          if (colorArray && (dimArray && stringArray)) {
            /** @type {number} */
            var i = 0;
            var l = dimArray.length;
            /** @type {number} */
            var acumLeft = 0;
            /** @type {number} */
            var acumRight = 0;
            /** @type {number} */
            var valAcum = 0;
            for (;i < l;i++) {
              ctx.fillStyle = ctx.strokeStyle = colorArray[i % colorLength];
              ctx.save();
              if (gradient && (dimArray[i][0] > 0 || dimArray[i][1] > 0)) {
                var h1 = acumLeft + dimArray[i][0];
                var h2 = acumRight + dimArray[i][1];
                /** @type {number} */
                var theta2 = Math.atan((h2 - h1) / width);
                /** @type {number} */
                var delta = 55;
                var linear = ctx.createLinearGradient(x + width / 2, y - (h1 + h2) / 2, x + width / 2 + delta * Math.sin(theta2), y - (h1 + h2) / 2 + delta * Math.cos(theta2));
                var color = $.rgbToHex($.map($.hexToRgb(colorArray[i % colorLength].slice(1)), function(dataAndEvents) {
                  return dataAndEvents * 0.85 >> 0;
                }));
                linear.addColorStop(0, colorArray[i % colorLength]);
                linear.addColorStop(1, color);
                ctx.fillStyle = linear;
              }
              ctx.beginPath();
              ctx.moveTo(x, y - acumLeft);
              ctx.lineTo(x + width, y - acumRight);
              ctx.lineTo(x + width, y - acumRight - dimArray[i][1]);
              ctx.lineTo(x, y - acumLeft - dimArray[i][0]);
              ctx.lineTo(x, y - acumLeft);
              ctx.fill();
              ctx.restore();
              if (border) {
                /** @type {boolean} */
                var strong = border.name == stringArray[i];
                /** @type {number} */
                var b1 = strong ? 0.7 : 0.8;
                color = $.rgbToHex($.map($.hexToRgb(colorArray[i % colorLength].slice(1)), function(a4) {
                  return a4 * b1 >> 0;
                }));
                ctx.strokeStyle = color;
                /** @type {number} */
                ctx.lineWidth = strong ? 4 : 1;
                ctx.save();
                ctx.beginPath();
                if (border.index === 0) {
                  ctx.moveTo(x, y - acumLeft);
                  ctx.lineTo(x, y - acumLeft - dimArray[i][0]);
                } else {
                  ctx.moveTo(x + width, y - acumRight);
                  ctx.lineTo(x + width, y - acumRight - dimArray[i][1]);
                }
                ctx.stroke();
                ctx.restore();
              }
              acumLeft += dimArray[i][0] || 0;
              acumRight += dimArray[i][1] || 0;
              if (dimArray[i][0] > 0) {
                valAcum += valArray[i][0] || 0;
              }
            }
            if (prev && label.type == "Native") {
              ctx.save();
              ctx.beginPath();
              ctx.fillStyle = ctx.strokeStyle = label.color;
              /** @type {string} */
              ctx.font = label.style + " " + label.size + "px " + label.family;
              /** @type {string} */
              ctx.textAlign = "center";
              /** @type {string} */
              ctx.textBaseline = "middle";
              var aggValue = aggregates(node.name, valLeft, valRight, node, valAcum);
              if (aggValue !== false) {
                ctx.fillText(aggValue !== true ? aggValue : valAcum, x, y - acumLeft - config.labelOffset - label.size / 2, width);
              }
              if (showLabels(node.name, valLeft, valRight, node)) {
                ctx.fillText(node.name, x, y + label.size / 2 + config.labelOffset);
              }
              ctx.restore();
            }
          }
        },
        /**
         * @param {?} opt_attributes
         * @param {?} value
         * @return {?}
         */
        contains : function(opt_attributes, value) {
          var pos = opt_attributes.pos.getc(true);
          var width = opt_attributes.getData("width");
          var height = opt_attributes.getData("height");
          var algnPos = this.getAlignedPos(pos, width, height);
          var x = algnPos.x;
          var y = algnPos.y;
          var dimArray = opt_attributes.getData("dimArray");
          /** @type {number} */
          var rx = value.x - x;
          if (value.x < x || (value.x > x + width || (value.y > y || value.y < y - height))) {
            return false;
          }
          /** @type {number} */
          var i = 0;
          var l = dimArray.length;
          var lAcum = y;
          var rAcum = y;
          for (;i < l;i++) {
            var dimi = dimArray[i];
            lAcum -= dimi[0];
            rAcum -= dimi[1];
            var intersec = lAcum + (rAcum - lAcum) * rx / width;
            if (value.y >= intersec) {
              /** @type {number} */
              var j = +(rx > width / 2);
              return{
                name : opt_attributes.getData("stringArray")[i],
                color : opt_attributes.getData("colorArray")[i],
                value : opt_attributes.getData("valueArray")[i][j],
                index : j
              };
            }
          }
          return false;
        }
      }
    });
    $jit.AreaChart = new Class({
      st : null,
      colors : ["#416D9C", "#70A35E", "#EBB056", "#C74243", "#83548B", "#909291", "#557EAA"],
      selected : {},
      busy : false,
      /**
       * @param {?} controller
       * @return {undefined}
       */
      initialize : function(controller) {
        this.controller = this.config = $.merge(Options("Canvas", "Margin", "Label", "AreaChart"), {
          Label : {
            type : "Native"
          }
        }, controller);
        var showLabels = this.config.showLabels;
        var typeLabels = $.type(showLabels);
        var showAggregates = this.config.showAggregates;
        var typeAggregates = $.type(showAggregates);
        this.config.showLabels = typeLabels == "function" ? showLabels : $.lambda(showLabels);
        this.config.showAggregates = typeAggregates == "function" ? showAggregates : $.lambda(showAggregates);
        this.initializeViz();
      },
      /**
       * @return {undefined}
       */
      initializeViz : function() {
        var config = this.config;
        var that = this;
        var nodeType = config.type.split(":")[0];
        var nodeLabels = {};
        var delegate = new $jit.ST({
          injectInto : config.injectInto,
          width : config.width,
          height : config.height,
          orientation : "bottom",
          levelDistance : 0,
          siblingOffset : 0,
          subtreeOffset : 0,
          withLabels : config.Label.type != "Native",
          useCanvas : config.useCanvas,
          Label : {
            type : config.Label.type
          },
          Node : {
            overridable : true,
            type : "areachart-" + nodeType,
            align : "left",
            width : 1,
            height : 1
          },
          Edge : {
            type : "none"
          },
          Tips : {
            enable : config.Tips.enable,
            type : "Native",
            force : true,
            /**
             * @param {?} from
             * @param {?} type
             * @param {?} event
             * @return {undefined}
             */
            onShow : function(from, type, event) {
              var lab = event;
              config.Tips.onShow(from, lab, type);
            }
          },
          Events : {
            enable : true,
            type : "Native",
            /**
             * @param {?} adj
             * @param {?} lab
             * @param {?} selector
             * @return {undefined}
             */
            onClick : function(adj, lab, selector) {
              if (!config.filterOnClick && !config.Events.enable) {
                return;
              }
              var from = lab.getContains();
              if (from) {
                if (config.filterOnClick) {
                  that.filter(from.name);
                }
              }
              if (config.Events.enable) {
                config.Events.onClick(from, lab, selector);
              }
            },
            /**
             * @param {?} adj
             * @param {?} lab
             * @param {?} event
             * @return {undefined}
             */
            onRightClick : function(adj, lab, event) {
              if (!config.restoreOnRightClick) {
                return;
              }
              that.restore();
            },
            /**
             * @param {?} adj
             * @param {?} lab
             * @param {?} event
             * @return {undefined}
             */
            onMouseMove : function(adj, lab, event) {
              if (!config.selectOnHover) {
                return;
              }
              if (adj) {
                var elem = lab.getContains();
                that.select(adj.id, elem.name, elem.index);
              } else {
                that.select(false, false, false);
              }
            }
          },
          /**
           * @param {?} adj
           * @param {?} lab
           * @return {undefined}
           */
          onCreateLabel : function(adj, lab) {
            var labelConf = config.Label;
            var valueArray = lab.getData("valueArray");
            var acumLeft = $.reduce(valueArray, function(dataAndEvents, deepDataAndEvents) {
              return dataAndEvents + deepDataAndEvents[0];
            }, 0);
            var acumRight = $.reduce(valueArray, function(dataAndEvents, deepDataAndEvents) {
              return dataAndEvents + deepDataAndEvents[1];
            }, 0);
            if (lab.getData("prev")) {
              var nlbs = {
                wrapper : document.createElement("div"),
                aggregate : document.createElement("div"),
                label : document.createElement("div")
              };
              /** @type {Element} */
              var wrapper = nlbs.wrapper;
              /** @type {Element} */
              var label = nlbs.label;
              /** @type {Element} */
              var aggregate = nlbs.aggregate;
              /** @type {(CSSStyleDeclaration|null)} */
              var wrapperStyle = wrapper.style;
              /** @type {(CSSStyleDeclaration|null)} */
              var style = label.style;
              /** @type {(CSSStyleDeclaration|null)} */
              var aggregateStyle = aggregate.style;
              nodeLabels[lab.id] = nlbs;
              wrapper.appendChild(label);
              wrapper.appendChild(aggregate);
              if (!config.showLabels(lab.name, acumLeft, acumRight, lab)) {
                /** @type {string} */
                label.style.display = "none";
              }
              if (!config.showAggregates(lab.name, acumLeft, acumRight, lab)) {
                /** @type {string} */
                aggregate.style.display = "none";
              }
              /** @type {string} */
              wrapperStyle.position = "relative";
              /** @type {string} */
              wrapperStyle.overflow = "visible";
              /** @type {string} */
              wrapperStyle.fontSize = labelConf.size + "px";
              wrapperStyle.fontFamily = labelConf.family;
              wrapperStyle.color = labelConf.color;
              /** @type {string} */
              wrapperStyle.textAlign = "center";
              /** @type {string} */
              aggregateStyle.position = style.position = "absolute";
              adj.style.width = lab.getData("width") + "px";
              adj.style.height = lab.getData("height") + "px";
              label.innerHTML = lab.name;
              adj.appendChild(wrapper);
            }
          },
          /**
           * @param {?} adj
           * @param {?} lab
           * @return {undefined}
           */
          onPlaceLabel : function(adj, lab) {
            if (!lab.getData("prev")) {
              return;
            }
            var labels = nodeLabels[lab.id];
            var wrapperStyle = labels.wrapper.style;
            var aggregateStyle = labels.label.style;
            var style = labels.aggregate.style;
            var w = lab.getData("width");
            var constrainedHeight = lab.getData("height");
            var dimArray = lab.getData("dimArray");
            var valArray = lab.getData("valueArray");
            var acumLeft = $.reduce(valArray, function(dataAndEvents, deepDataAndEvents) {
              return dataAndEvents + deepDataAndEvents[0];
            }, 0);
            var acumRight = $.reduce(valArray, function(dataAndEvents, deepDataAndEvents) {
              return dataAndEvents + deepDataAndEvents[1];
            }, 0);
            /** @type {number} */
            var font = parseInt(wrapperStyle.fontSize, 10);
            var styleDeclaration = adj.style;
            if (dimArray && valArray) {
              if (config.showLabels(lab.name, acumLeft, acumRight, lab)) {
                /** @type {string} */
                aggregateStyle.display = "";
              } else {
                /** @type {string} */
                aggregateStyle.display = "none";
              }
              var aggValue = config.showAggregates(lab.name, acumLeft, acumRight, lab);
              if (aggValue !== false) {
                /** @type {string} */
                style.display = "";
              } else {
                /** @type {string} */
                style.display = "none";
              }
              /** @type {string} */
              wrapperStyle.width = style.width = aggregateStyle.width = adj.style.width = w + "px";
              /** @type {string} */
              style.left = aggregateStyle.left = -w / 2 + "px";
              /** @type {number} */
              var i = 0;
              var l = valArray.length;
              /** @type {number} */
              var acum = 0;
              /** @type {number} */
              var leftAcum = 0;
              for (;i < l;i++) {
                if (dimArray[i][0] > 0) {
                  acum += valArray[i][0];
                  leftAcum += dimArray[i][0];
                }
              }
              /** @type {string} */
              style.top = -font - config.labelOffset + "px";
              /** @type {string} */
              aggregateStyle.top = config.labelOffset + leftAcum + "px";
              /** @type {string} */
              adj.style.top = parseInt(adj.style.top, 10) - leftAcum + "px";
              /** @type {string} */
              adj.style.height = wrapperStyle.height = leftAcum + "px";
              labels.aggregate.innerHTML = aggValue !== true ? aggValue : acum;
            }
          }
        });
        var testNode = delegate.canvas.getSize();
        var margin = config.Margin;
        delegate.config.offsetY = -testNode.height / 2 + margin.bottom + (config.showLabels && config.labelOffset + config.Label.size);
        /** @type {number} */
        delegate.config.offsetX = (margin.right - margin.left) / 2;
        this.delegate = delegate;
        this.canvas = this.delegate.canvas;
      },
      /**
       * @param {Object} json
       * @return {undefined}
       */
      loadJSON : function(json) {
        /** @type {number} */
        var prefix = $.time();
        /** @type {Array} */
        var ch = [];
        var delegate = this.delegate;
        var name = $.splat(json.label);
        var color = $.splat(json.color || this.colors);
        var config = this.config;
        /** @type {boolean} */
        var gradient = !!config.type.split(":")[1];
        var animate = config.animate;
        /** @type {number} */
        var i = 0;
        var values = json.values;
        var valuesLen = values.length;
        for (;i < valuesLen - 1;i++) {
          var value = values[i];
          var prev = values[i - 1];
          var next = values[i + 1];
          var valLeft = $.splat(values[i].values);
          var valRight = $.splat(values[i + 1].values);
          var valArray = $.zip(valLeft, valRight);
          /** @type {number} */
          var D = 0;
          /** @type {number} */
          var C = 0;
          ch.push({
            id : prefix + value.label,
            name : value.label,
            data : {
              value : valArray,
              "$valueArray" : valArray,
              "$colorArray" : color,
              "$stringArray" : name,
              "$next" : next.label,
              "$prev" : prev ? prev.label : false,
              "$config" : config,
              "$gradient" : gradient
            },
            children : []
          });
        }
        var root = {
          id : prefix + "$root",
          name : "",
          data : {
            "$type" : "none",
            "$width" : 1,
            "$height" : 1
          },
          children : ch
        };
        delegate.loadJSON(root);
        this.normalizeDims();
        delegate.compute();
        delegate.select(delegate.root);
        if (animate) {
          delegate.fx.animate({
            modes : ["node-property:height:dimArray"],
            duration : 1500
          });
        }
      },
      /**
       * @param {Object} json
       * @param {Object} onComplete
       * @return {undefined}
       */
      updateJSON : function(json, onComplete) {
        if (this.busy) {
          return;
        }
        /** @type {boolean} */
        this.busy = true;
        var delegate = this.delegate;
        var graph = delegate.graph;
        var extended = json.label && $.splat(json.label);
        var values = json.values;
        var animate = this.config.animate;
        var that = this;
        var hashValues = {};
        /** @type {number} */
        var i = 0;
        var valuesLen = values.length;
        for (;i < valuesLen;i++) {
          hashValues[values[i].label] = values[i];
        }
        graph.eachNode(function(n) {
          var v = hashValues[n.name];
          var original = n.getData("stringArray");
          var attributes = n.getData("valueArray");
          var next = n.getData("next");
          if (v) {
            v.values = $.splat(v.values);
            $.each(attributes, function(a, key) {
              a[0] = v.values[key];
              if (extended) {
                original[key] = extended[key];
              }
            });
            n.setData("valueArray", attributes);
          }
          if (next) {
            v = hashValues[next];
            if (v) {
              $.each(attributes, function(vec, i) {
                vec[1] = v.values[i];
              });
            }
          }
        });
        this.normalizeDims();
        delegate.compute();
        delegate.select(delegate.root);
        if (animate) {
          delegate.fx.animate({
            modes : ["node-property:height:dimArray"],
            duration : 1500,
            /**
             * @return {undefined}
             */
            onComplete : function() {
              /** @type {boolean} */
              that.busy = false;
              if (onComplete) {
                onComplete.onComplete();
              }
            }
          });
        }
      },
      /**
       * @param {string} flags
       * @param {Object} scope
       * @return {undefined}
       */
      filter : function(flags, scope) {
        if (this.busy) {
          return;
        }
        /** @type {boolean} */
        this.busy = true;
        if (this.config.Tips.enable) {
          this.delegate.tips.hide();
        }
        this.select(false, false, false);
        var filter = $.splat(flags);
        var root = this.delegate.graph.getNode(this.delegate.root);
        var that = this;
        this.normalizeDims();
        root.eachAdjacency(function(adj) {
          var n = adj.nodeTo;
          var dimArray = n.getData("dimArray", "end");
          var stringArray = n.getData("stringArray");
          n.setData("dimArray", $.map(dimArray, function(dataAndEvents, i) {
            return $.indexOf(filter, stringArray[i]) > -1 ? dataAndEvents : [0, 0];
          }), "end");
        });
        this.delegate.fx.animate({
          modes : ["node-property:dimArray"],
          duration : 1500,
          /**
           * @return {undefined}
           */
          onComplete : function() {
            /** @type {boolean} */
            that.busy = false;
            if (scope) {
              scope.onComplete();
            }
          }
        });
      },
      /**
       * @param {Object} callback
       * @return {undefined}
       */
      restore : function(callback) {
        if (this.busy) {
          return;
        }
        /** @type {boolean} */
        this.busy = true;
        if (this.config.Tips.enable) {
          this.delegate.tips.hide();
        }
        this.select(false, false, false);
        this.normalizeDims();
        var that = this;
        this.delegate.fx.animate({
          modes : ["node-property:height:dimArray"],
          duration : 1500,
          /**
           * @return {undefined}
           */
          onComplete : function() {
            /** @type {boolean} */
            that.busy = false;
            if (callback) {
              callback.onComplete();
            }
          }
        });
      },
      /**
       * @param {?} id
       * @param {boolean} lab
       * @param {boolean} index
       * @return {undefined}
       */
      select : function(id, lab, index) {
        if (!this.config.selectOnHover) {
          return;
        }
        var s = this.selected;
        if (s.id != id || (s.name != lab || s.index != index)) {
          s.id = id;
          /** @type {boolean} */
          s.name = lab;
          /** @type {boolean} */
          s.index = index;
          this.delegate.graph.eachNode(function(n) {
            n.setData("border", false);
          });
          if (id) {
            var n = this.delegate.graph.getNode(id);
            n.setData("border", s);
            /** @type {string} */
            var link = index === 0 ? "prev" : "next";
            link = n.getData(link);
            if (link) {
              n = this.delegate.graph.getByName(link);
              if (n) {
                n.setData("border", {
                  name : lab,
                  index : 1 - index
                });
              }
            }
          }
          this.delegate.plot();
        }
      },
      /**
       * @return {?}
       */
      getLegend : function() {
        var legend = {};
        var n;
        this.delegate.graph.getNode(this.delegate.root).eachAdjacency(function(adj) {
          n = adj.nodeTo;
        });
        var colors = n.getData("colorArray");
        var colorsLen = colors.length;
        $.each(n.getData("stringArray"), function(s, i) {
          legend[s] = colors[i % colorsLen];
        });
        return legend;
      },
      /**
       * @return {?}
       */
      getMaxValue : function() {
        /** @type {number} */
        var maxValue = 0;
        this.delegate.graph.eachNode(function(n) {
          var attributes = n.getData("valueArray");
          /** @type {number} */
          var y = 0;
          /** @type {number} */
          var x = 0;
          $.each(attributes, function(dataAndEvents) {
            y += +dataAndEvents[0];
            x += +dataAndEvents[1];
          });
          var acum = x > y ? x : y;
          maxValue = maxValue > acum ? maxValue : acum;
        });
        return maxValue;
      },
      /**
       * @return {undefined}
       */
      normalizeDims : function() {
        var root = this.delegate.graph.getNode(this.delegate.root);
        /** @type {number} */
        var z = 0;
        root.eachAdjacency(function() {
          z++;
        });
        var maxValue = this.getMaxValue() || 1;
        var $cont = this.delegate.canvas.getSize();
        var config = this.config;
        var margin = config.Margin;
        var labelOffset = config.labelOffset + config.Label.size;
        /** @type {number} */
        var recurring = ($cont.width - (margin.left + margin.right)) / z;
        var animate = config.animate;
        /** @type {number} */
        var height = $cont.height - (margin.top + margin.bottom) - (config.showAggregates && labelOffset) - (config.showLabels && labelOffset);
        this.delegate.graph.eachNode(function(n) {
          /** @type {number} */
          var y = 0;
          /** @type {number} */
          var x = 0;
          /** @type {Array} */
          var animateValue = [];
          $.each(n.getData("valueArray"), function(dataAndEvents) {
            y += +dataAndEvents[0];
            x += +dataAndEvents[1];
            animateValue.push([0, 0]);
          });
          var acum = x > y ? x : y;
          n.setData("width", recurring);
          if (animate) {
            n.setData("height", acum * height / maxValue, "end");
            n.setData("dimArray", $.map(n.getData("valueArray"), function(n) {
              return[n[0] * height / maxValue, n[1] * height / maxValue];
            }), "end");
            var dimArray = n.getData("dimArray");
            if (!dimArray) {
              n.setData("dimArray", animateValue);
            }
          } else {
            n.setData("height", acum * height / maxValue);
            n.setData("dimArray", $.map(n.getData("valueArray"), function(n) {
              return[n[0] * height / maxValue, n[1] * height / maxValue];
            }));
          }
        });
      }
    });
    Options.BarChart = {
      $extend : true,
      animate : true,
      type : "stacked",
      labelOffset : 3,
      barsOffset : 0,
      hoveredColor : "#9fd4ff",
      orientation : "horizontal",
      showAggregates : true,
      showLabels : true,
      Tips : {
        enable : false,
        /** @type {function (): undefined} */
        onShow : $.empty,
        /** @type {function (): undefined} */
        onHide : $.empty
      },
      Events : {
        enable : false,
        /** @type {function (): undefined} */
        onClick : $.empty
      }
    };
    $jit.ST.Plot.NodeTypes.implement({
      "barchart-stacked" : {
        /**
         * @param {?} node
         * @param {?} lab
         * @return {undefined}
         */
        render : function(node, lab) {
          var pos = node.pos.getc(true);
          var width = node.getData("width");
          var height = node.getData("height");
          var algnPos = this.getAlignedPos(pos, width, height);
          var x = algnPos.x;
          var y = algnPos.y;
          var dimArray = node.getData("dimArray");
          var valueArray = node.getData("valueArray");
          var colorArray = node.getData("colorArray");
          var colorLength = colorArray.length;
          var stringArray = node.getData("stringArray");
          var ctx = lab.getCtx();
          var opt = {};
          var border = node.getData("border");
          var gradient = node.getData("gradient");
          var config = node.getData("config");
          /** @type {boolean} */
          var isH = config.orientation == "horizontal";
          var aggregates = config.showAggregates;
          var showLabels = config.showLabels;
          var label = config.Label;
          if (colorArray && (dimArray && stringArray)) {
            /** @type {number} */
            var i = 0;
            var l = dimArray.length;
            /** @type {number} */
            var acum = 0;
            /** @type {number} */
            var valAcum = 0;
            for (;i < l;i++) {
              ctx.fillStyle = ctx.strokeStyle = colorArray[i % colorLength];
              if (gradient) {
                var linear;
                if (isH) {
                  linear = ctx.createLinearGradient(x + acum + dimArray[i] / 2, y, x + acum + dimArray[i] / 2, y + height);
                } else {
                  linear = ctx.createLinearGradient(x, y - acum - dimArray[i] / 2, x + width, y - acum - dimArray[i] / 2);
                }
                var color = $.rgbToHex($.map($.hexToRgb(colorArray[i % colorLength].slice(1)), function(dataAndEvents) {
                  return dataAndEvents * 0.5 >> 0;
                }));
                linear.addColorStop(0, color);
                linear.addColorStop(0.5, colorArray[i % colorLength]);
                linear.addColorStop(1, color);
                ctx.fillStyle = linear;
              }
              if (isH) {
                ctx.fillRect(x + acum, y, dimArray[i], height);
              } else {
                ctx.fillRect(x, y - acum - dimArray[i], width, dimArray[i]);
              }
              if (border && border.name == stringArray[i]) {
                opt.acum = acum;
                opt.dimValue = dimArray[i];
              }
              acum += dimArray[i] || 0;
              valAcum += valueArray[i] || 0;
            }
            if (border) {
              ctx.save();
              /** @type {number} */
              ctx.lineWidth = 2;
              ctx.strokeStyle = border.color;
              if (isH) {
                ctx.strokeRect(x + opt.acum + 1, y + 1, opt.dimValue - 2, height - 2);
              } else {
                ctx.strokeRect(x + 1, y - opt.acum - opt.dimValue + 1, width - 2, opt.dimValue - 2);
              }
              ctx.restore();
            }
            if (label.type == "Native") {
              ctx.save();
              ctx.fillStyle = ctx.strokeStyle = label.color;
              /** @type {string} */
              ctx.font = label.style + " " + label.size + "px " + label.family;
              /** @type {string} */
              ctx.textBaseline = "middle";
              var aggValue = aggregates(node.name, valAcum, node);
              if (aggValue !== false) {
                aggValue = aggValue !== true ? aggValue : valAcum;
                if (isH) {
                  /** @type {string} */
                  ctx.textAlign = "right";
                  ctx.fillText(aggValue, x + acum - config.labelOffset, y + height / 2);
                } else {
                  /** @type {string} */
                  ctx.textAlign = "center";
                  ctx.fillText(aggValue, x + width / 2, y - height - label.size / 2 - config.labelOffset);
                }
              }
              if (showLabels(node.name, valAcum, node)) {
                if (isH) {
                  /** @type {string} */
                  ctx.textAlign = "center";
                  ctx.translate(x - config.labelOffset - label.size / 2, y + height / 2);
                  ctx.rotate(Math.PI / 2);
                  ctx.fillText(node.name, 0, 0);
                } else {
                  /** @type {string} */
                  ctx.textAlign = "center";
                  ctx.fillText(node.name, x + width / 2, y + label.size / 2 + config.labelOffset);
                }
              }
              ctx.restore();
            }
          }
        },
        /**
         * @param {?} opt_attributes
         * @param {?} value
         * @return {?}
         */
        contains : function(opt_attributes, value) {
          var pos = opt_attributes.pos.getc(true);
          var width = opt_attributes.getData("width");
          var height = opt_attributes.getData("height");
          var algnPos = this.getAlignedPos(pos, width, height);
          var x = algnPos.x;
          var y = algnPos.y;
          var dimArray = opt_attributes.getData("dimArray");
          var config = opt_attributes.getData("config");
          /** @type {number} */
          var dx0 = value.x - x;
          /** @type {boolean} */
          var horz = config.orientation == "horizontal";
          if (horz) {
            if (value.x < x || (value.x > x + width || (value.y > y + height || value.y < y))) {
              return false;
            }
          } else {
            if (value.x < x || (value.x > x + width || (value.y > y || value.y < y - height))) {
              return false;
            }
          }
          /** @type {number} */
          var i = 0;
          var l = dimArray.length;
          var acum = horz ? x : y;
          for (;i < l;i++) {
            var dimi = dimArray[i];
            if (horz) {
              acum += dimi;
              var intersec = acum;
              if (value.x <= intersec) {
                return{
                  name : opt_attributes.getData("stringArray")[i],
                  color : opt_attributes.getData("colorArray")[i],
                  value : opt_attributes.getData("valueArray")[i],
                  label : opt_attributes.name
                };
              }
            } else {
              acum -= dimi;
              intersec = acum;
              if (value.y >= intersec) {
                return{
                  name : opt_attributes.getData("stringArray")[i],
                  color : opt_attributes.getData("colorArray")[i],
                  value : opt_attributes.getData("valueArray")[i],
                  label : opt_attributes.name
                };
              }
            }
          }
          return false;
        }
      },
      "barchart-grouped" : {
        /**
         * @param {?} node
         * @param {?} lab
         * @return {undefined}
         */
        render : function(node, lab) {
          var pos = node.pos.getc(true);
          var width = node.getData("width");
          var height = node.getData("height");
          var algnPos = this.getAlignedPos(pos, width, height);
          var x = algnPos.x;
          var y = algnPos.y;
          var dimArray = node.getData("dimArray");
          var valueArray = node.getData("valueArray");
          var valueLength = valueArray.length;
          var colorArray = node.getData("colorArray");
          var colorLength = colorArray.length;
          var stringArray = node.getData("stringArray");
          var ctx = lab.getCtx();
          var opt = {};
          var border = node.getData("border");
          var gradient = node.getData("gradient");
          var config = node.getData("config");
          /** @type {boolean} */
          var horz = config.orientation == "horizontal";
          var aggregates = config.showAggregates;
          var showLabels = config.showLabels;
          var label = config.Label;
          /** @type {number} */
          var fixedDim = (horz ? height : width) / valueLength;
          if (colorArray && (dimArray && stringArray)) {
            /** @type {number} */
            var i = 0;
            var l = valueLength;
            /** @type {number} */
            var X = 0;
            /** @type {number} */
            var valAcum = 0;
            for (;i < l;i++) {
              ctx.fillStyle = ctx.strokeStyle = colorArray[i % colorLength];
              if (gradient) {
                var linear;
                if (horz) {
                  linear = ctx.createLinearGradient(x + dimArray[i] / 2, y + fixedDim * i, x + dimArray[i] / 2, y + fixedDim * (i + 1));
                } else {
                  linear = ctx.createLinearGradient(x + fixedDim * i, y - dimArray[i] / 2, x + fixedDim * (i + 1), y - dimArray[i] / 2);
                }
                var color = $.rgbToHex($.map($.hexToRgb(colorArray[i % colorLength].slice(1)), function(dataAndEvents) {
                  return dataAndEvents * 0.5 >> 0;
                }));
                linear.addColorStop(0, color);
                linear.addColorStop(0.5, colorArray[i % colorLength]);
                linear.addColorStop(1, color);
                ctx.fillStyle = linear;
              }
              if (horz) {
                ctx.fillRect(x, y + fixedDim * i, dimArray[i], fixedDim);
              } else {
                ctx.fillRect(x + fixedDim * i, y - dimArray[i], fixedDim, dimArray[i]);
              }
              if (border && border.name == stringArray[i]) {
                /** @type {number} */
                opt.acum = fixedDim * i;
                opt.dimValue = dimArray[i];
              }
              X += dimArray[i] || 0;
              valAcum += valueArray[i] || 0;
            }
            if (border) {
              ctx.save();
              /** @type {number} */
              ctx.lineWidth = 2;
              ctx.strokeStyle = border.color;
              if (horz) {
                ctx.strokeRect(x + 1, y + opt.acum + 1, opt.dimValue - 2, fixedDim - 2);
              } else {
                ctx.strokeRect(x + opt.acum + 1, y - opt.dimValue + 1, fixedDim - 2, opt.dimValue - 2);
              }
              ctx.restore();
            }
            if (label.type == "Native") {
              ctx.save();
              ctx.fillStyle = ctx.strokeStyle = label.color;
              /** @type {string} */
              ctx.font = label.style + " " + label.size + "px " + label.family;
              /** @type {string} */
              ctx.textBaseline = "middle";
              var aggValue = aggregates(node.name, valAcum, node);
              if (aggValue !== false) {
                aggValue = aggValue !== true ? aggValue : valAcum;
                if (horz) {
                  /** @type {string} */
                  ctx.textAlign = "right";
                  ctx.fillText(aggValue, x + Math.max.apply(null, dimArray) - config.labelOffset, y + height / 2);
                } else {
                  /** @type {string} */
                  ctx.textAlign = "center";
                  ctx.fillText(aggValue, x + width / 2, y - Math.max.apply(null, dimArray) - label.size / 2 - config.labelOffset);
                }
              }
              if (showLabels(node.name, valAcum, node)) {
                if (horz) {
                  /** @type {string} */
                  ctx.textAlign = "center";
                  ctx.translate(x - config.labelOffset - label.size / 2, y + height / 2);
                  ctx.rotate(Math.PI / 2);
                  ctx.fillText(node.name, 0, 0);
                } else {
                  /** @type {string} */
                  ctx.textAlign = "center";
                  ctx.fillText(node.name, x + width / 2, y + label.size / 2 + config.labelOffset);
                }
              }
              ctx.restore();
            }
          }
        },
        /**
         * @param {?} opt_attributes
         * @param {?} value
         * @return {?}
         */
        contains : function(opt_attributes, value) {
          var pos = opt_attributes.pos.getc(true);
          var width = opt_attributes.getData("width");
          var height = opt_attributes.getData("height");
          var algnPos = this.getAlignedPos(pos, width, height);
          var x = algnPos.x;
          var y = algnPos.y;
          var codeSegments = opt_attributes.getData("dimArray");
          var valueLength = codeSegments.length;
          var config = opt_attributes.getData("config");
          /** @type {number} */
          var dx0 = value.x - x;
          /** @type {boolean} */
          var horz = config.orientation == "horizontal";
          /** @type {number} */
          var fixedDim = (horz ? height : width) / valueLength;
          if (horz) {
            if (value.x < x || (value.x > x + width || (value.y > y + height || value.y < y))) {
              return false;
            }
          } else {
            if (value.x < x || (value.x > x + width || (value.y > y || value.y < y - height))) {
              return false;
            }
          }
          /** @type {number} */
          var i = 0;
          var valuesLen = codeSegments.length;
          for (;i < valuesLen;i++) {
            var delta = codeSegments[i];
            if (horz) {
              var limit = y + fixedDim * i;
              if (value.x <= x + delta && (value.y >= limit && value.y <= limit + fixedDim)) {
                return{
                  name : opt_attributes.getData("stringArray")[i],
                  color : opt_attributes.getData("colorArray")[i],
                  value : opt_attributes.getData("valueArray")[i],
                  label : opt_attributes.name
                };
              }
            } else {
              limit = x + fixedDim * i;
              if (value.x >= limit && (value.x <= limit + fixedDim && value.y >= y - delta)) {
                return{
                  name : opt_attributes.getData("stringArray")[i],
                  color : opt_attributes.getData("colorArray")[i],
                  value : opt_attributes.getData("valueArray")[i],
                  label : opt_attributes.name
                };
              }
            }
          }
          return false;
        }
      }
    });
    $jit.BarChart = new Class({
      st : null,
      colors : ["#416D9C", "#70A35E", "#EBB056", "#C74243", "#83548B", "#909291", "#557EAA"],
      selected : {},
      busy : false,
      /**
       * @param {?} controller
       * @return {undefined}
       */
      initialize : function(controller) {
        this.controller = this.config = $.merge(Options("Canvas", "Margin", "Label", "BarChart"), {
          Label : {
            type : "Native"
          }
        }, controller);
        var showLabels = this.config.showLabels;
        var typeLabels = $.type(showLabels);
        var showAggregates = this.config.showAggregates;
        var typeAggregates = $.type(showAggregates);
        this.config.showLabels = typeLabels == "function" ? showLabels : $.lambda(showLabels);
        this.config.showAggregates = typeAggregates == "function" ? showAggregates : $.lambda(showAggregates);
        this.initializeViz();
      },
      /**
       * @return {undefined}
       */
      initializeViz : function() {
        var config = this.config;
        var that = this;
        var nodeType = config.type.split(":")[0];
        /** @type {boolean} */
        var horz = config.orientation == "horizontal";
        var nodeLabels = {};
        var delegate = new $jit.ST({
          injectInto : config.injectInto,
          width : config.width,
          height : config.height,
          orientation : horz ? "left" : "bottom",
          levelDistance : 0,
          siblingOffset : config.barsOffset,
          subtreeOffset : 0,
          withLabels : config.Label.type != "Native",
          useCanvas : config.useCanvas,
          Label : {
            type : config.Label.type
          },
          Node : {
            overridable : true,
            type : "barchart-" + nodeType,
            align : "left",
            width : 1,
            height : 1
          },
          Edge : {
            type : "none"
          },
          Tips : {
            enable : config.Tips.enable,
            type : "Native",
            force : true,
            /**
             * @param {?} from
             * @param {?} type
             * @param {?} event
             * @return {undefined}
             */
            onShow : function(from, type, event) {
              var lab = event;
              config.Tips.onShow(from, lab, type);
            }
          },
          Events : {
            enable : true,
            type : "Native",
            /**
             * @param {?} adj
             * @param {?} lab
             * @param {?} selector
             * @return {undefined}
             */
            onClick : function(adj, lab, selector) {
              if (!config.Events.enable) {
                return;
              }
              var from = lab.getContains();
              config.Events.onClick(from, lab, selector);
            },
            /**
             * @param {?} adj
             * @param {?} lab
             * @param {?} event
             * @return {undefined}
             */
            onMouseMove : function(adj, lab, event) {
              if (!config.hoveredColor) {
                return;
              }
              if (adj) {
                var elem = lab.getContains();
                that.select(adj.id, elem.name, elem.index);
              } else {
                that.select(false, false, false);
              }
            }
          },
          /**
           * @param {?} adj
           * @param {?} lab
           * @return {undefined}
           */
          onCreateLabel : function(adj, lab) {
            var labelConf = config.Label;
            var reversed = lab.getData("valueArray");
            var acumLeft = $.reduce(reversed, function(far, near) {
              return far + near;
            }, 0);
            var nlbs = {
              wrapper : document.createElement("div"),
              aggregate : document.createElement("div"),
              label : document.createElement("div")
            };
            /** @type {Element} */
            var wrapper = nlbs.wrapper;
            /** @type {Element} */
            var label = nlbs.label;
            /** @type {Element} */
            var aggregate = nlbs.aggregate;
            /** @type {(CSSStyleDeclaration|null)} */
            var wrapperStyle = wrapper.style;
            /** @type {(CSSStyleDeclaration|null)} */
            var labelStyle = label.style;
            /** @type {(CSSStyleDeclaration|null)} */
            var aggregateStyle = aggregate.style;
            nodeLabels[lab.id] = nlbs;
            wrapper.appendChild(label);
            wrapper.appendChild(aggregate);
            if (!config.showLabels(lab.name, acumLeft, lab)) {
              /** @type {string} */
              labelStyle.display = "none";
            }
            if (!config.showAggregates(lab.name, acumLeft, lab)) {
              /** @type {string} */
              aggregateStyle.display = "none";
            }
            /** @type {string} */
            wrapperStyle.position = "relative";
            /** @type {string} */
            wrapperStyle.overflow = "visible";
            /** @type {string} */
            wrapperStyle.fontSize = labelConf.size + "px";
            wrapperStyle.fontFamily = labelConf.family;
            wrapperStyle.color = labelConf.color;
            /** @type {string} */
            wrapperStyle.textAlign = "center";
            /** @type {string} */
            aggregateStyle.position = labelStyle.position = "absolute";
            adj.style.width = lab.getData("width") + "px";
            adj.style.height = lab.getData("height") + "px";
            /** @type {string} */
            aggregateStyle.left = labelStyle.left = "0px";
            label.innerHTML = lab.name;
            adj.appendChild(wrapper);
          },
          /**
           * @param {?} adj
           * @param {?} lab
           * @return {undefined}
           */
          onPlaceLabel : function(adj, lab) {
            if (!nodeLabels[lab.id]) {
              return;
            }
            var labels = nodeLabels[lab.id];
            var style = labels.wrapper.style;
            var labelStyle = labels.label.style;
            var aggregateStyle = labels.aggregate.style;
            /** @type {boolean} */
            var grouped = config.type.split(":")[0] == "grouped";
            /** @type {boolean} */
            var horz = config.orientation == "horizontal";
            var dimArray = lab.getData("dimArray");
            var valArray = lab.getData("valueArray");
            var w = grouped && horz ? Math.max.apply(null, dimArray) : lab.getData("width");
            var height = grouped && !horz ? Math.max.apply(null, dimArray) : lab.getData("height");
            /** @type {number} */
            var font = parseInt(style.fontSize, 10);
            var styleDeclaration = adj.style;
            if (dimArray && valArray) {
              /** @type {string} */
              style.width = aggregateStyle.width = labelStyle.width = adj.style.width = w + "px";
              /** @type {number} */
              var i = 0;
              var l = valArray.length;
              /** @type {number} */
              var acum = 0;
              for (;i < l;i++) {
                if (dimArray[i] > 0) {
                  acum += valArray[i];
                }
              }
              if (config.showLabels(lab.name, acum, lab)) {
                /** @type {string} */
                labelStyle.display = "";
              } else {
                /** @type {string} */
                labelStyle.display = "none";
              }
              var aggValue = config.showAggregates(lab.name, acum, lab);
              if (aggValue !== false) {
                /** @type {string} */
                aggregateStyle.display = "";
              } else {
                /** @type {string} */
                aggregateStyle.display = "none";
              }
              if (config.orientation == "horizontal") {
                /** @type {string} */
                aggregateStyle.textAlign = "right";
                /** @type {string} */
                labelStyle.textAlign = "left";
                /** @type {string} */
                labelStyle.textIndex = aggregateStyle.textIndent = config.labelOffset + "px";
                /** @type {string} */
                aggregateStyle.top = labelStyle.top = (height - font) / 2 + "px";
                /** @type {string} */
                adj.style.height = style.height = height + "px";
              } else {
                /** @type {string} */
                aggregateStyle.top = -font - config.labelOffset + "px";
                /** @type {string} */
                labelStyle.top = config.labelOffset + height + "px";
                /** @type {string} */
                adj.style.top = parseInt(adj.style.top, 10) - height + "px";
                /** @type {string} */
                adj.style.height = style.height = height + "px";
              }
              labels.aggregate.innerHTML = aggValue !== true ? aggValue : acum;
            }
          }
        });
        var $cont = delegate.canvas.getSize();
        var margin = config.Margin;
        if (horz) {
          /** @type {number} */
          delegate.config.offsetX = $cont.width / 2 - margin.left - (config.showLabels && config.labelOffset + config.Label.size);
          /** @type {number} */
          delegate.config.offsetY = (margin.bottom - margin.top) / 2;
        } else {
          delegate.config.offsetY = -$cont.height / 2 + margin.bottom + (config.showLabels && config.labelOffset + config.Label.size);
          /** @type {number} */
          delegate.config.offsetX = (margin.right - margin.left) / 2;
        }
        this.delegate = delegate;
        this.canvas = this.delegate.canvas;
      },
      /**
       * @param {Object} json
       * @return {undefined}
       */
      loadJSON : function(json) {
        if (this.busy) {
          return;
        }
        /** @type {boolean} */
        this.busy = true;
        /** @type {number} */
        var prefix = $.time();
        /** @type {Array} */
        var ch = [];
        var delegate = this.delegate;
        var name = $.splat(json.label);
        var color = $.splat(json.color || this.colors);
        var config = this.config;
        /** @type {boolean} */
        var gradient = !!config.type.split(":")[1];
        var animate = config.animate;
        /** @type {boolean} */
        var isH = config.orientation == "horizontal";
        var that = this;
        /** @type {number} */
        var i = 0;
        var values = json.values;
        var valuesLen = values.length;
        for (;i < valuesLen;i++) {
          var value = values[i];
          var valArray = $.splat(values[i].values);
          /** @type {number} */
          var F = 0;
          ch.push({
            id : prefix + value.label,
            name : value.label,
            data : {
              value : valArray,
              "$valueArray" : valArray,
              "$colorArray" : color,
              "$stringArray" : name,
              "$gradient" : gradient,
              "$config" : config
            },
            children : []
          });
        }
        var root = {
          id : prefix + "$root",
          name : "",
          data : {
            "$type" : "none",
            "$width" : 1,
            "$height" : 1
          },
          children : ch
        };
        delegate.loadJSON(root);
        this.normalizeDims();
        delegate.compute();
        delegate.select(delegate.root);
        if (animate) {
          if (isH) {
            delegate.fx.animate({
              modes : ["node-property:width:dimArray"],
              duration : 1500,
              /**
               * @return {undefined}
               */
              onComplete : function() {
                /** @type {boolean} */
                that.busy = false;
              }
            });
          } else {
            delegate.fx.animate({
              modes : ["node-property:height:dimArray"],
              duration : 1500,
              /**
               * @return {undefined}
               */
              onComplete : function() {
                /** @type {boolean} */
                that.busy = false;
              }
            });
          }
        } else {
          /** @type {boolean} */
          this.busy = false;
        }
      },
      /**
       * @param {Object} json
       * @param {Object} onComplete
       * @return {undefined}
       */
      updateJSON : function(json, onComplete) {
        if (this.busy) {
          return;
        }
        /** @type {boolean} */
        this.busy = true;
        this.select(false, false, false);
        var delegate = this.delegate;
        var graph = delegate.graph;
        var attributes = json.values;
        var animate = this.config.animate;
        var that = this;
        /** @type {boolean} */
        var isH = this.config.orientation == "horizontal";
        $.each(attributes, function(v) {
          var n = graph.getByName(v.label);
          if (n) {
            n.setData("valueArray", $.splat(v.values));
            if (json.label) {
              n.setData("stringArray", $.splat(json.label));
            }
          }
        });
        this.normalizeDims();
        delegate.compute();
        delegate.select(delegate.root);
        if (animate) {
          if (isH) {
            delegate.fx.animate({
              modes : ["node-property:width:dimArray"],
              duration : 1500,
              /**
               * @return {undefined}
               */
              onComplete : function() {
                /** @type {boolean} */
                that.busy = false;
                if (onComplete) {
                  onComplete.onComplete();
                }
              }
            });
          } else {
            delegate.fx.animate({
              modes : ["node-property:height:dimArray"],
              duration : 1500,
              /**
               * @return {undefined}
               */
              onComplete : function() {
                /** @type {boolean} */
                that.busy = false;
                if (onComplete) {
                  onComplete.onComplete();
                }
              }
            });
          }
        }
      },
      /**
       * @param {string} id
       * @param {boolean} lab
       * @return {undefined}
       */
      select : function(id, lab) {
        if (!this.config.hoveredColor) {
          return;
        }
        var s = this.selected;
        if (s.id != id || s.name != lab) {
          /** @type {string} */
          s.id = id;
          /** @type {boolean} */
          s.name = lab;
          s.color = this.config.hoveredColor;
          this.delegate.graph.eachNode(function(n) {
            if (id == n.id) {
              n.setData("border", s);
            } else {
              n.setData("border", false);
            }
          });
          this.delegate.plot();
        }
      },
      /**
       * @return {?}
       */
      getLegend : function() {
        var legend = {};
        var n;
        this.delegate.graph.getNode(this.delegate.root).eachAdjacency(function(adj) {
          n = adj.nodeTo;
        });
        var colors = n.getData("colorArray");
        var colorsLen = colors.length;
        $.each(n.getData("stringArray"), function(s, i) {
          legend[s] = colors[i % colorsLen];
        });
        return legend;
      },
      /**
       * @return {?}
       */
      getMaxValue : function() {
        /** @type {number} */
        var maxValue = 0;
        /** @type {boolean} */
        var stacked = this.config.type.split(":")[0] == "stacked";
        this.delegate.graph.eachNode(function(n) {
          var attributes = n.getData("valueArray");
          /** @type {number} */
          var acum = 0;
          if (!attributes) {
            return;
          }
          if (stacked) {
            $.each(attributes, function(v) {
              acum += +v;
            });
          } else {
            /** @type {number} */
            acum = Math.max.apply(null, attributes);
          }
          maxValue = maxValue > acum ? maxValue : acum;
        });
        return maxValue;
      },
      /**
       * @param {string} type
       * @return {undefined}
       */
      setBarType : function(type) {
        /** @type {string} */
        this.config.type = type;
        this.delegate.config.Node.type = "barchart-" + type.split(":")[0];
      },
      /**
       * @return {undefined}
       */
      normalizeDims : function() {
        var root = this.delegate.graph.getNode(this.delegate.root);
        /** @type {number} */
        var l = 0;
        root.eachAdjacency(function() {
          l++;
        });
        var maxValue = this.getMaxValue() || 1;
        var size = this.delegate.canvas.getSize();
        var config = this.config;
        var margin = config.Margin;
        var marginWidth = margin.left + margin.right;
        var marginHeight = margin.top + margin.bottom;
        /** @type {boolean} */
        var horz = config.orientation == "horizontal";
        /** @type {number} */
        var fixedDim = (size[horz ? "height" : "width"] - (horz ? marginHeight : marginWidth) - (l - 1) * config.barsOffset) / l;
        var animate = config.animate;
        /** @type {number} */
        var height = size[horz ? "width" : "height"] - (horz ? marginWidth : marginHeight) - (!horz && (config.showAggregates && config.Label.size + config.labelOffset)) - (config.showLabels && config.Label.size + config.labelOffset);
        /** @type {string} */
        var dim1 = horz ? "height" : "width";
        /** @type {string} */
        var dim2 = horz ? "width" : "height";
        this.delegate.graph.eachNode(function(n) {
          /** @type {number} */
          var acum = 0;
          /** @type {Array} */
          var animateValue = [];
          $.each(n.getData("valueArray"), function(v) {
            acum += +v;
            animateValue.push(0);
          });
          n.setData(dim1, fixedDim);
          if (animate) {
            n.setData(dim2, acum * height / maxValue, "end");
            n.setData("dimArray", $.map(n.getData("valueArray"), function(n) {
              return n * height / maxValue;
            }), "end");
            var dimArray = n.getData("dimArray");
            if (!dimArray) {
              n.setData("dimArray", animateValue);
            }
          } else {
            n.setData(dim2, acum * height / maxValue);
            n.setData("dimArray", $.map(n.getData("valueArray"), function(n) {
              return n * height / maxValue;
            }));
          }
        });
      }
    });
    Options.PieChart = {
      $extend : true,
      animate : true,
      offset : 25,
      sliceOffset : 0,
      labelOffset : 3,
      type : "stacked",
      hoveredColor : "#9fd4ff",
      Events : {
        enable : false,
        /** @type {function (): undefined} */
        onClick : $.empty
      },
      Tips : {
        enable : false,
        /** @type {function (): undefined} */
        onShow : $.empty,
        /** @type {function (): undefined} */
        onHide : $.empty
      },
      showLabels : true,
      resizeLabels : false,
      updateHeights : false
    };
    Layout.Radial = new Class({
      /**
       * @param {?} adj
       * @return {undefined}
       */
      compute : function(adj) {
        var lab = $.splat(adj || ["current", "start", "end"]);
        column.compute(this.graph, lab, this.config);
        this.graph.computeLevels(this.root, 0, "ignore");
        var lengthFunc = this.createLevelDistanceFunc();
        this.computeAngularWidths(lab);
        this.computePositions(lab, lengthFunc);
      },
      /**
       * @param {?} node
       * @param {Object} getLength
       * @return {undefined}
       */
      computePositions : function(node, getLength) {
        var employees = node;
        var graph = this.graph;
        var root = graph.getNode(this.root);
        var parent = this.parent;
        var config = this.config;
        /** @type {number} */
        var i = 0;
        var l = employees.length;
        for (;i < l;i++) {
          var pi = employees[i];
          root.setPos($P(0, 0), pi);
          root.setData("span", Math.PI * 2, pi);
        }
        root.angleSpan = {
          begin : 0,
          end : 2 * Math.PI
        };
        graph.eachBFS(this.root, function(elem) {
          /** @type {number} */
          var angleSpan = elem.angleSpan.end - elem.angleSpan.begin;
          var angleInit = elem.angleSpan.begin;
          var len = getLength(elem);
          /** @type {number} */
          var totalAngularWidths = 0;
          /** @type {Array} */
          var subnodes = [];
          var maxDim = {};
          elem.eachSubnode(function(sib) {
            totalAngularWidths += sib._treeAngularWidth;
            /** @type {number} */
            var i = 0;
            var l = employees.length;
            for (;i < l;i++) {
              var pi = employees[i];
              var dim = sib.getData("dim", pi);
              maxDim[pi] = pi in maxDim ? dim > maxDim[pi] ? dim : maxDim[pi] : dim;
            }
            subnodes.push(sib);
          }, "ignore");
          if (parent && (parent.id == elem.id && (subnodes.length > 0 && subnodes[0].dist))) {
            subnodes.sort(function(a, b) {
              return(a.dist >= b.dist) - (a.dist <= b.dist);
            });
          }
          /** @type {number} */
          var i = 0;
          /** @type {number} */
          var valuesLen = subnodes.length;
          for (;i < valuesLen;i++) {
            var child = subnodes[i];
            if (!child._flag) {
              /** @type {number} */
              var angleProportion = child._treeAngularWidth / totalAngularWidths * angleSpan;
              var theta = angleInit + angleProportion / 2;
              /** @type {number} */
              var padIndex = 0;
              var l = employees.length;
              for (;padIndex < l;padIndex++) {
                var pi = employees[padIndex];
                child.setPos($P(theta, len), pi);
                child.setData("span", angleProportion, pi);
                child.setData("dim-quotient", child.getData("dim", pi) / maxDim[pi], pi);
              }
              child.angleSpan = {
                begin : angleInit,
                end : angleInit + angleProportion
              };
              angleInit += angleProportion;
            }
          }
        }, "ignore");
      },
      /**
       * @param {?} prop
       * @return {undefined}
       */
      setAngularWidthForNodes : function(prop) {
        this.graph.eachBFS(this.root, function(elem, i) {
          var diamValue = elem.getData("angularWidth", prop[0]) || 5;
          /** @type {number} */
          elem._angularWidth = diamValue / i;
        }, "ignore");
      },
      /**
       * @return {undefined}
       */
      setSubtreesAngularWidth : function() {
        var paragraph = this;
        this.graph.eachNode(function(child) {
          paragraph.setSubtreeAngularWidth(child);
        }, "ignore");
      },
      /**
       * @param {?} elem
       * @return {undefined}
       */
      setSubtreeAngularWidth : function(elem) {
        var paragraph = this;
        var nodeAW = elem._angularWidth;
        /** @type {number} */
        var sumAW = 0;
        elem.eachSubnode(function(child) {
          paragraph.setSubtreeAngularWidth(child);
          sumAW += child._treeAngularWidth;
        }, "ignore");
        /** @type {number} */
        elem._treeAngularWidth = Math.max(nodeAW, sumAW);
      },
      /**
       * @param {?} prop
       * @return {undefined}
       */
      computeAngularWidths : function(prop) {
        this.setAngularWidthForNodes(prop);
        this.setSubtreesAngularWidth();
      }
    });
    $jit.Sunburst = new Class({
      Implements : [valid, Extras, Layout.Radial],
      /**
       * @param {?} controller
       * @return {undefined}
       */
      initialize : function(controller) {
        var $Sunburst = $jit.Sunburst;
        var config = {
          interpolation : "linear",
          levelDistance : 100,
          Node : {
            type : "multipie",
            height : 0
          },
          Edge : {
            type : "none"
          },
          Label : {
            textAlign : "start",
            textBaseline : "middle"
          }
        };
        this.controller = this.config = $.merge(Options("Canvas", "Node", "Edge", "Fx", "Tips", "NodeStyles", "Events", "Navigation", "Controller", "Label"), config, controller);
        var canvasConfig = this.config;
        if (canvasConfig.useCanvas) {
          this.canvas = canvasConfig.useCanvas;
          /** @type {string} */
          this.config.labelContainer = this.canvas.id + "-label";
        } else {
          if (canvasConfig.background) {
            canvasConfig.background = $.merge({
              type : "Circles"
            }, canvasConfig.background);
          }
          this.canvas = new Canvas(this, canvasConfig);
          /** @type {string} */
          this.config.labelContainer = (typeof canvasConfig.injectInto == "string" ? canvasConfig.injectInto : canvasConfig.injectInto.id) + "-label";
        }
        this.graphOptions = {
          /** @type {function (number, number): undefined} */
          klass : Transform,
          Node : {
            selected : false,
            exist : true,
            drawn : true
          }
        };
        this.graph = new Graph(this.graphOptions, this.config.Node, this.config.Edge);
        this.labels = new $Sunburst.Label[canvasConfig.Label.type](this);
        this.fx = new $Sunburst.Plot(this, $Sunburst);
        this.op = new $Sunburst.Op(this);
        /** @type {null} */
        this.json = null;
        /** @type {null} */
        this.root = null;
        /** @type {null} */
        this.rotated = null;
        /** @type {boolean} */
        this.busy = false;
        this.initializeExtras();
      },
      /**
       * @return {?}
       */
      createLevelDistanceFunc : function() {
        var ld = this.config.levelDistance;
        return function(node) {
          return(node._depth + 1) * ld;
        };
      },
      /**
       * @return {undefined}
       */
      refresh : function() {
        this.compute();
        this.plot();
      },
      /**
       * @return {undefined}
       */
      reposition : function() {
        this.compute("end");
      },
      /**
       * @param {number} node
       * @param {string} method
       * @param {Object} opt
       * @return {undefined}
       */
      rotate : function(node, method, opt) {
        var theta = node.getPos(opt.property || "current").getp(true).theta;
        /** @type {number} */
        this.rotated = node;
        this.rotateAngle(-theta, method, opt);
      },
      /**
       * @param {number} theta
       * @param {string} method
       * @param {Object} opt
       * @return {undefined}
       */
      rotateAngle : function(theta, method, opt) {
        var z = this;
        var options = $.merge(this.config, opt || {}, {
          modes : ["polar"]
        });
        var prop = opt.property || (method === "animate" ? "end" : "current");
        if (method === "animate") {
          this.fx.animation.pause();
        }
        this.graph.eachNode(function(obj) {
          var p = obj.getPos(prop);
          p.theta += theta;
          if (p.theta < 0) {
            p.theta += Math.PI * 2;
          }
        });
        if (method == "animate") {
          this.fx.animate(options);
        } else {
          if (method == "replot") {
            this.fx.plot();
            /** @type {boolean} */
            this.busy = false;
          }
        }
      },
      /**
       * @return {undefined}
       */
      plot : function() {
        this.fx.plot();
      }
    });
    /** @type {boolean} */
    $jit.Sunburst.$extend = true;
    (function(Hypertree) {
      Hypertree.Op = new Class({
        Implements : Graph.Op
      });
      Hypertree.Plot = new Class({
        Implements : Graph.Plot
      });
      Hypertree.Label = {};
      Hypertree.Label.Native = new Class({
        Implements : Graph.Label.Native,
        /**
         * @param {?} viz
         * @return {undefined}
         */
        initialize : function(viz) {
          this.viz = viz;
          this.label = viz.config.Label;
          this.config = viz.config;
        },
        /**
         * @param {?} canvas
         * @param {Object} node
         * @param {?} controller
         * @return {undefined}
         */
        renderLabel : function(canvas, node, controller) {
          var span = node.getData("span");
          if (span < Math.PI / 2 && Math.tan(span) * this.config.levelDistance * node._depth < 10) {
            return;
          }
          var ctx = canvas.getCtx();
          var innerSize = ctx.measureText(node.name);
          if (node.id == this.viz.root) {
            /** @type {number} */
            var x = -innerSize.width / 2;
            /** @type {number} */
            var y = 0;
            /** @type {number} */
            var thetap = 0;
            /** @type {number} */
            var ld = 0;
          } else {
            /** @type {number} */
            var indent = 5;
            /** @type {number} */
            ld = controller.levelDistance - indent;
            var clone = node.pos.clone();
            clone.rho += indent;
            var p = clone.getp(true);
            var scroll = clone.getc(true);
            x = scroll.x;
            y = scroll.y;
            /** @type {number} */
            var pi = Math.PI;
            /** @type {boolean} */
            var cond = p.theta > pi / 2 && p.theta < 3 * pi / 2;
            thetap = cond ? p.theta + pi : p.theta;
            if (cond) {
              x -= Math.abs(Math.cos(p.theta) * innerSize.width);
              y += Math.sin(p.theta) * innerSize.width;
            } else {
              if (node.id == this.viz.root) {
                x -= innerSize.width / 2;
              }
            }
          }
          ctx.save();
          ctx.translate(x, y);
          ctx.rotate(thetap);
          ctx.fillText(node.name, 0, 0);
          ctx.restore();
        }
      });
      Hypertree.Label.SVG = new Class({
        Implements : Graph.Label.SVG,
        /**
         * @param {?} viz
         * @return {undefined}
         */
        initialize : function(viz) {
          this.viz = viz;
        },
        /**
         * @param {?} from
         * @param {?} lab
         * @param {?} options
         * @return {undefined}
         */
        placeLabel : function(from, lab, options) {
          var offsetCoordinate = lab.pos.getc(true);
          var viz = this.viz;
          var canvas = this.viz.canvas;
          var $cont = canvas.getSize();
          var tl = {
            x : Math.round(offsetCoordinate.x + $cont.width / 2),
            y : Math.round(offsetCoordinate.y + $cont.height / 2)
          };
          from.setAttribute("x", tl.x);
          from.setAttribute("y", tl.y);
          var bb = from.getBBox();
          if (bb) {
            var x = from.getAttribute("x");
            var y = from.getAttribute("y");
            var p = lab.pos.getp(true);
            /** @type {number} */
            var pi = Math.PI;
            /** @type {boolean} */
            var cond = p.theta > pi / 2 && p.theta < 3 * pi / 2;
            if (cond) {
              from.setAttribute("x", x - bb.width);
              from.setAttribute("y", y - bb.height);
            } else {
              if (lab.id == viz.root) {
                from.setAttribute("x", x - bb.width / 2);
              }
            }
            var thetap = cond ? p.theta + pi : p.theta;
            if (lab._depth) {
              from.setAttribute("transform", "rotate(" + thetap * 360 / (2 * pi) + " " + x + " " + y + ")");
            }
          }
          options.onPlaceLabel(from, lab);
        }
      });
      Hypertree.Label.HTML = new Class({
        Implements : Graph.Label.HTML,
        /**
         * @param {?} viz
         * @return {undefined}
         */
        initialize : function(viz) {
          this.viz = viz;
        },
        /**
         * @param {?} from
         * @param {?} lab
         * @param {?} options
         * @return {undefined}
         */
        placeLabel : function(from, lab, options) {
          var pos = lab.pos.clone();
          var canvas = this.viz.canvas;
          var height = lab.getData("height");
          /** @type {number} */
          var ldist = (height || lab._depth == 0 ? height : this.viz.config.levelDistance) / 2;
          var $cont = canvas.getSize();
          pos.rho += ldist;
          pos = pos.getc(true);
          var labelPos = {
            x : Math.round(pos.x + $cont.width / 2),
            y : Math.round(pos.y + $cont.height / 2)
          };
          var style = from.style;
          /** @type {string} */
          style.left = labelPos.x + "px";
          /** @type {string} */
          style.top = labelPos.y + "px";
          /** @type {string} */
          style.display = this.fitsInCanvas(labelPos, canvas) ? "" : "none";
          options.onPlaceLabel(from, lab);
        }
      });
      Hypertree.Plot.NodeTypes = new Class({
        none : {
          /** @type {function (): undefined} */
          render : $.empty,
          contains : $.lambda(false),
          /**
           * @param {Object} node
           * @param {?} pos
           * @return {?}
           */
          anglecontains : function(node, pos) {
            /** @type {number} */
            var span = node.getData("span") / 2;
            var theta = node.pos.theta;
            /** @type {number} */
            var begin = theta - span;
            var end = theta + span;
            if (begin < 0) {
              begin += Math.PI * 2;
            }
            /** @type {number} */
            var atan = Math.atan2(pos.y, pos.x);
            if (atan < 0) {
              atan += Math.PI * 2;
            }
            if (begin > end) {
              return atan > begin && atan <= Math.PI * 2 || atan < end;
            } else {
              return atan > begin && atan < end;
            }
          }
        },
        pie : {
          /**
           * @param {?} adj
           * @param {?} lab
           * @return {undefined}
           */
          render : function(adj, lab) {
            /** @type {number} */
            var span = adj.getData("span") / 2;
            var theta = adj.pos.theta;
            /** @type {number} */
            var begin = theta - span;
            var end = theta + span;
            var polarNode = adj.pos.getp(true);
            var polar = new Transform(polarNode.rho, begin);
            var p4coord = polar.getc(true);
            polar.theta = end;
            var endPoint = polar.getc(true);
            var ctx = lab.getCtx();
            ctx.beginPath();
            ctx.moveTo(0, 0);
            ctx.lineTo(p4coord.x, p4coord.y);
            ctx.moveTo(0, 0);
            ctx.lineTo(endPoint.x, endPoint.y);
            ctx.moveTo(0, 0);
            ctx.arc(0, 0, polarNode.rho * adj.getData("dim-quotient"), begin, end, false);
            ctx.fill();
          },
          /**
           * @param {?} opt_attributes
           * @param {?} value
           * @return {?}
           */
          contains : function(opt_attributes, value) {
            if (this.nodeTypes.none.anglecontains.call(this, opt_attributes, value)) {
              /** @type {number} */
              var rho = Math.sqrt(value.x * value.x + value.y * value.y);
              var ld = this.config.levelDistance;
              var d = opt_attributes._depth;
              return rho <= ld * d;
            }
            return false;
          }
        },
        multipie : {
          /**
           * @param {?} adj
           * @param {?} lab
           * @return {undefined}
           */
          render : function(adj, lab) {
            var height = adj.getData("height");
            var ldist = height ? height : this.config.levelDistance;
            /** @type {number} */
            var span = adj.getData("span") / 2;
            var theta = adj.pos.theta;
            /** @type {number} */
            var begin = theta - span;
            var end = theta + span;
            var polarNode = adj.pos.getp(true);
            var polar = new Transform(polarNode.rho, begin);
            var s1 = polar.getc(true);
            polar.theta = end;
            var pt1 = polar.getc(true);
            polar.rho += ldist;
            var p4coord = polar.getc(true);
            /** @type {number} */
            polar.theta = begin;
            var endPoint = polar.getc(true);
            var ctx = lab.getCtx();
            ctx.moveTo(0, 0);
            ctx.beginPath();
            ctx.arc(0, 0, polarNode.rho, begin, end, false);
            ctx.arc(0, 0, polarNode.rho + ldist, end, begin, true);
            ctx.moveTo(s1.x, s1.y);
            ctx.lineTo(endPoint.x, endPoint.y);
            ctx.moveTo(pt1.x, pt1.y);
            ctx.lineTo(p4coord.x, p4coord.y);
            ctx.fill();
            if (adj.collapsed) {
              ctx.save();
              /** @type {number} */
              ctx.lineWidth = 2;
              ctx.moveTo(0, 0);
              ctx.beginPath();
              ctx.arc(0, 0, polarNode.rho + ldist + 5, end - 0.01, begin + 0.01, true);
              ctx.stroke();
              ctx.restore();
            }
          },
          /**
           * @param {?} opt_attributes
           * @param {?} value
           * @return {?}
           */
          contains : function(opt_attributes, value) {
            if (this.nodeTypes.none.anglecontains.call(this, opt_attributes, value)) {
              /** @type {number} */
              var rho = Math.sqrt(value.x * value.x + value.y * value.y);
              var height = opt_attributes.getData("height");
              var ldist = height ? height : this.config.levelDistance;
              var ld = this.config.levelDistance;
              var d = opt_attributes._depth;
              return rho >= ld * d && rho <= ld * d + ldist;
            }
            return false;
          }
        },
        "gradient-multipie" : {
          /**
           * @param {?} adj
           * @param {?} lab
           * @return {undefined}
           */
          render : function(adj, lab) {
            var ctx = lab.getCtx();
            var height = adj.getData("height");
            var ldist = height ? height : this.config.levelDistance;
            var radialGradient = ctx.createRadialGradient(0, 0, adj.getPos().rho, 0, 0, adj.getPos().rho + ldist);
            var attributes = $.hexToRgb(adj.getData("color"));
            /** @type {Array} */
            var ans = [];
            $.each(attributes, function(i) {
              ans.push(parseInt(i * 0.5, 10));
            });
            var endColor = $.rgbToHex(ans);
            radialGradient.addColorStop(0, endColor);
            radialGradient.addColorStop(1, adj.getData("color"));
            ctx.fillStyle = radialGradient;
            this.nodeTypes.multipie.render.call(this, adj, lab);
          },
          /**
           * @param {?} opt_attributes
           * @param {?} value
           * @return {?}
           */
          contains : function(opt_attributes, value) {
            return this.nodeTypes.multipie.contains.call(this, opt_attributes, value);
          }
        },
        "gradient-pie" : {
          /**
           * @param {?} adj
           * @param {?} lab
           * @return {undefined}
           */
          render : function(adj, lab) {
            var ctx = lab.getCtx();
            var radialGradient = ctx.createRadialGradient(0, 0, 0, 0, 0, adj.getPos().rho);
            var attributes = $.hexToRgb(adj.getData("color"));
            /** @type {Array} */
            var ans = [];
            $.each(attributes, function(i) {
              ans.push(parseInt(i * 0.5, 10));
            });
            var endColor = $.rgbToHex(ans);
            radialGradient.addColorStop(1, endColor);
            radialGradient.addColorStop(0, adj.getData("color"));
            ctx.fillStyle = radialGradient;
            this.nodeTypes.pie.render.call(this, adj, lab);
          },
          /**
           * @param {?} opt_attributes
           * @param {?} value
           * @return {?}
           */
          contains : function(opt_attributes, value) {
            return this.nodeTypes.pie.contains.call(this, opt_attributes, value);
          }
        }
      });
      Hypertree.Plot.EdgeTypes = new Class({
        /** @type {function (): undefined} */
        none : $.empty,
        line : {
          /**
           * @param {?} adj
           * @param {?} type
           * @return {undefined}
           */
          render : function(adj, type) {
            var from = adj.nodeFrom.pos.getc(true);
            var lab = adj.nodeTo.pos.getc(true);
            this.edgeHelper.line.render(from, lab, type);
          },
          /**
           * @param {?} opt_attributes
           * @param {?} value
           * @return {?}
           */
          contains : function(opt_attributes, value) {
            var attributes = opt_attributes.nodeFrom.pos.getc(true);
            var pdataOld = opt_attributes.nodeTo.pos.getc(true);
            return this.edgeHelper.line.contains(attributes, pdataOld, value, this.edge.epsilon);
          }
        },
        arrow : {
          /**
           * @param {?} adj
           * @param {?} type
           * @return {undefined}
           */
          render : function(adj, type) {
            var from = adj.nodeFrom.pos.getc(true);
            var lab = adj.nodeTo.pos.getc(true);
            var qualifier = adj.getData("dim");
            var direction = adj.data.$direction;
            var cycle = direction && (direction.length > 1 && direction[0] != adj.nodeFrom.id);
            this.edgeHelper.arrow.render(from, lab, qualifier, cycle, type);
          },
          /**
           * @param {?} opt_attributes
           * @param {?} value
           * @return {?}
           */
          contains : function(opt_attributes, value) {
            var attributes = opt_attributes.nodeFrom.pos.getc(true);
            var pdataOld = opt_attributes.nodeTo.pos.getc(true);
            return this.edgeHelper.arrow.contains(attributes, pdataOld, value, this.edge.epsilon);
          }
        },
        hyperline : {
          /**
           * @param {?} adj
           * @param {?} lab
           * @return {undefined}
           */
          render : function(adj, lab) {
            var p = adj.nodeFrom.pos.getc();
            var Vec3 = adj.nodeTo.pos.getc();
            /** @type {number} */
            var qualifier = Math.max(p.norm(), Vec3.norm());
            this.edgeHelper.hyperline.render(p.$scale(1 / qualifier), Vec3.$scale(1 / qualifier), qualifier, lab);
          },
          contains : $.lambda(false)
        }
      });
    })($jit.Sunburst);
    $jit.Sunburst.Plot.NodeTypes.implement({
      "piechart-stacked" : {
        /**
         * @param {?} adj
         * @param {?} lab
         * @return {undefined}
         */
        render : function(adj, lab) {
          var T = adj.pos.getp(true);
          var dimArray = adj.getData("dimArray");
          var valueArray = adj.getData("valueArray");
          var colorArray = adj.getData("colorArray");
          var colorLength = colorArray.length;
          var stringArray = adj.getData("stringArray");
          /** @type {number} */
          var span = adj.getData("span") / 2;
          var theta = adj.pos.theta;
          /** @type {number} */
          var begin = theta - span;
          var end = theta + span;
          var polar = new Transform;
          var ctx = lab.getCtx();
          var opt = {};
          var gradient = adj.getData("gradient");
          var border = adj.getData("border");
          var config = adj.getData("config");
          var showLabels = config.showLabels;
          var resizeLabels = config.resizeLabels;
          var label = config.Label;
          /** @type {number} */
          var cx = config.sliceOffset * Math.cos((begin + end) / 2);
          /** @type {number} */
          var cy = config.sliceOffset * Math.sin((begin + end) / 2);
          if (colorArray && (dimArray && stringArray)) {
            /** @type {number} */
            var i = 0;
            var l = dimArray.length;
            /** @type {number} */
            var acum = 0;
            /** @type {number} */
            var X = 0;
            for (;i < l;i++) {
              var dimi = dimArray[i];
              var color = colorArray[i % colorLength];
              if (dimi <= 0) {
                continue;
              }
              ctx.fillStyle = ctx.strokeStyle = color;
              if (gradient && dimi) {
                var grad = ctx.createRadialGradient(cx, cy, acum + config.sliceOffset, cx, cy, acum + dimi + config.sliceOffset);
                var a = $.hexToRgb(color);
                var ans = $.map(a, function(dataAndEvents) {
                  return dataAndEvents * 0.8 >> 0;
                });
                var endColor = $.rgbToHex(ans);
                grad.addColorStop(0, color);
                grad.addColorStop(0.5, color);
                grad.addColorStop(1, endColor);
                ctx.fillStyle = grad;
              }
              polar.rho = acum + config.sliceOffset;
              /** @type {number} */
              polar.theta = begin;
              var ah = polar.getc(true);
              polar.theta = end;
              var O = polar.getc(true);
              polar.rho += dimi;
              var aj = polar.getc(true);
              /** @type {number} */
              polar.theta = begin;
              var Q = polar.getc(true);
              ctx.beginPath();
              ctx.arc(cx, cy, acum + 0.01, begin, end, false);
              ctx.arc(cx, cy, acum + dimi + 0.01, end, begin, true);
              ctx.fill();
              if (border && border.name == stringArray[i]) {
                opt.acum = acum;
                opt.dimValue = dimArray[i];
                /** @type {number} */
                opt.begin = begin;
                opt.end = end;
              }
              acum += dimi || 0;
              X += valueArray[i] || 0;
            }
            if (border) {
              ctx.save();
              /** @type {string} */
              ctx.globalCompositeOperation = "source-over";
              /** @type {number} */
              ctx.lineWidth = 2;
              ctx.strokeStyle = border.color;
              /** @type {number} */
              var aa = begin < end ? 1 : -1;
              ctx.beginPath();
              ctx.arc(cx, cy, opt.acum + 0.01 + 1, opt.begin, opt.end, false);
              ctx.arc(cx, cy, opt.acum + opt.dimValue + 0.01 - 1, opt.end, opt.begin, true);
              ctx.closePath();
              ctx.stroke();
              ctx.restore();
            }
            if (showLabels && label.type == "Native") {
              ctx.save();
              ctx.fillStyle = ctx.strokeStyle = label.color;
              var scale = resizeLabels ? adj.getData("normalizedDim") : 1;
              /** @type {number} */
              var fontSize = label.size * scale >> 0;
              /** @type {number} */
              fontSize = fontSize < +resizeLabels ? +resizeLabels : fontSize;
              /** @type {string} */
              ctx.font = label.style + " " + fontSize + "px " + label.family;
              /** @type {string} */
              ctx.textBaseline = "middle";
              /** @type {string} */
              ctx.textAlign = "center";
              polar.rho = acum + config.labelOffset + config.sliceOffset;
              polar.theta = adj.pos.theta;
              var cart = polar.getc(true);
              ctx.fillText(adj.name, cart.x, cart.y);
              ctx.restore();
            }
          }
        },
        /**
         * @param {?} opt_attributes
         * @param {?} value
         * @return {?}
         */
        contains : function(opt_attributes, value) {
          if (this.nodeTypes.none.anglecontains.call(this, opt_attributes, value)) {
            /** @type {number} */
            var rho = Math.sqrt(value.x * value.x + value.y * value.y);
            var ld = this.config.levelDistance;
            var d = opt_attributes._depth;
            var config = opt_attributes.getData("config");
            if (rho <= ld * d + config.sliceOffset) {
              var dimArray = opt_attributes.getData("dimArray");
              /** @type {number} */
              var i = 0;
              var l = dimArray.length;
              var acum = config.sliceOffset;
              for (;i < l;i++) {
                var dimi = dimArray[i];
                if (rho >= acum && rho <= acum + dimi) {
                  return{
                    name : opt_attributes.getData("stringArray")[i],
                    color : opt_attributes.getData("colorArray")[i],
                    value : opt_attributes.getData("valueArray")[i],
                    label : opt_attributes.name
                  };
                }
                acum += dimi;
              }
            }
            return false;
          }
          return false;
        }
      }
    });
    $jit.PieChart = new Class({
      sb : null,
      colors : ["#416D9C", "#70A35E", "#EBB056", "#C74243", "#83548B", "#909291", "#557EAA"],
      selected : {},
      busy : false,
      /**
       * @param {?} controller
       * @return {undefined}
       */
      initialize : function(controller) {
        this.controller = this.config = $.merge(Options("Canvas", "PieChart", "Label"), {
          Label : {
            type : "Native"
          }
        }, controller);
        this.initializeViz();
      },
      /**
       * @return {undefined}
       */
      initializeViz : function() {
        var config = this.config;
        var that = this;
        var nodeType = config.type.split(":")[0];
        var delegate = new $jit.Sunburst({
          injectInto : config.injectInto,
          width : config.width,
          height : config.height,
          useCanvas : config.useCanvas,
          withLabels : config.Label.type != "Native",
          Label : {
            type : config.Label.type
          },
          Node : {
            overridable : true,
            type : "piechart-" + nodeType,
            width : 1,
            height : 1
          },
          Edge : {
            type : "none"
          },
          Tips : {
            enable : config.Tips.enable,
            type : "Native",
            force : true,
            /**
             * @param {?} from
             * @param {?} type
             * @param {?} event
             * @return {undefined}
             */
            onShow : function(from, type, event) {
              var lab = event;
              config.Tips.onShow(from, lab, type);
            }
          },
          Events : {
            enable : true,
            type : "Native",
            /**
             * @param {?} adj
             * @param {?} lab
             * @param {?} selector
             * @return {undefined}
             */
            onClick : function(adj, lab, selector) {
              if (!config.Events.enable) {
                return;
              }
              var from = lab.getContains();
              config.Events.onClick(from, lab, selector);
            },
            /**
             * @param {?} adj
             * @param {?} lab
             * @param {?} event
             * @return {undefined}
             */
            onMouseMove : function(adj, lab, event) {
              if (!config.hoveredColor) {
                return;
              }
              if (adj) {
                var elem = lab.getContains();
                that.select(adj.id, elem.name, elem.index);
              } else {
                that.select(false, false, false);
              }
            }
          },
          /**
           * @param {?} adj
           * @param {?} lab
           * @return {undefined}
           */
          onCreateLabel : function(adj, lab) {
            var labelConf = config.Label;
            if (config.showLabels) {
              var wrapperStyle = adj.style;
              /** @type {string} */
              wrapperStyle.fontSize = labelConf.size + "px";
              wrapperStyle.fontFamily = labelConf.family;
              wrapperStyle.color = labelConf.color;
              /** @type {string} */
              wrapperStyle.textAlign = "center";
              adj.innerHTML = lab.name;
            }
          },
          /**
           * @param {?} adj
           * @param {?} lab
           * @return {undefined}
           */
          onPlaceLabel : function(adj, lab) {
            if (!config.showLabels) {
              return;
            }
            var offsetCoordinate = lab.pos.getp(true);
            var dimArray = lab.getData("dimArray");
            /** @type {number} */
            var span = lab.getData("span") / 2;
            var theta = lab.pos.theta;
            /** @type {number} */
            var begin = theta - span;
            var end = theta + span;
            var polar = new Transform;
            var showLabels = config.showLabels;
            var resizeLabels = config.resizeLabels;
            var label = config.Label;
            if (dimArray) {
              /** @type {number} */
              var i = 0;
              var l = dimArray.length;
              /** @type {number} */
              var acum = 0;
              for (;i < l;i++) {
                acum += dimArray[i];
              }
              var scale = resizeLabels ? lab.getData("normalizedDim") : 1;
              /** @type {number} */
              var fontSize = label.size * scale >> 0;
              /** @type {number} */
              fontSize = fontSize < +resizeLabels ? +resizeLabels : fontSize;
              /** @type {string} */
              adj.style.fontSize = fontSize + "px";
              polar.rho = acum + config.labelOffset + config.sliceOffset;
              /** @type {number} */
              polar.theta = (begin + end) / 2;
              offsetCoordinate = polar.getc(true);
              var $cont = that.canvas.getSize();
              var pos = {
                x : Math.round(offsetCoordinate.x + $cont.width / 2),
                y : Math.round(offsetCoordinate.y + $cont.height / 2)
              };
              /** @type {string} */
              adj.style.left = pos.x + "px";
              /** @type {string} */
              adj.style.top = pos.y + "px";
            }
          }
        });
        var size = delegate.canvas.getSize();
        /** @type {function (...[*]): number} */
        var min = Math.min;
        /** @type {number} */
        delegate.config.levelDistance = min(size.width, size.height) / 2 - config.offset - config.sliceOffset;
        this.delegate = delegate;
        this.canvas = this.delegate.canvas;
        /** @type {string} */
        this.canvas.getCtx().globalCompositeOperation = "lighter";
      },
      /**
       * @param {Object} json
       * @return {undefined}
       */
      loadJSON : function(json) {
        /** @type {number} */
        var prefix = $.time();
        /** @type {Array} */
        var ch = [];
        var delegate = this.delegate;
        var resolveValues = $.splat(json.label);
        var length = resolveValues.length;
        var color = $.splat(json.color || this.colors);
        var colorLength = color.length;
        var config = this.config;
        /** @type {boolean} */
        var gradient = !!config.type.split(":")[1];
        var animate = config.animate;
        /** @type {boolean} */
        var mono = length == 1;
        /** @type {number} */
        var i = 0;
        var values = json.values;
        var valuesLen = values.length;
        for (;i < valuesLen;i++) {
          var val = values[i];
          var valArray = $.splat(val.values);
          ch.push({
            id : prefix + val.label,
            name : val.label,
            data : {
              value : valArray,
              "$valueArray" : valArray,
              "$colorArray" : mono ? $.splat(color[i % colorLength]) : color,
              "$stringArray" : resolveValues,
              "$gradient" : gradient,
              "$config" : config,
              "$angularWidth" : $.reduce(valArray, function(far, near) {
                return far + near;
              })
            },
            children : []
          });
        }
        var root = {
          id : prefix + "$root",
          name : "",
          data : {
            "$type" : "none",
            "$width" : 1,
            "$height" : 1
          },
          children : ch
        };
        delegate.loadJSON(root);
        this.normalizeDims();
        delegate.refresh();
        if (animate) {
          delegate.fx.animate({
            modes : ["node-property:dimArray"],
            duration : 1500
          });
        }
      },
      /**
       * @param {Object} json
       * @param {Object} onComplete
       * @return {undefined}
       */
      updateJSON : function(json, onComplete) {
        if (this.busy) {
          return;
        }
        /** @type {boolean} */
        this.busy = true;
        var delegate = this.delegate;
        var graph = delegate.graph;
        var attributes = json.values;
        var animate = this.config.animate;
        var that = this;
        $.each(attributes, function(v) {
          var n = graph.getByName(v.label);
          var vals = $.splat(v.values);
          if (n) {
            n.setData("valueArray", vals);
            n.setData("angularWidth", $.reduce(vals, function(far, near) {
              return far + near;
            }));
            if (json.label) {
              n.setData("stringArray", $.splat(json.label));
            }
          }
        });
        this.normalizeDims();
        if (animate) {
          delegate.compute("end");
          delegate.fx.animate({
            modes : ["node-property:dimArray:span", "linear"],
            duration : 1500,
            /**
             * @return {undefined}
             */
            onComplete : function() {
              /** @type {boolean} */
              that.busy = false;
              if (onComplete) {
                onComplete.onComplete();
              }
            }
          });
        } else {
          delegate.refresh();
        }
      },
      /**
       * @param {?} id
       * @param {boolean} lab
       * @return {undefined}
       */
      select : function(id, lab) {
        if (!this.config.hoveredColor) {
          return;
        }
        var s = this.selected;
        if (s.id != id || s.name != lab) {
          s.id = id;
          /** @type {boolean} */
          s.name = lab;
          s.color = this.config.hoveredColor;
          this.delegate.graph.eachNode(function(n) {
            if (id == n.id) {
              n.setData("border", s);
            } else {
              n.setData("border", false);
            }
          });
          this.delegate.plot();
        }
      },
      /**
       * @return {?}
       */
      getLegend : function() {
        var legend = {};
        var n;
        this.delegate.graph.getNode(this.delegate.root).eachAdjacency(function(adj) {
          n = adj.nodeTo;
        });
        var colors = n.getData("colorArray");
        var colorsLen = colors.length;
        $.each(n.getData("stringArray"), function(s, i) {
          legend[s] = colors[i % colorsLen];
        });
        return legend;
      },
      /**
       * @return {?}
       */
      getMaxValue : function() {
        /** @type {number} */
        var maxValue = 0;
        this.delegate.graph.eachNode(function(n) {
          var attributes = n.getData("valueArray");
          /** @type {number} */
          var acum = 0;
          $.each(attributes, function(v) {
            acum += +v;
          });
          maxValue = maxValue > acum ? maxValue : acum;
        });
        return maxValue;
      },
      /**
       * @return {undefined}
       */
      normalizeDims : function() {
        var root = this.delegate.graph.getNode(this.delegate.root);
        /** @type {number} */
        var w = 0;
        root.eachAdjacency(function() {
          w++;
        });
        var maxValue = this.getMaxValue() || 1;
        var config = this.config;
        var animate = config.animate;
        var rho = this.delegate.config.levelDistance;
        this.delegate.graph.eachNode(function(n) {
          /** @type {number} */
          var acum = 0;
          /** @type {Array} */
          var animateValue = [];
          $.each(n.getData("valueArray"), function(v) {
            acum += +v;
            animateValue.push(1);
          });
          /** @type {boolean} */
          var stat = animateValue.length == 1 && !config.updateHeights;
          if (animate) {
            n.setData("dimArray", $.map(n.getData("valueArray"), function(n) {
              return stat ? rho : n * rho / maxValue;
            }), "end");
            var dimArray = n.getData("dimArray");
            if (!dimArray) {
              n.setData("dimArray", animateValue);
            }
          } else {
            n.setData("dimArray", $.map(n.getData("valueArray"), function(n) {
              return stat ? rho : n * rho / maxValue;
            }));
          }
          n.setData("normalizedDim", acum / maxValue);
        });
      }
    });
    Layout.TM = {};
    Layout.TM.SliceAndDice = new Class({
      /**
       * @param {?} adj
       * @return {undefined}
       */
      compute : function(adj) {
        var from = this.graph.getNode(this.clickedNode && this.clickedNode.id || this.root);
        this.controller.onBeforeCompute(from);
        var size = this.canvas.getSize();
        var config = this.config;
        var width = size.width;
        var height = size.height;
        this.graph.computeLevels(this.root, 0, "ignore");
        from.getPos(adj).setc(-width / 2, -height / 2);
        from.setData("width", width, adj);
        from.setData("height", height + config.titleHeight, adj);
        this.computePositions(from, from, this.layout.orientation, adj);
        this.controller.onAfterCompute(from);
      },
      /**
       * @param {?} node
       * @param {Object} ch
       * @param {string} orn
       * @param {Object} prop
       * @return {undefined}
       */
      computePositions : function(node, ch, orn, prop) {
        /** @type {number} */
        var totalArea = 0;
        node.eachSubnode(function(n) {
          totalArea += n.getData("area", prop);
        });
        var config = this.config;
        var offset = config.offset;
        var val = node.getData("width", prop);
        /** @type {number} */
        var value = Math.max(node.getData("height", prop) - config.titleHeight, 0);
        /** @type {number} */
        var pow = node == ch ? 1 : ch.getData("area", prop) / totalArea;
        var result;
        var size;
        var dim;
        var pos;
        var pos2;
        var posth;
        var pos2th;
        /** @type {boolean} */
        var horizontal = orn == "h";
        if (horizontal) {
          /** @type {string} */
          orn = "v";
          /** @type {number} */
          result = value;
          /** @type {number} */
          size = val * pow;
          /** @type {string} */
          dim = "height";
          /** @type {string} */
          pos = "y";
          /** @type {string} */
          pos2 = "x";
          posth = config.titleHeight;
          /** @type {number} */
          pos2th = 0;
        } else {
          /** @type {string} */
          orn = "h";
          /** @type {number} */
          result = value * pow;
          size = val;
          /** @type {string} */
          dim = "width";
          /** @type {string} */
          pos = "x";
          /** @type {string} */
          pos2 = "y";
          /** @type {number} */
          posth = 0;
          pos2th = config.titleHeight;
        }
        var cpos = ch.getPos(prop);
        ch.setData("width", size, prop);
        ch.setData("height", result, prop);
        /** @type {number} */
        var offsetSize = 0;
        var tm = this;
        ch.eachSubnode(function(n) {
          var p = n.getPos(prop);
          p[pos] = offsetSize + cpos[pos] + posth;
          p[pos2] = cpos[pos2] + pos2th;
          tm.computePositions(ch, n, orn, prop);
          offsetSize += n.getData(dim, prop);
        });
      }
    });
    Layout.TM.Area = {
      /**
       * @param {string} adj
       * @return {undefined}
       */
      compute : function(adj) {
        adj = adj || "current";
        var from = this.graph.getNode(this.clickedNode && this.clickedNode.id || this.root);
        this.controller.onBeforeCompute(from);
        var config = this.config;
        var cs = this.canvas.getSize();
        var len = cs.width;
        var h = cs.height;
        var start = config.offset;
        /** @type {number} */
        var size = len - start;
        /** @type {number} */
        var offhght = h - start;
        this.graph.computeLevels(this.root, 0, "ignore");
        from.getPos(adj).setc(-len / 2, -h / 2);
        from.setData("width", len, adj);
        from.setData("height", h, adj);
        var coord = {
          top : -h / 2 + config.titleHeight,
          left : -len / 2,
          width : size,
          height : offhght - config.titleHeight
        };
        this.computePositions(from, coord, adj);
        this.controller.onAfterCompute(from);
      },
      /**
       * @param {string} tail
       * @param {?} initElem
       * @param {number} w
       * @param {?} coord
       * @param {?} comp
       * @param {Object} prop
       * @return {undefined}
       */
      computeDim : function(tail, initElem, w, coord, comp, prop) {
        if (tail.length + initElem.length == 1) {
          var l = tail.length == 1 ? tail : initElem;
          this.layoutLast(l, w, coord, prop);
          return;
        }
        if (tail.length >= 2 && initElem.length == 0) {
          /** @type {Array} */
          initElem = [tail.shift()];
        }
        if (tail.length == 0) {
          if (initElem.length > 0) {
            this.layoutRow(initElem, w, coord, prop);
          }
          return;
        }
        var c = tail[0];
        if (comp(initElem, w) >= comp([c].concat(initElem), w)) {
          this.computeDim(tail.slice(1), initElem.concat([c]), w, coord, comp, prop);
        } else {
          var newCoords = this.layoutRow(initElem, w, coord, prop);
          this.computeDim(tail, [], newCoords.dim, newCoords, comp, prop);
        }
      },
      /**
       * @param {Array} ch
       * @param {number} w
       * @return {?}
       */
      worstAspectRatio : function(ch, w) {
        if (!ch || ch.length == 0) {
          return Number.MAX_VALUE;
        }
        /** @type {number} */
        var areaSum = 0;
        /** @type {number} */
        var maxArea = 0;
        /** @type {number} */
        var minArea = Number.MAX_VALUE;
        /** @type {number} */
        var i = 0;
        var l = ch.length;
        for (;i < l;i++) {
          var area = ch[i]._area;
          areaSum += area;
          minArea = minArea < area ? minArea : area;
          maxArea = maxArea > area ? maxArea : area;
        }
        /** @type {number} */
        var sqw = w * w;
        /** @type {number} */
        var sqAreaSum = areaSum * areaSum;
        return Math.max(sqw * maxArea / sqAreaSum, sqAreaSum / (sqw * minArea));
      },
      /**
       * @param {Array} ch
       * @param {number} w
       * @return {?}
       */
      avgAspectRatio : function(ch, w) {
        if (!ch || ch.length == 0) {
          return Number.MAX_VALUE;
        }
        /** @type {number} */
        var sum = 0;
        /** @type {number} */
        var i = 0;
        var len = ch.length;
        for (;i < len;i++) {
          var area = ch[i]._area;
          /** @type {number} */
          var h = area / w;
          sum += w > h ? w / h : h / w;
        }
        return sum / len;
      },
      /**
       * @param {Array} ch
       * @param {number} w
       * @param {?} coord
       * @param {Object} prop
       * @return {undefined}
       */
      layoutLast : function(ch, w, coord, prop) {
        var child = ch[0];
        child.getPos(prop).setc(coord.left, coord.top);
        child.setData("width", coord.width, prop);
        child.setData("height", coord.height, prop);
      }
    };
    Layout.TM.Squarified = new Class({
      Implements : Layout.TM.Area,
      /**
       * @param {?} node
       * @param {Object} coord
       * @param {Object} prop
       * @return {undefined}
       */
      computePositions : function(node, coord, prop) {
        var config = this.config;
        /** @type {function (...[*]): number} */
        var max = Math.max;
        if (coord.width >= coord.height) {
          /** @type {string} */
          this.layout.orientation = "h";
        } else {
          /** @type {string} */
          this.layout.orientation = "v";
        }
        var ch = node.getSubnodes([1, 1], "ignore");
        if (ch.length > 0) {
          this.processChildrenLayout(node, ch, coord, prop);
          /** @type {number} */
          var i = 0;
          var l = ch.length;
          for (;i < l;i++) {
            var chi = ch[i];
            var offst = config.offset;
            /** @type {number} */
            var height = max(chi.getData("height", prop) - offst - config.titleHeight, 0);
            /** @type {number} */
            var width = max(chi.getData("width", prop) - offst, 0);
            var chipos = chi.getPos(prop);
            coord = {
              width : width,
              height : height,
              top : chipos.y + config.titleHeight,
              left : chipos.x
            };
            this.computePositions(chi, coord, prop);
          }
        }
      },
      /**
       * @param {?} dataAndEvents
       * @param {Array} ch
       * @param {Object} coord
       * @param {Object} prop
       * @return {undefined}
       */
      processChildrenLayout : function(dataAndEvents, ch, coord, prop) {
        /** @type {number} */
        var parentArea = coord.width * coord.height;
        var i;
        var l = ch.length;
        /** @type {number} */
        var totalChArea = 0;
        /** @type {Array} */
        var chArea = [];
        /** @type {number} */
        i = 0;
        for (;i < l;i++) {
          /** @type {number} */
          chArea[i] = parseFloat(ch[i].getData("area", prop));
          totalChArea += chArea[i];
        }
        /** @type {number} */
        i = 0;
        for (;i < l;i++) {
          /** @type {number} */
          ch[i]._area = parentArea * chArea[i] / totalChArea;
        }
        var minimumSideValue = this.layout.horizontal() ? coord.height : coord.width;
        ch.sort(function(b, a) {
          /** @type {number} */
          var firstByIndex = a._area - b._area;
          return firstByIndex ? firstByIndex : a.id == b.id ? 0 : a.id < b.id ? 1 : -1;
        });
        /** @type {Array} */
        var initElem = [ch[0]];
        var tail = ch.slice(1);
        this.squarify(tail, initElem, minimumSideValue, coord, prop);
      },
      /**
       * @param {string} tail
       * @param {?} initElem
       * @param {number} w
       * @param {?} coord
       * @param {Object} prop
       * @return {undefined}
       */
      squarify : function(tail, initElem, w, coord, prop) {
        this.computeDim(tail, initElem, w, coord, this.worstAspectRatio, prop);
      },
      /**
       * @param {?} opt_attributes
       * @param {number} w
       * @param {?} coord
       * @param {Object} prop
       * @return {?}
       */
      layoutRow : function(opt_attributes, w, coord, prop) {
        if (this.layout.horizontal()) {
          return this.layoutV(opt_attributes, w, coord, prop);
        } else {
          return this.layoutH(opt_attributes, w, coord, prop);
        }
      },
      /**
       * @param {?} attributes
       * @param {number} w
       * @param {?} coord
       * @param {Object} prop
       * @return {?}
       */
      layoutV : function(attributes, w, coord, prop) {
        /** @type {number} */
        var totalArea = 0;
        /**
         * @param {number} x
         * @return {?}
         */
        var rnd = function(x) {
          return x;
        };
        $.each(attributes, function(elem) {
          totalArea += elem._area;
        });
        var width = rnd(totalArea / w);
        /** @type {number} */
        var top = 0;
        /** @type {number} */
        var i = 0;
        var aLength = attributes.length;
        for (;i < aLength;i++) {
          var h = rnd(attributes[i]._area / width);
          var chi = attributes[i];
          chi.getPos(prop).setc(coord.left, coord.top + top);
          chi.setData("width", width, prop);
          chi.setData("height", h, prop);
          top += h;
        }
        var ans = {
          height : coord.height,
          width : coord.width - width,
          top : coord.top,
          left : coord.left + width
        };
        /** @type {number} */
        ans.dim = Math.min(ans.width, ans.height);
        if (ans.dim != ans.height) {
          this.layout.change();
        }
        return ans;
      },
      /**
       * @param {?} attributes
       * @param {number} w
       * @param {?} coord
       * @param {Object} prop
       * @return {?}
       */
      layoutH : function(attributes, w, coord, prop) {
        /** @type {number} */
        var totalArea = 0;
        $.each(attributes, function(elem) {
          totalArea += elem._area;
        });
        /** @type {number} */
        var height = totalArea / w;
        var top = coord.top;
        /** @type {number} */
        var left = 0;
        /** @type {number} */
        var i = 0;
        var aLength = attributes.length;
        for (;i < aLength;i++) {
          var chi = attributes[i];
          /** @type {number} */
          w = chi._area / height;
          chi.getPos(prop).setc(coord.left + left, top);
          chi.setData("width", w, prop);
          chi.setData("height", height, prop);
          left += w;
        }
        var ans = {
          height : coord.height - height,
          width : coord.width,
          top : coord.top + height,
          left : coord.left
        };
        /** @type {number} */
        ans.dim = Math.min(ans.width, ans.height);
        if (ans.dim != ans.width) {
          this.layout.change();
        }
        return ans;
      }
    });
    Layout.TM.Strip = new Class({
      Implements : Layout.TM.Area,
      /**
       * @param {?} node
       * @param {Object} coord
       * @param {Object} prop
       * @return {undefined}
       */
      computePositions : function(node, coord, prop) {
        var ch = node.getSubnodes([1, 1], "ignore");
        var config = this.config;
        /** @type {function (...[*]): number} */
        var max = Math.max;
        if (ch.length > 0) {
          this.processChildrenLayout(node, ch, coord, prop);
          /** @type {number} */
          var i = 0;
          var l = ch.length;
          for (;i < l;i++) {
            var chi = ch[i];
            var offst = config.offset;
            /** @type {number} */
            var height = max(chi.getData("height", prop) - offst - config.titleHeight, 0);
            /** @type {number} */
            var width = max(chi.getData("width", prop) - offst, 0);
            var chipos = chi.getPos(prop);
            coord = {
              width : width,
              height : height,
              top : chipos.y + config.titleHeight,
              left : chipos.x
            };
            this.computePositions(chi, coord, prop);
          }
        }
      },
      /**
       * @param {?} dataAndEvents
       * @param {Array} ch
       * @param {Object} coord
       * @param {Object} prop
       * @return {undefined}
       */
      processChildrenLayout : function(dataAndEvents, ch, coord, prop) {
        /** @type {number} */
        var parentArea = coord.width * coord.height;
        var i;
        var l = ch.length;
        /** @type {number} */
        var totalChArea = 0;
        /** @type {Array} */
        var chArea = [];
        /** @type {number} */
        i = 0;
        for (;i < l;i++) {
          /** @type {number} */
          chArea[i] = +ch[i].getData("area", prop);
          totalChArea += chArea[i];
        }
        /** @type {number} */
        i = 0;
        for (;i < l;i++) {
          /** @type {number} */
          ch[i]._area = parentArea * chArea[i] / totalChArea;
        }
        var side = this.layout.horizontal() ? coord.width : coord.height;
        /** @type {Array} */
        var initElem = [ch[0]];
        var tail = ch.slice(1);
        this.stripify(tail, initElem, side, coord, prop);
      },
      /**
       * @param {string} tail
       * @param {?} initElem
       * @param {number} w
       * @param {?} coord
       * @param {Object} prop
       * @return {undefined}
       */
      stripify : function(tail, initElem, w, coord, prop) {
        this.computeDim(tail, initElem, w, coord, this.avgAspectRatio, prop);
      },
      /**
       * @param {?} opt_attributes
       * @param {number} w
       * @param {?} coord
       * @param {Object} prop
       * @return {?}
       */
      layoutRow : function(opt_attributes, w, coord, prop) {
        if (this.layout.horizontal()) {
          return this.layoutH(opt_attributes, w, coord, prop);
        } else {
          return this.layoutV(opt_attributes, w, coord, prop);
        }
      },
      /**
       * @param {?} attributes
       * @param {number} w
       * @param {?} coord
       * @param {Object} prop
       * @return {?}
       */
      layoutV : function(attributes, w, coord, prop) {
        /** @type {number} */
        var totalArea = 0;
        $.each(attributes, function(elem) {
          totalArea += elem._area;
        });
        /** @type {number} */
        var width = totalArea / w;
        /** @type {number} */
        var top = 0;
        /** @type {number} */
        var i = 0;
        var aLength = attributes.length;
        for (;i < aLength;i++) {
          var chi = attributes[i];
          /** @type {number} */
          var h = chi._area / width;
          chi.getPos(prop).setc(coord.left, coord.top + (w - h - top));
          chi.setData("width", width, prop);
          chi.setData("height", h, prop);
          top += h;
        }
        return{
          height : coord.height,
          width : coord.width - width,
          top : coord.top,
          left : coord.left + width,
          dim : w
        };
      },
      /**
       * @param {?} attributes
       * @param {number} w
       * @param {?} coord
       * @param {Object} prop
       * @return {?}
       */
      layoutH : function(attributes, w, coord, prop) {
        /** @type {number} */
        var totalArea = 0;
        $.each(attributes, function(elem) {
          totalArea += elem._area;
        });
        /** @type {number} */
        var height = totalArea / w;
        /** @type {number} */
        var top = coord.height - height;
        /** @type {number} */
        var left = 0;
        /** @type {number} */
        var i = 0;
        var aLength = attributes.length;
        for (;i < aLength;i++) {
          var chi = attributes[i];
          /** @type {number} */
          var width = chi._area / height;
          chi.getPos(prop).setc(coord.left + left, coord.top + top);
          chi.setData("width", width, prop);
          chi.setData("height", height, prop);
          left += width;
        }
        return{
          height : coord.height - height,
          width : coord.width,
          top : coord.top,
          left : coord.left,
          dim : w
        };
      }
    });
    Layout.Icicle = new Class({
      /**
       * @param {?} adj
       * @return {undefined}
       */
      compute : function(adj) {
        adj = adj || "current";
        var from = this.graph.getNode(this.root);
        var config = this.config;
        var size = this.canvas.getSize();
        var width = size.width;
        var height = size.height;
        var offset = config.offset;
        var parentWidth = config.constrained ? config.levelsToShow : Number.MAX_VALUE;
        this.controller.onBeforeCompute(from);
        Graph.Util.computeLevels(this.graph, from.id, 0, "ignore");
        /** @type {number} */
        var treeDepth = 0;
        Graph.Util.eachLevel(from, 0, false, function(dataAndEvents, d) {
          if (d > treeDepth) {
            /** @type {number} */
            treeDepth = d;
          }
        });
        var startNode = this.graph.getNode(this.clickedNode && this.clickedNode.id || from.id);
        /** @type {number} */
        var maxDepth = Math.min(treeDepth, parentWidth - 1);
        var initialDepth = startNode._depth;
        if (this.layout.horizontal()) {
          this.computeSubtree(startNode, -width / 2, -height / 2, width / (maxDepth + 1), height, initialDepth, maxDepth, adj);
        } else {
          this.computeSubtree(startNode, -width / 2, -height / 2, width, height / (maxDepth + 1), initialDepth, maxDepth, adj);
        }
      },
      /**
       * @param {Object} root
       * @param {number} x
       * @param {number} y
       * @param {number} width
       * @param {number} height
       * @param {?} initialDepth
       * @param {number} maxDepth
       * @param {Object} prop
       * @return {undefined}
       */
      computeSubtree : function(root, x, y, width, height, initialDepth, maxDepth, prop) {
        root.getPos(prop).setc(x, y);
        root.setData("width", width, prop);
        root.setData("height", height, prop);
        var nodeLength;
        /** @type {number} */
        var K = 0;
        /** @type {number} */
        var totalDim = 0;
        var attributes = Graph.Util.getSubnodes(root, [1, 1], "ignore");
        if (!attributes.length) {
          return;
        }
        $.each(attributes, function(e) {
          totalDim += e.getData("dim");
        });
        /** @type {number} */
        var i = 0;
        var aLength = attributes.length;
        for (;i < aLength;i++) {
          if (this.layout.horizontal()) {
            /** @type {number} */
            nodeLength = height * attributes[i].getData("dim") / totalDim;
            this.computeSubtree(attributes[i], x + width, y, width, nodeLength, initialDepth, maxDepth, prop);
            y += nodeLength;
          } else {
            /** @type {number} */
            nodeLength = width * attributes[i].getData("dim") / totalDim;
            this.computeSubtree(attributes[i], x, y + height, nodeLength, height, initialDepth, maxDepth, prop);
            x += nodeLength;
          }
        }
      }
    });
    $jit.Icicle = new Class({
      Implements : [valid, Extras, Layout.Icicle],
      layout : {
        orientation : "h",
        /**
         * @return {?}
         */
        vertical : function() {
          return this.orientation == "v";
        },
        /**
         * @return {?}
         */
        horizontal : function() {
          return this.orientation == "h";
        },
        /**
         * @return {undefined}
         */
        change : function() {
          /** @type {string} */
          this.orientation = this.vertical() ? "h" : "v";
        }
      },
      /**
       * @param {?} controller
       * @return {undefined}
       */
      initialize : function(controller) {
        var config = {
          animate : false,
          orientation : "h",
          offset : 2,
          levelsToShow : Number.MAX_VALUE,
          constrained : false,
          Node : {
            type : "rectangle",
            overridable : true
          },
          Edge : {
            type : "none"
          },
          Label : {
            type : "Native"
          },
          duration : 700,
          fps : 45
        };
        var opts = Options("Canvas", "Node", "Edge", "Fx", "Tips", "NodeStyles", "Events", "Navigation", "Controller", "Label");
        this.controller = this.config = $.merge(opts, config, controller);
        this.layout.orientation = this.config.orientation;
        var canvasConfig = this.config;
        if (canvasConfig.useCanvas) {
          this.canvas = canvasConfig.useCanvas;
          /** @type {string} */
          this.config.labelContainer = this.canvas.id + "-label";
        } else {
          this.canvas = new Canvas(this, canvasConfig);
          /** @type {string} */
          this.config.labelContainer = (typeof canvasConfig.injectInto == "string" ? canvasConfig.injectInto : canvasConfig.injectInto.id) + "-label";
        }
        this.graphOptions = {
          /** @type {function (number, (number|string)): undefined} */
          klass : Vector,
          Node : {
            selected : false,
            exist : true,
            drawn : true
          }
        };
        this.graph = new Graph(this.graphOptions, this.config.Node, this.config.Edge, this.config.Label);
        this.labels = new $jit.Icicle.Label[this.config.Label.type](this);
        this.fx = new $jit.Icicle.Plot(this, $jit.Icicle);
        this.op = new $jit.Icicle.Op(this);
        this.group = new $jit.Icicle.Group(this);
        /** @type {null} */
        this.clickedNode = null;
        this.initializeExtras();
      },
      /**
       * @return {undefined}
       */
      refresh : function() {
        var type = this.config.Label.type;
        if (type != "Native") {
          var that = this;
          this.graph.eachNode(function(from) {
            that.labels.hideLabel(from, false);
          });
        }
        this.compute();
        this.plot();
      },
      /**
       * @return {undefined}
       */
      plot : function() {
        this.fx.plot(this.config);
      },
      /**
       * @param {?} node
       * @return {undefined}
       */
      enter : function(node) {
        if (this.busy) {
          return;
        }
        /** @type {boolean} */
        this.busy = true;
        var that = this;
        var config = this.config;
        var callback = {
          /**
           * @return {undefined}
           */
          onComplete : function() {
            if (config.request) {
              that.compute();
            }
            if (config.animate) {
              that.graph.nodeList.setDataset(["current", "end"], {
                alpha : [1, 0]
              });
              Graph.Util.eachSubgraph(node, function(n) {
                n.setData("alpha", 1, "end");
              }, "ignore");
              that.fx.animate({
                duration : 500,
                modes : ["node-property:alpha"],
                /**
                 * @return {undefined}
                 */
                onComplete : function() {
                  that.clickedNode = node;
                  that.compute("end");
                  that.fx.animate({
                    modes : ["linear", "node-property:width:height"],
                    duration : 1E3,
                    /**
                     * @return {undefined}
                     */
                    onComplete : function() {
                      /** @type {boolean} */
                      that.busy = false;
                      that.clickedNode = node;
                    }
                  });
                }
              });
            } else {
              that.clickedNode = node;
              /** @type {boolean} */
              that.busy = false;
              that.refresh();
            }
          }
        };
        if (config.request) {
          this.requestNodes(clickedNode, callback);
        } else {
          callback.onComplete();
        }
      },
      /**
       * @return {undefined}
       */
      out : function() {
        if (this.busy) {
          return;
        }
        var that = this;
        var GUtil = Graph.Util;
        var config = this.config;
        var graph = this.graph;
        var args = GUtil.getParents(graph.getNode(this.clickedNode && this.clickedNode.id || this.root));
        var parent = args[0];
        var clickedNode = parent;
        var previousClickedNode = this.clickedNode;
        /** @type {boolean} */
        this.busy = true;
        /** @type {boolean} */
        this.events.hoveredNode = false;
        if (!parent) {
          /** @type {boolean} */
          this.busy = false;
          return;
        }
        callback = {
          /**
           * @return {undefined}
           */
          onComplete : function() {
            that.clickedNode = parent;
            if (config.request) {
              that.requestNodes(parent, {
                /**
                 * @return {undefined}
                 */
                onComplete : function() {
                  that.compute();
                  that.plot();
                  /** @type {boolean} */
                  that.busy = false;
                }
              });
            } else {
              that.compute();
              that.plot();
              /** @type {boolean} */
              that.busy = false;
            }
          }
        };
        if (config.animate) {
          this.clickedNode = clickedNode;
          this.compute("end");
          this.clickedNode = previousClickedNode;
          this.fx.animate({
            modes : ["linear", "node-property:width:height"],
            duration : 1E3,
            /**
             * @return {undefined}
             */
            onComplete : function() {
              that.clickedNode = clickedNode;
              graph.nodeList.setDataset(["current", "end"], {
                alpha : [0, 1]
              });
              GUtil.eachSubgraph(previousClickedNode, function(n) {
                n.setData("alpha", 1);
              }, "ignore");
              that.fx.animate({
                duration : 500,
                modes : ["node-property:alpha"],
                /**
                 * @return {undefined}
                 */
                onComplete : function() {
                  callback.onComplete();
                }
              });
            }
          });
        } else {
          callback.onComplete();
        }
      },
      /**
       * @param {?} node
       * @param {?} onComplete
       * @return {undefined}
       */
      requestNodes : function(node, onComplete) {
        var handler = $.merge(this.controller, onComplete);
        var recurring = this.config.constrained ? this.config.levelsToShow : Number.MAX_VALUE;
        if (handler.request) {
          /** @type {Array} */
          var leaves = [];
          var d = node._depth;
          Graph.Util.eachLevel(node, 0, recurring, function(n) {
            if (n.drawn && !Graph.Util.anySubnode(n)) {
              leaves.push(n);
              /** @type {number} */
              n._level = n._depth - d;
              if (this.config.constrained) {
                /** @type {number} */
                n._level = recurring - n._level;
              }
            }
          });
          this.group.requestNodes(leaves, handler);
        } else {
          handler.onComplete();
        }
      }
    });
    $jit.Icicle.Op = new Class({
      Implements : Graph.Op
    });
    $jit.Icicle.Group = new Class({
      /**
       * @param {Object} viz
       * @return {undefined}
       */
      initialize : function(viz) {
        /** @type {Object} */
        this.viz = viz;
        this.canvas = viz.canvas;
        this.config = viz.config;
      },
      /**
       * @param {Array} nodes
       * @param {?} controller
       * @return {undefined}
       */
      requestNodes : function(nodes, controller) {
        /** @type {number} */
        var counter = 0;
        var len = nodes.length;
        var nodeSelected = {};
        /**
         * @return {undefined}
         */
        var complete = function() {
          controller.onComplete();
        };
        var viz = this.viz;
        if (len == 0) {
          complete();
        }
        /** @type {number} */
        var i = 0;
        for (;i < len;i++) {
          nodeSelected[nodes[i].id] = nodes[i];
          controller.request(nodes[i].id, nodes[i]._level, {
            /**
             * @param {number} adj
             * @param {?} lab
             * @return {undefined}
             */
            onComplete : function(adj, lab) {
              if (lab && lab.children) {
                /** @type {number} */
                lab.id = adj;
                viz.op.sum(lab, {
                  type : "nothing"
                });
              }
              if (++counter == len) {
                Graph.Util.computeLevels(viz.graph, viz.root, 0);
                complete();
              }
            }
          });
        }
      }
    });
    $jit.Icicle.Plot = new Class({
      Implements : Graph.Plot,
      /**
       * @param {?} opt
       * @param {boolean} animating
       * @return {undefined}
       */
      plot : function(opt, animating) {
        opt = opt || this.viz.controller;
        var viz = this.viz;
        var graph = viz.graph;
        var root = graph.getNode(viz.clickedNode && viz.clickedNode.id || viz.root);
        var initialDepth = root._depth;
        viz.canvas.clear();
        this.plotTree(root, $.merge(opt, {
          withLabels : true,
          hideLabels : false,
          /**
           * @param {?} dataAndEvents
           * @param {Object} node
           * @return {?}
           */
          plotSubtree : function(dataAndEvents, node) {
            return!viz.config.constrained || node._depth - initialDepth < viz.config.levelsToShow;
          }
        }), animating);
      }
    });
    $jit.Icicle.Label = {};
    $jit.Icicle.Label.Native = new Class({
      Implements : Graph.Label.Native,
      /**
       * @param {?} canvas
       * @param {Object} node
       * @param {?} opt
       * @return {undefined}
       */
      renderLabel : function(canvas, node, opt) {
        var ctx = canvas.getCtx();
        var width = node.getData("width");
        var height = node.getData("height");
        var size = node.getLabelData("size");
        var bbox = ctx.measureText(node.name);
        if (height < size * 1.5 || width < bbox.width) {
          return;
        }
        var pos = node.pos.getc(true);
        ctx.fillText(node.name, pos.x + width / 2, pos.y + height / 2);
      }
    });
    $jit.Icicle.Label.SVG = new Class({
      Implements : Graph.Label.SVG,
      /**
       * @param {?} viz
       * @return {undefined}
       */
      initialize : function(viz) {
        this.viz = viz;
      },
      /**
       * @param {?} from
       * @param {?} lab
       * @param {?} options
       * @return {undefined}
       */
      placeLabel : function(from, lab, options) {
        var offsetCoordinate = lab.pos.getc(true);
        var canvas = this.viz.canvas;
        var $cont = canvas.getSize();
        var tl = {
          x : Math.round(offsetCoordinate.x + $cont.width / 2),
          y : Math.round(offsetCoordinate.y + $cont.height / 2)
        };
        from.setAttribute("x", tl.x);
        from.setAttribute("y", tl.y);
        options.onPlaceLabel(from, lab);
      }
    });
    $jit.Icicle.Label.HTML = new Class({
      Implements : Graph.Label.HTML,
      /**
       * @param {?} viz
       * @return {undefined}
       */
      initialize : function(viz) {
        this.viz = viz;
      },
      /**
       * @param {?} from
       * @param {?} lab
       * @param {?} options
       * @return {undefined}
       */
      placeLabel : function(from, lab, options) {
        var offsetCoordinate = lab.pos.getc(true);
        var canvas = this.viz.canvas;
        var $cont = canvas.getSize();
        var pos = {
          x : Math.round(offsetCoordinate.x + $cont.width / 2),
          y : Math.round(offsetCoordinate.y + $cont.height / 2)
        };
        var aggregateStyle = from.style;
        /** @type {string} */
        aggregateStyle.left = pos.x + "px";
        /** @type {string} */
        aggregateStyle.top = pos.y + "px";
        /** @type {string} */
        aggregateStyle.display = "";
        options.onPlaceLabel(from, lab);
      }
    });
    $jit.Icicle.Plot.NodeTypes = new Class({
      none : {
        /** @type {function (): undefined} */
        render : $.empty
      },
      rectangle : {
        /**
         * @param {?} adj
         * @param {?} lab
         * @param {?} event
         * @return {undefined}
         */
        render : function(adj, lab, event) {
          var config = this.viz.config;
          var offset = config.offset;
          var width = adj.getData("width");
          var height = adj.getData("height");
          var border = adj.getData("border");
          var pos = adj.pos.getc(true);
          var posx = pos.x + offset / 2;
          var posy = pos.y + offset / 2;
          var ctx = lab.getCtx();
          if (width - offset < 2 || height - offset < 2) {
            return;
          }
          if (config.cushion) {
            var color = adj.getData("color");
            var gradient = ctx.createRadialGradient(posx + (width - offset) / 2, posy + (height - offset) / 2, 1, posx + (width - offset) / 2, posy + (height - offset) / 2, width < height ? height : width);
            var fgcolor = $.rgbToHex($.map($.hexToRgb(color), function(dataAndEvents) {
              return dataAndEvents * 0.3 >> 0;
            }));
            gradient.addColorStop(0, color);
            gradient.addColorStop(1, fgcolor);
            ctx.fillStyle = gradient;
          }
          if (border) {
            ctx.strokeStyle = border;
            /** @type {number} */
            ctx.lineWidth = 3;
          }
          ctx.fillRect(posx, posy, Math.max(0, width - offset), Math.max(0, height - offset));
          if (border) {
            ctx.strokeRect(pos.x, pos.y, width, height);
          }
        },
        /**
         * @param {?} opt_attributes
         * @param {?} value
         * @return {?}
         */
        contains : function(opt_attributes, value) {
          if (this.viz.clickedNode && !$jit.Graph.Util.isDescendantOf(opt_attributes, this.viz.clickedNode.id)) {
            return false;
          }
          var offsetCoordinate = opt_attributes.pos.getc(true);
          var actual = opt_attributes.getData("width");
          var epsilon = opt_attributes.getData("height");
          return this.nodeHelper.rectangle.contains({
            x : offsetCoordinate.x + actual / 2,
            y : offsetCoordinate.y + epsilon / 2
          }, value, actual, epsilon);
        }
      }
    });
    $jit.Icicle.Plot.EdgeTypes = new Class({
      /** @type {function (): undefined} */
      none : $.empty
    });
    Layout.ForceDirected = new Class({
      /**
       * @param {?} $allOptions
       * @return {?}
       */
      getOptions : function($allOptions) {
        var element = this.canvas.getSize();
        var originalWidth = element.width;
        var originalHeight = element.height;
        /** @type {number} */
        var count = 0;
        this.graph.eachNode(function(dataAndEvents) {
          count++;
        });
        /** @type {number} */
        var variance = originalWidth * originalHeight / count;
        /** @type {number} */
        var mult = Math.sqrt(variance);
        var ld = this.config.levelDistance;
        return{
          width : originalWidth,
          height : originalHeight,
          tstart : originalWidth * 0.1,
          /**
           * @param {number} v00
           * @return {?}
           */
          nodef : function(v00) {
            return variance / (v00 || 1);
          },
          /**
           * @param {?} value
           * @return {?}
           */
          edgef : function(value) {
            return mult * (value - ld);
          }
        };
      },
      /**
       * @param {?} adj
       * @param {?} type
       * @return {undefined}
       */
      compute : function(adj, type) {
        var lab = $.splat(adj || ["current", "start", "end"]);
        var coord = this.getOptions();
        column.compute(this.graph, lab, this.config);
        this.graph.computeLevels(this.root, 0, "ignore");
        this.graph.eachNode(function(self) {
          $.each(lab, function(prop) {
            var p = self.getPos(prop);
            if (p.equals(Vector.KER)) {
              /** @type {number} */
              p.x = coord.width / 5 * (Math.random() - 0.5);
              /** @type {number} */
              p.y = coord.height / 5 * (Math.random() - 0.5);
            }
            self.disp = {};
            $.each(lab, function(timeoutKey) {
              self.disp[timeoutKey] = getIndex(0, 0);
            });
          });
        });
        this.computePositions(lab, coord, type);
      },
      /**
       * @param {?} node
       * @param {Object} prop
       * @param {Object} p
       * @return {undefined}
       */
      computePositions : function(node, prop, p) {
        var len = this.config.iterations;
        /** @type {number} */
        var i = 0;
        var jQuery = this;
        if (p) {
          (function play() {
            var pl = p.iter;
            /** @type {number} */
            var j = 0;
            for (;j < pl;j++) {
              prop.t = prop.tstart;
              if (len) {
                prop.t *= 1 - i++ / (len - 1);
              }
              jQuery.computePositionStep(node, prop);
              if (len && i >= len) {
                p.onComplete();
                return;
              }
            }
            p.onStep(Math.round(i / (len - 1) * 100));
            setTimeout(play, 1);
          })();
        } else {
          for (;i < len;i++) {
            /** @type {number} */
            prop.t = prop.tstart * (1 - i / (len - 1));
            this.computePositionStep(node, prop);
          }
        }
      },
      /**
       * @param {?} attributes
       * @param {Object} options
       * @return {undefined}
       */
      computePositionStep : function(attributes, options) {
        var graph = this.graph;
        /** @type {function (...[*]): number} */
        var mn = Math.min;
        /** @type {function (...[*]): number} */
        var max = Math.max;
        var pos = getIndex(0, 0);
        graph.eachNode(function(node) {
          $.each(attributes, function(y) {
            /** @type {number} */
            node.disp[y].x = 0;
            /** @type {number} */
            node.disp[y].y = 0;
          });
          graph.eachNode(function(n) {
            if (n.id != node.id) {
              $.each(attributes, function(prop) {
                var p = node.getPos(prop);
                var v = n.getPos(prop);
                /** @type {number} */
                pos.x = p.x - v.x;
                /** @type {number} */
                pos.y = p.y - v.y;
                var x = pos.norm() || 1;
                node.disp[prop].$add(pos.$scale(options.nodef(x) / x));
              });
            }
          });
        });
        /** @type {boolean} */
        var T = !!graph.getNode(this.root).visited;
        graph.eachNode(function(node) {
          node.eachAdjacency(function(adj) {
            var child = adj.nodeTo;
            if (!!child.visited === T) {
              $.each(attributes, function(prop) {
                var p = node.getPos(prop);
                var v = child.getPos(prop);
                /** @type {number} */
                pos.x = p.x - v.x;
                /** @type {number} */
                pos.y = p.y - v.y;
                var udataCur = pos.norm() || 1;
                node.disp[prop].$add(pos.$scale(-options.edgef(udataCur) / udataCur));
                child.disp[prop].$add(pos.$scale(-1));
              });
            }
          });
          /** @type {boolean} */
          node.visited = !T;
        });
        var precision = options.t;
        /** @type {number} */
        var indents = options.width / 2;
        /** @type {number} */
        var minMargin = options.height / 2;
        graph.eachNode(function(event) {
          $.each(attributes, function(prop) {
            var pos = event.disp[prop];
            var I = pos.norm() || 1;
            prop = event.getPos(prop);
            prop.$add(getIndex(pos.x * mn(Math.abs(pos.x), precision) / I, pos.y * mn(Math.abs(pos.y), precision) / I));
            /** @type {number} */
            prop.x = mn(indents, max(-indents, prop.x));
            /** @type {number} */
            prop.y = mn(minMargin, max(-minMargin, prop.y));
          });
        });
      }
    });
    $jit.ForceDirected = new Class({
      Implements : [valid, Extras, Layout.ForceDirected],
      /**
       * @param {?} controller
       * @return {undefined}
       */
      initialize : function(controller) {
        var $ForceDirected = $jit.ForceDirected;
        var config = {
          iterations : 50,
          levelDistance : 50
        };
        this.controller = this.config = $.merge(Options("Canvas", "Node", "Edge", "Fx", "Tips", "NodeStyles", "Events", "Navigation", "Controller", "Label"), config, controller);
        var canvasConfig = this.config;
        if (canvasConfig.useCanvas) {
          this.canvas = canvasConfig.useCanvas;
          /** @type {string} */
          this.config.labelContainer = this.canvas.id + "-label";
        } else {
          if (canvasConfig.background) {
            canvasConfig.background = $.merge({
              type : "Circles"
            }, canvasConfig.background);
          }
          this.canvas = new Canvas(this, canvasConfig);
          /** @type {string} */
          this.config.labelContainer = (typeof canvasConfig.injectInto == "string" ? canvasConfig.injectInto : canvasConfig.injectInto.id) + "-label";
        }
        this.graphOptions = {
          /** @type {function (number, (number|string)): undefined} */
          klass : Vector,
          Node : {
            selected : false,
            exist : true,
            drawn : true
          }
        };
        this.graph = new Graph(this.graphOptions, this.config.Node, this.config.Edge);
        this.labels = new $ForceDirected.Label[canvasConfig.Label.type](this);
        this.fx = new $ForceDirected.Plot(this, $ForceDirected);
        this.op = new $ForceDirected.Op(this);
        /** @type {null} */
        this.json = null;
        /** @type {boolean} */
        this.busy = false;
        this.initializeExtras();
      },
      /**
       * @return {undefined}
       */
      refresh : function() {
        this.compute();
        this.plot();
      },
      /**
       * @return {undefined}
       */
      reposition : function() {
        this.compute("end");
      },
      /**
       * @param {?} lab
       * @return {undefined}
       */
      computeIncremental : function(lab) {
        lab = $.merge({
          iter : 20,
          property : "end",
          /** @type {function (): undefined} */
          onStep : $.empty,
          /** @type {function (): undefined} */
          onComplete : $.empty
        }, lab || {});
        this.config.onBeforeCompute(this.graph.getNode(this.root));
        this.compute(lab.property, lab);
      },
      /**
       * @return {undefined}
       */
      plot : function() {
        this.fx.plot();
      },
      /**
       * @param {?} opt_attributes
       * @return {undefined}
       */
      animate : function(opt_attributes) {
        this.fx.animate($.merge({
          modes : ["linear"]
        }, opt_attributes || {}));
      }
    });
    /** @type {boolean} */
    $jit.ForceDirected.$extend = true;
    (function(Hypertree) {
      Hypertree.Op = new Class({
        Implements : Graph.Op
      });
      Hypertree.Plot = new Class({
        Implements : Graph.Plot
      });
      Hypertree.Label = {};
      Hypertree.Label.Native = new Class({
        Implements : Graph.Label.Native
      });
      Hypertree.Label.SVG = new Class({
        Implements : Graph.Label.SVG,
        /**
         * @param {?} viz
         * @return {undefined}
         */
        initialize : function(viz) {
          this.viz = viz;
        },
        /**
         * @param {?} from
         * @param {?} lab
         * @param {?} options
         * @return {undefined}
         */
        placeLabel : function(from, lab, options) {
          var pos = lab.pos.getc(true);
          var canvas = this.viz.canvas;
          var ox = canvas.translateOffsetX;
          var oy = canvas.translateOffsetY;
          var sx = canvas.scaleOffsetX;
          var sy = canvas.scaleOffsetY;
          var $cont = canvas.getSize();
          var tl = {
            x : Math.round(pos.x * sx + ox + $cont.width / 2),
            y : Math.round(pos.y * sy + oy + $cont.height / 2)
          };
          from.setAttribute("x", tl.x);
          from.setAttribute("y", tl.y);
          options.onPlaceLabel(from, lab);
        }
      });
      Hypertree.Label.HTML = new Class({
        Implements : Graph.Label.HTML,
        /**
         * @param {?} viz
         * @return {undefined}
         */
        initialize : function(viz) {
          this.viz = viz;
        },
        /**
         * @param {?} from
         * @param {?} lab
         * @param {?} options
         * @return {undefined}
         */
        placeLabel : function(from, lab, options) {
          var pos = lab.pos.getc(true);
          var canvas = this.viz.canvas;
          var ox = canvas.translateOffsetX;
          var oy = canvas.translateOffsetY;
          var sx = canvas.scaleOffsetX;
          var sy = canvas.scaleOffsetY;
          var $cont = canvas.getSize();
          var labelPos = {
            x : Math.round(pos.x * sx + ox + $cont.width / 2),
            y : Math.round(pos.y * sy + oy + $cont.height / 2)
          };
          var style = from.style;
          /** @type {string} */
          style.left = labelPos.x + "px";
          /** @type {string} */
          style.top = labelPos.y + "px";
          /** @type {string} */
          style.display = this.fitsInCanvas(labelPos, canvas) ? "" : "none";
          options.onPlaceLabel(from, lab);
        }
      });
      Hypertree.Plot.NodeTypes = new Class({
        none : {
          /** @type {function (): undefined} */
          render : $.empty,
          contains : $.lambda(false)
        },
        circle : {
          /**
           * @param {?} adj
           * @param {?} type
           * @return {undefined}
           */
          render : function(adj, type) {
            var lab = adj.pos.getc(true);
            var qualifier = adj.getData("dim");
            this.nodeHelper.circle.render("fill", lab, qualifier, type);
          },
          /**
           * @param {?} opt_attributes
           * @param {?} value
           * @return {?}
           */
          contains : function(opt_attributes, value) {
            var attributes = opt_attributes.pos.getc(true);
            var actual = opt_attributes.getData("dim");
            return this.nodeHelper.circle.contains(attributes, value, actual);
          }
        },
        ellipse : {
          /**
           * @param {?} adj
           * @param {?} type
           * @return {undefined}
           */
          render : function(adj, type) {
            var lab = adj.pos.getc(true);
            var qualifier = adj.getData("width");
            var cycle = adj.getData("height");
            this.nodeHelper.ellipse.render("fill", lab, qualifier, cycle, type);
          },
          /**
           * @param {?} opt_attributes
           * @param {?} value
           * @return {?}
           */
          contains : function(opt_attributes, value) {
            var attributes = opt_attributes.pos.getc(true);
            var actual = opt_attributes.getData("width");
            var epsilon = opt_attributes.getData("height");
            return this.nodeHelper.ellipse.contains(attributes, value, actual, epsilon);
          }
        },
        square : {
          /**
           * @param {?} adj
           * @param {?} type
           * @return {undefined}
           */
          render : function(adj, type) {
            var lab = adj.pos.getc(true);
            var qualifier = adj.getData("dim");
            this.nodeHelper.square.render("fill", lab, qualifier, type);
          },
          /**
           * @param {?} opt_attributes
           * @param {?} value
           * @return {?}
           */
          contains : function(opt_attributes, value) {
            var attributes = opt_attributes.pos.getc(true);
            var actual = opt_attributes.getData("dim");
            return this.nodeHelper.square.contains(attributes, value, actual);
          }
        },
        rectangle : {
          /**
           * @param {?} adj
           * @param {?} type
           * @return {undefined}
           */
          render : function(adj, type) {
            var lab = adj.pos.getc(true);
            var qualifier = adj.getData("width");
            var cycle = adj.getData("height");
            this.nodeHelper.rectangle.render("fill", lab, qualifier, cycle, type);
          },
          /**
           * @param {?} opt_attributes
           * @param {?} value
           * @return {?}
           */
          contains : function(opt_attributes, value) {
            var attributes = opt_attributes.pos.getc(true);
            var actual = opt_attributes.getData("width");
            var epsilon = opt_attributes.getData("height");
            return this.nodeHelper.rectangle.contains(attributes, value, actual, epsilon);
          }
        },
        triangle : {
          /**
           * @param {?} adj
           * @param {?} type
           * @return {undefined}
           */
          render : function(adj, type) {
            var lab = adj.pos.getc(true);
            var qualifier = adj.getData("dim");
            this.nodeHelper.triangle.render("fill", lab, qualifier, type);
          },
          /**
           * @param {?} opt_attributes
           * @param {?} value
           * @return {?}
           */
          contains : function(opt_attributes, value) {
            var attributes = opt_attributes.pos.getc(true);
            var actual = opt_attributes.getData("dim");
            return this.nodeHelper.triangle.contains(attributes, value, actual);
          }
        },
        star : {
          /**
           * @param {?} adj
           * @param {?} type
           * @return {undefined}
           */
          render : function(adj, type) {
            var lab = adj.pos.getc(true);
            var qualifier = adj.getData("dim");
            this.nodeHelper.star.render("fill", lab, qualifier, type);
          },
          /**
           * @param {?} opt_attributes
           * @param {?} value
           * @return {?}
           */
          contains : function(opt_attributes, value) {
            var attributes = opt_attributes.pos.getc(true);
            var actual = opt_attributes.getData("dim");
            return this.nodeHelper.star.contains(attributes, value, actual);
          }
        }
      });
      Hypertree.Plot.EdgeTypes = new Class({
        /** @type {function (): undefined} */
        none : $.empty,
        line : {
          /**
           * @param {?} adj
           * @param {?} type
           * @return {undefined}
           */
          render : function(adj, type) {
            var from = adj.nodeFrom.pos.getc(true);
            var lab = adj.nodeTo.pos.getc(true);
            this.edgeHelper.line.render(from, lab, type);
          },
          /**
           * @param {?} opt_attributes
           * @param {?} value
           * @return {?}
           */
          contains : function(opt_attributes, value) {
            var attributes = opt_attributes.nodeFrom.pos.getc(true);
            var pdataOld = opt_attributes.nodeTo.pos.getc(true);
            return this.edgeHelper.line.contains(attributes, pdataOld, value, this.edge.epsilon);
          }
        },
        arrow : {
          /**
           * @param {?} adj
           * @param {?} type
           * @return {undefined}
           */
          render : function(adj, type) {
            var from = adj.nodeFrom.pos.getc(true);
            var lab = adj.nodeTo.pos.getc(true);
            var qualifier = adj.getData("dim");
            var direction = adj.data.$direction;
            var cycle = direction && (direction.length > 1 && direction[0] != adj.nodeFrom.id);
            this.edgeHelper.arrow.render(from, lab, qualifier, cycle, type);
          },
          /**
           * @param {?} opt_attributes
           * @param {?} value
           * @return {?}
           */
          contains : function(opt_attributes, value) {
            var attributes = opt_attributes.nodeFrom.pos.getc(true);
            var pdataOld = opt_attributes.nodeTo.pos.getc(true);
            return this.edgeHelper.arrow.contains(attributes, pdataOld, value, this.edge.epsilon);
          }
        }
      });
    })($jit.ForceDirected);
    $jit.TM = {};
    var Hypertree = $jit.TM;
    /** @type {boolean} */
    $jit.TM.$extend = true;
    Hypertree.Base = {
      layout : {
        orientation : "h",
        /**
         * @return {?}
         */
        vertical : function() {
          return this.orientation == "v";
        },
        /**
         * @return {?}
         */
        horizontal : function() {
          return this.orientation == "h";
        },
        /**
         * @return {undefined}
         */
        change : function() {
          /** @type {string} */
          this.orientation = this.vertical() ? "h" : "v";
        }
      },
      /**
       * @param {?} controller
       * @return {undefined}
       */
      initialize : function(controller) {
        var config = {
          orientation : "h",
          titleHeight : 13,
          offset : 2,
          levelsToShow : 0,
          constrained : false,
          animate : false,
          Node : {
            type : "rectangle",
            overridable : true,
            width : 3,
            height : 3,
            color : "#444"
          },
          Label : {
            textAlign : "center",
            textBaseline : "top"
          },
          Edge : {
            type : "none"
          },
          duration : 700,
          fps : 45
        };
        this.controller = this.config = $.merge(Options("Canvas", "Node", "Edge", "Fx", "Controller", "Tips", "NodeStyles", "Events", "Navigation", "Label"), config, controller);
        this.layout.orientation = this.config.orientation;
        var canvasConfig = this.config;
        if (canvasConfig.useCanvas) {
          this.canvas = canvasConfig.useCanvas;
          /** @type {string} */
          this.config.labelContainer = this.canvas.id + "-label";
        } else {
          if (canvasConfig.background) {
            canvasConfig.background = $.merge({
              type : "Circles"
            }, canvasConfig.background);
          }
          this.canvas = new Canvas(this, canvasConfig);
          /** @type {string} */
          this.config.labelContainer = (typeof canvasConfig.injectInto == "string" ? canvasConfig.injectInto : canvasConfig.injectInto.id) + "-label";
        }
        this.graphOptions = {
          /** @type {function (number, (number|string)): undefined} */
          klass : Vector,
          Node : {
            selected : false,
            exist : true,
            drawn : true
          }
        };
        this.graph = new Graph(this.graphOptions, this.config.Node, this.config.Edge);
        this.labels = new Hypertree.Label[canvasConfig.Label.type](this);
        this.fx = new Hypertree.Plot(this);
        this.op = new Hypertree.Op(this);
        this.group = new Hypertree.Group(this);
        this.geom = new Hypertree.Geom(this);
        /** @type {null} */
        this.clickedNode = null;
        /** @type {boolean} */
        this.busy = false;
        this.initializeExtras();
      },
      /**
       * @return {undefined}
       */
      refresh : function() {
        if (this.busy) {
          return;
        }
        /** @type {boolean} */
        this.busy = true;
        var that = this;
        if (this.config.animate) {
          this.compute("end");
          if (this.config.levelsToShow > 0) {
            this.geom.setRightLevelToShow(this.graph.getNode(this.clickedNode && this.clickedNode.id || this.root));
          }
          this.fx.animate($.merge(this.config, {
            modes : ["linear", "node-property:width:height"],
            /**
             * @return {undefined}
             */
            onComplete : function() {
              /** @type {boolean} */
              that.busy = false;
            }
          }));
        } else {
          var type = this.config.Label.type;
          if (type != "Native") {
            that = this;
            this.graph.eachNode(function(from) {
              that.labels.hideLabel(from, false);
            });
          }
          /** @type {boolean} */
          this.busy = false;
          this.compute();
          if (this.config.levelsToShow > 0) {
            this.geom.setRightLevelToShow(this.graph.getNode(this.clickedNode && this.clickedNode.id || this.root));
          }
          this.plot();
        }
      },
      /**
       * @return {undefined}
       */
      plot : function() {
        this.fx.plot();
      },
      /**
       * @param {?} n
       * @return {?}
       */
      leaf : function(n) {
        return n.getSubnodes([1, 1], "ignore").length == 0;
      },
      /**
       * @param {?} n
       * @return {undefined}
       */
      enter : function(n) {
        if (this.busy) {
          return;
        }
        /** @type {boolean} */
        this.busy = true;
        var that = this;
        var config = this.config;
        var graph = this.graph;
        var node = n;
        var previousClickedNode = this.clickedNode;
        var callback = {
          /**
           * @return {undefined}
           */
          onComplete : function() {
            if (config.levelsToShow > 0) {
              that.geom.setRightLevelToShow(n);
            }
            if (config.levelsToShow > 0 || config.request) {
              that.compute();
            }
            if (config.animate) {
              graph.nodeList.setData("alpha", 0, "end");
              n.eachSubgraph(function(n) {
                n.setData("alpha", 1, "end");
              }, "ignore");
              that.fx.animate({
                duration : 500,
                modes : ["node-property:alpha"],
                /**
                 * @return {undefined}
                 */
                onComplete : function() {
                  that.clickedNode = node;
                  that.compute("end");
                  that.clickedNode = previousClickedNode;
                  that.fx.animate({
                    modes : ["linear", "node-property:width:height"],
                    duration : 1E3,
                    /**
                     * @return {undefined}
                     */
                    onComplete : function() {
                      /** @type {boolean} */
                      that.busy = false;
                      that.clickedNode = node;
                    }
                  });
                }
              });
            } else {
              /** @type {boolean} */
              that.busy = false;
              that.clickedNode = n;
              that.refresh();
            }
          }
        };
        if (config.request) {
          this.requestNodes(node, callback);
        } else {
          callback.onComplete();
        }
      },
      /**
       * @return {undefined}
       */
      out : function() {
        if (this.busy) {
          return;
        }
        /** @type {boolean} */
        this.busy = true;
        /** @type {boolean} */
        this.events.hoveredNode = false;
        var that = this;
        var config = this.config;
        var graph = this.graph;
        var args = graph.getNode(this.clickedNode && this.clickedNode.id || this.root).getParents();
        var parent = args[0];
        var clickedNode = parent;
        var previousClickedNode = this.clickedNode;
        if (!parent) {
          /** @type {boolean} */
          this.busy = false;
          return;
        }
        callback = {
          /**
           * @return {undefined}
           */
          onComplete : function() {
            that.clickedNode = parent;
            if (config.request) {
              that.requestNodes(parent, {
                /**
                 * @return {undefined}
                 */
                onComplete : function() {
                  that.compute();
                  that.plot();
                  /** @type {boolean} */
                  that.busy = false;
                }
              });
            } else {
              that.compute();
              that.plot();
              /** @type {boolean} */
              that.busy = false;
            }
          }
        };
        if (config.levelsToShow > 0) {
          this.geom.setRightLevelToShow(parent);
        }
        if (config.animate) {
          this.clickedNode = clickedNode;
          this.compute("end");
          this.clickedNode = previousClickedNode;
          this.fx.animate({
            modes : ["linear", "node-property:width:height"],
            duration : 1E3,
            /**
             * @return {undefined}
             */
            onComplete : function() {
              that.clickedNode = clickedNode;
              graph.eachNode(function(n) {
                n.setDataset(["current", "end"], {
                  alpha : [0, 1]
                });
              }, "ignore");
              previousClickedNode.eachSubgraph(function(n) {
                n.setData("alpha", 1);
              }, "ignore");
              that.fx.animate({
                duration : 500,
                modes : ["node-property:alpha"],
                /**
                 * @return {undefined}
                 */
                onComplete : function() {
                  callback.onComplete();
                }
              });
            }
          });
        } else {
          callback.onComplete();
        }
      },
      /**
       * @param {Array} node
       * @param {?} onComplete
       * @return {undefined}
       */
      requestNodes : function(node, onComplete) {
        var handler = $.merge(this.controller, onComplete);
        var lev = this.config.levelsToShow;
        if (handler.request) {
          /** @type {Array} */
          var leaves = [];
          var d = node._depth;
          node.eachLevel(0, lev, function(n) {
            /** @type {number} */
            var nodeLevel = lev - (n._depth - d);
            if (n.drawn && (!n.anySubnode() && nodeLevel > 0)) {
              leaves.push(n);
              /** @type {number} */
              n._level = nodeLevel;
            }
          });
          this.group.requestNodes(leaves, handler);
        } else {
          handler.onComplete();
        }
      },
      /**
       * @return {undefined}
       */
      reposition : function() {
        this.compute("end");
      }
    };
    Hypertree.Op = new Class({
      Implements : Graph.Op,
      /**
       * @param {?} viz
       * @return {undefined}
       */
      initialize : function(viz) {
        this.viz = viz;
      }
    });
    Hypertree.Geom = new Class({
      Implements : Graph.Geom,
      /**
       * @return {?}
       */
      getRightLevelToShow : function() {
        return this.viz.config.levelsToShow;
      },
      /**
       * @param {?} node
       * @return {undefined}
       */
      setRightLevelToShow : function(node) {
        var y = this.getRightLevelToShow();
        var fx = this.viz.labels;
        node.eachLevel(0, y + 1, function(from) {
          /** @type {number} */
          var x = from._depth - node._depth;
          if (x > y) {
            /** @type {boolean} */
            from.drawn = false;
            /** @type {boolean} */
            from.exist = false;
            /** @type {boolean} */
            from.ignore = true;
            fx.hideLabel(from, false);
          } else {
            /** @type {boolean} */
            from.drawn = true;
            /** @type {boolean} */
            from.exist = true;
            delete from.ignore;
          }
        });
        /** @type {boolean} */
        node.drawn = true;
        delete node.ignore;
      }
    });
    Hypertree.Group = new Class({
      /**
       * @param {Object} viz
       * @return {undefined}
       */
      initialize : function(viz) {
        /** @type {Object} */
        this.viz = viz;
        this.canvas = viz.canvas;
        this.config = viz.config;
      },
      /**
       * @param {Array} nodes
       * @param {?} controller
       * @return {undefined}
       */
      requestNodes : function(nodes, controller) {
        /** @type {number} */
        var counter = 0;
        var len = nodes.length;
        var nodeSelected = {};
        /**
         * @return {undefined}
         */
        var complete = function() {
          controller.onComplete();
        };
        var viz = this.viz;
        if (len == 0) {
          complete();
        }
        /** @type {number} */
        var i = 0;
        for (;i < len;i++) {
          nodeSelected[nodes[i].id] = nodes[i];
          controller.request(nodes[i].id, nodes[i]._level, {
            /**
             * @param {number} adj
             * @param {?} lab
             * @return {undefined}
             */
            onComplete : function(adj, lab) {
              if (lab && lab.children) {
                /** @type {number} */
                lab.id = adj;
                viz.op.sum(lab, {
                  type : "nothing"
                });
              }
              if (++counter == len) {
                viz.graph.computeLevels(viz.root, 0);
                complete();
              }
            }
          });
        }
      }
    });
    Hypertree.Plot = new Class({
      Implements : Graph.Plot,
      /**
       * @param {Object} viz
       * @return {undefined}
       */
      initialize : function(viz) {
        /** @type {Object} */
        this.viz = viz;
        this.config = viz.config;
        this.node = this.config.Node;
        this.edge = this.config.Edge;
        this.animation = new Animation;
        this.nodeTypes = new Hypertree.Plot.NodeTypes;
        this.edgeTypes = new Hypertree.Plot.EdgeTypes;
        this.labels = viz.labels;
      },
      /**
       * @param {?} opt
       * @param {boolean} animating
       * @return {undefined}
       */
      plot : function(opt, animating) {
        var viz = this.viz;
        var graph = viz.graph;
        viz.canvas.clear();
        this.plotTree(graph.getNode(viz.clickedNode && viz.clickedNode.id || viz.root), $.merge(viz.config, opt || {}, {
          withLabels : true,
          hideLabels : false,
          /**
           * @param {?} node
           * @param {Object} x
           * @return {?}
           */
          plotSubtree : function(node, x) {
            return node.anySubnode("exist");
          }
        }), animating);
      }
    });
    Hypertree.Label = {};
    Hypertree.Label.Native = new Class({
      Implements : Graph.Label.Native,
      /**
       * @param {Object} viz
       * @return {undefined}
       */
      initialize : function(viz) {
        this.config = viz.config;
        this.leaf = viz.leaf;
      },
      /**
       * @param {?} canvas
       * @param {Object} node
       * @param {?} opt
       * @return {undefined}
       */
      renderLabel : function(canvas, node, opt) {
        if (!this.leaf(node) && !this.config.titleHeight) {
          return;
        }
        var pos = node.pos.getc(true);
        var ctx = canvas.getCtx();
        var width = node.getData("width");
        var constrainedHeight = node.getData("height");
        var opposite = pos.x + width / 2;
        var posY = pos.y;
        ctx.fillText(node.name, opposite, posY, width);
      }
    });
    Hypertree.Label.SVG = new Class({
      Implements : Graph.Label.SVG,
      /**
       * @param {?} viz
       * @return {undefined}
       */
      initialize : function(viz) {
        this.viz = viz;
        this.leaf = viz.leaf;
        this.config = viz.config;
      },
      /**
       * @param {?} from
       * @param {?} lab
       * @param {?} options
       * @return {undefined}
       */
      placeLabel : function(from, lab, options) {
        var pos = lab.pos.getc(true);
        var canvas = this.viz.canvas;
        var ox = canvas.translateOffsetX;
        var oy = canvas.translateOffsetY;
        var sx = canvas.scaleOffsetX;
        var sy = canvas.scaleOffsetY;
        var $cont = canvas.getSize();
        var tl = {
          x : Math.round(pos.x * sx + ox + $cont.width / 2),
          y : Math.round(pos.y * sy + oy + $cont.height / 2)
        };
        from.setAttribute("x", tl.x);
        from.setAttribute("y", tl.y);
        if (!this.leaf(lab) && !this.config.titleHeight) {
          /** @type {string} */
          from.style.display = "none";
        }
        options.onPlaceLabel(from, lab);
      }
    });
    Hypertree.Label.HTML = new Class({
      Implements : Graph.Label.HTML,
      /**
       * @param {?} viz
       * @return {undefined}
       */
      initialize : function(viz) {
        this.viz = viz;
        this.leaf = viz.leaf;
        this.config = viz.config;
      },
      /**
       * @param {?} from
       * @param {?} lab
       * @param {?} options
       * @return {undefined}
       */
      placeLabel : function(from, lab, options) {
        var size = lab.pos.getc(true);
        var canvas = this.viz.canvas;
        var ox = canvas.translateOffsetX;
        var oy = canvas.translateOffsetY;
        var sx = canvas.scaleOffsetX;
        var sy = canvas.scaleOffsetY;
        var $cont = canvas.getSize();
        var pos = {
          x : Math.round(size.x * sx + ox + $cont.width / 2),
          y : Math.round(size.y * sy + oy + $cont.height / 2)
        };
        var cs = from.style;
        /** @type {string} */
        cs.left = pos.x + "px";
        /** @type {string} */
        cs.top = pos.y + "px";
        /** @type {string} */
        cs.width = lab.getData("width") * sx + "px";
        /** @type {string} */
        cs.height = lab.getData("height") * sy + "px";
        /** @type {number} */
        cs.zIndex = lab._depth * 100;
        /** @type {string} */
        cs.display = "";
        if (!this.leaf(lab) && !this.config.titleHeight) {
          /** @type {string} */
          from.style.display = "none";
        }
        options.onPlaceLabel(from, lab);
      }
    });
    Hypertree.Plot.NodeTypes = new Class({
      none : {
        /** @type {function (): undefined} */
        render : $.empty
      },
      rectangle : {
        /**
         * @param {?} adj
         * @param {?} lab
         * @param {?} event
         * @return {undefined}
         */
        render : function(adj, lab, event) {
          var orn = this.viz.leaf(adj);
          var config = this.config;
          var offst = config.offset;
          var titleHeight = config.titleHeight;
          var pos = adj.pos.getc(true);
          var width = adj.getData("width");
          var height = adj.getData("height");
          var border = adj.getData("border");
          var ctx = lab.getCtx();
          var posx = pos.x + offst / 2;
          var posy = pos.y + offst / 2;
          if (width <= offst || height <= offst) {
            return;
          }
          if (orn) {
            if (config.cushion) {
              var radgrad = ctx.createRadialGradient(posx + (width - offst) / 2, posy + (height - offst) / 2, 1, posx + (width - offst) / 2, posy + (height - offst) / 2, width < height ? height : width);
              var color = adj.getData("color");
              var colorend = $.rgbToHex($.map($.hexToRgb(color), function(dataAndEvents) {
                return dataAndEvents * 0.2 >> 0;
              }));
              radgrad.addColorStop(0, color);
              radgrad.addColorStop(1, colorend);
              ctx.fillStyle = radgrad;
            }
            ctx.fillRect(posx, posy, width - offst, height - offst);
            if (border) {
              ctx.save();
              ctx.strokeStyle = border;
              ctx.strokeRect(posx, posy, width - offst, height - offst);
              ctx.restore();
            }
          } else {
            if (titleHeight > 0) {
              ctx.fillRect(pos.x + offst / 2, pos.y + offst / 2, width - offst, titleHeight - offst);
              if (border) {
                ctx.save();
                ctx.strokeStyle = border;
                ctx.strokeRect(pos.x + offst / 2, pos.y + offst / 2, width - offst, height - offst);
                ctx.restore();
              }
            }
          }
        },
        /**
         * @param {?} opt_attributes
         * @param {?} value
         * @return {?}
         */
        contains : function(opt_attributes, value) {
          if (this.viz.clickedNode && !opt_attributes.isDescendantOf(this.viz.clickedNode.id) || opt_attributes.ignore) {
            return false;
          }
          var offsetCoordinate = opt_attributes.pos.getc(true);
          var actual = opt_attributes.getData("width");
          var leaf = this.viz.leaf(opt_attributes);
          var epsilon = leaf ? opt_attributes.getData("height") : this.config.titleHeight;
          return this.nodeHelper.rectangle.contains({
            x : offsetCoordinate.x + actual / 2,
            y : offsetCoordinate.y + epsilon / 2
          }, value, actual, epsilon);
        }
      }
    });
    Hypertree.Plot.EdgeTypes = new Class({
      /** @type {function (): undefined} */
      none : $.empty
    });
    Hypertree.SliceAndDice = new Class({
      Implements : [valid, Extras, Hypertree.Base, Layout.TM.SliceAndDice]
    });
    Hypertree.Squarified = new Class({
      Implements : [valid, Extras, Hypertree.Base, Layout.TM.Squarified]
    });
    Hypertree.Strip = new Class({
      Implements : [valid, Extras, Hypertree.Base, Layout.TM.Strip]
    });
    $jit.RGraph = new Class({
      Implements : [valid, Extras, Layout.Radial],
      /**
       * @param {?} controller
       * @return {undefined}
       */
      initialize : function(controller) {
        var $RGraph = $jit.RGraph;
        var config = {
          interpolation : "linear",
          levelDistance : 100
        };
        this.controller = this.config = $.merge(Options("Canvas", "Node", "Edge", "Fx", "Controller", "Tips", "NodeStyles", "Events", "Navigation", "Label"), config, controller);
        var canvasConfig = this.config;
        if (canvasConfig.useCanvas) {
          this.canvas = canvasConfig.useCanvas;
          /** @type {string} */
          this.config.labelContainer = this.canvas.id + "-label";
        } else {
          if (canvasConfig.background) {
            canvasConfig.background = $.merge({
              type : "Circles"
            }, canvasConfig.background);
          }
          this.canvas = new Canvas(this, canvasConfig);
          /** @type {string} */
          this.config.labelContainer = (typeof canvasConfig.injectInto == "string" ? canvasConfig.injectInto : canvasConfig.injectInto.id) + "-label";
        }
        this.graphOptions = {
          /** @type {function (number, number): undefined} */
          klass : Transform,
          Node : {
            selected : false,
            exist : true,
            drawn : true
          }
        };
        this.graph = new Graph(this.graphOptions, this.config.Node, this.config.Edge);
        this.labels = new $RGraph.Label[canvasConfig.Label.type](this);
        this.fx = new $RGraph.Plot(this, $RGraph);
        this.op = new $RGraph.Op(this);
        /** @type {null} */
        this.json = null;
        /** @type {null} */
        this.root = null;
        /** @type {boolean} */
        this.busy = false;
        /** @type {boolean} */
        this.parent = false;
        this.initializeExtras();
      },
      /**
       * @return {?}
       */
      createLevelDistanceFunc : function() {
        var ld = this.config.levelDistance;
        return function(node) {
          return(node._depth + 1) * ld;
        };
      },
      /**
       * @return {undefined}
       */
      refresh : function() {
        this.compute();
        this.plot();
      },
      /**
       * @return {undefined}
       */
      reposition : function() {
        this.compute("end");
      },
      /**
       * @return {undefined}
       */
      plot : function() {
        this.fx.plot();
      },
      /**
       * @param {?} id
       * @return {?}
       */
      getNodeAndParentAngle : function(id) {
        /** @type {boolean} */
        var theta = false;
        var node = this.graph.getNode(id);
        var codeSegments = node.getParents();
        var p = codeSegments.length > 0 ? codeSegments[0] : false;
        if (p) {
          var previous = p.pos.getc();
          var projection = node.pos.getc();
          var newPos = previous.add(projection.scale(-1));
          /** @type {number} */
          theta = Math.atan2(newPos.y, newPos.x);
          if (theta < 0) {
            theta += 2 * Math.PI;
          }
        }
        return{
          parent : p,
          theta : theta
        };
      },
      /**
       * @param {?} par
       * @param {string} id
       * @return {undefined}
       */
      tagChildren : function(par, id) {
        if (par.angleSpan) {
          /** @type {Array} */
          var adjs = [];
          par.eachAdjacency(function(elem) {
            adjs.push(elem.nodeTo);
          }, "ignore");
          /** @type {number} */
          var len = adjs.length;
          /** @type {number} */
          var i = 0;
          for (;i < len && id != adjs[i].id;i++) {
          }
          /** @type {number} */
          var j = (i + 1) % len;
          /** @type {number} */
          var k = 0;
          for (;id != adjs[j].id;j = (j + 1) % len) {
            /** @type {number} */
            adjs[j].dist = k++;
          }
        }
      },
      /**
       * @param {?} adj
       * @param {?} lab
       * @return {undefined}
       */
      onClick : function(adj, lab) {
        if (this.root != adj && !this.busy) {
          /** @type {boolean} */
          this.busy = true;
          this.root = adj;
          var that = this;
          this.controller.onBeforeCompute(this.graph.getNode(adj));
          var obj = this.getNodeAndParentAngle(adj);
          this.tagChildren(obj.parent, adj);
          this.parent = obj.parent;
          this.compute("end");
          /** @type {number} */
          var lambda = obj.theta - obj.parent.endPos.theta;
          this.graph.eachNode(function(elem) {
            elem.endPos.set(elem.endPos.getp().add($P(lambda, 0)));
          });
          var mode = this.config.interpolation;
          lab = $.merge({
            /** @type {function (): undefined} */
            onComplete : $.empty
          }, lab || {});
          this.fx.animate($.merge({
            hideLabels : true,
            modes : [mode]
          }, lab, {
            /**
             * @return {undefined}
             */
            onComplete : function() {
              /** @type {boolean} */
              that.busy = false;
              lab.onComplete();
            }
          }));
        }
      }
    });
    /** @type {boolean} */
    $jit.RGraph.$extend = true;
    (function(Hypertree) {
      Hypertree.Op = new Class({
        Implements : Graph.Op
      });
      Hypertree.Plot = new Class({
        Implements : Graph.Plot
      });
      Hypertree.Label = {};
      Hypertree.Label.Native = new Class({
        Implements : Graph.Label.Native
      });
      Hypertree.Label.SVG = new Class({
        Implements : Graph.Label.SVG,
        /**
         * @param {?} viz
         * @return {undefined}
         */
        initialize : function(viz) {
          this.viz = viz;
        },
        /**
         * @param {?} from
         * @param {?} lab
         * @param {?} options
         * @return {undefined}
         */
        placeLabel : function(from, lab, options) {
          var pos = lab.pos.getc(true);
          var canvas = this.viz.canvas;
          var ox = canvas.translateOffsetX;
          var oy = canvas.translateOffsetY;
          var sx = canvas.scaleOffsetX;
          var sy = canvas.scaleOffsetY;
          var $cont = canvas.getSize();
          var tl = {
            x : Math.round(pos.x * sx + ox + $cont.width / 2),
            y : Math.round(pos.y * sy + oy + $cont.height / 2)
          };
          from.setAttribute("x", tl.x);
          from.setAttribute("y", tl.y);
          options.onPlaceLabel(from, lab);
        }
      });
      Hypertree.Label.HTML = new Class({
        Implements : Graph.Label.HTML,
        /**
         * @param {?} viz
         * @return {undefined}
         */
        initialize : function(viz) {
          this.viz = viz;
        },
        /**
         * @param {?} from
         * @param {?} lab
         * @param {?} options
         * @return {undefined}
         */
        placeLabel : function(from, lab, options) {
          var pos = lab.pos.getc(true);
          var canvas = this.viz.canvas;
          var ox = canvas.translateOffsetX;
          var oy = canvas.translateOffsetY;
          var sx = canvas.scaleOffsetX;
          var sy = canvas.scaleOffsetY;
          var $cont = canvas.getSize();
          var labelPos = {
            x : Math.round(pos.x * sx + ox + $cont.width / 2),
            y : Math.round(pos.y * sy + oy + $cont.height / 2)
          };
          var style = from.style;
          /** @type {string} */
          style.left = labelPos.x + "px";
          /** @type {string} */
          style.top = labelPos.y + "px";
          /** @type {string} */
          style.display = this.fitsInCanvas(labelPos, canvas) ? "" : "none";
          options.onPlaceLabel(from, lab);
        }
      });
      Hypertree.Plot.NodeTypes = new Class({
        none : {
          /** @type {function (): undefined} */
          render : $.empty,
          contains : $.lambda(false)
        },
        circle : {
          /**
           * @param {?} adj
           * @param {?} type
           * @return {undefined}
           */
          render : function(adj, type) {
            var lab = adj.pos.getc(true);
            var qualifier = adj.getData("dim");
            this.nodeHelper.circle.render("fill", lab, qualifier, type);
          },
          /**
           * @param {?} opt_attributes
           * @param {?} value
           * @return {?}
           */
          contains : function(opt_attributes, value) {
            var attributes = opt_attributes.pos.getc(true);
            var actual = opt_attributes.getData("dim");
            return this.nodeHelper.circle.contains(attributes, value, actual);
          }
        },
        ellipse : {
          /**
           * @param {?} adj
           * @param {?} type
           * @return {undefined}
           */
          render : function(adj, type) {
            var lab = adj.pos.getc(true);
            var qualifier = adj.getData("width");
            var cycle = adj.getData("height");
            this.nodeHelper.ellipse.render("fill", lab, qualifier, cycle, type);
          },
          /**
           * @param {?} opt_attributes
           * @param {?} value
           * @return {?}
           */
          contains : function(opt_attributes, value) {
            var attributes = opt_attributes.pos.getc(true);
            var actual = opt_attributes.getData("width");
            var epsilon = opt_attributes.getData("height");
            return this.nodeHelper.ellipse.contains(attributes, value, actual, epsilon);
          }
        },
        square : {
          /**
           * @param {?} adj
           * @param {?} type
           * @return {undefined}
           */
          render : function(adj, type) {
            var lab = adj.pos.getc(true);
            var qualifier = adj.getData("dim");
            this.nodeHelper.square.render("fill", lab, qualifier, type);
          },
          /**
           * @param {?} opt_attributes
           * @param {?} value
           * @return {?}
           */
          contains : function(opt_attributes, value) {
            var attributes = opt_attributes.pos.getc(true);
            var actual = opt_attributes.getData("dim");
            return this.nodeHelper.square.contains(attributes, value, actual);
          }
        },
        rectangle : {
          /**
           * @param {?} adj
           * @param {?} type
           * @return {undefined}
           */
          render : function(adj, type) {
            var lab = adj.pos.getc(true);
            var qualifier = adj.getData("width");
            var cycle = adj.getData("height");
            this.nodeHelper.rectangle.render("fill", lab, qualifier, cycle, type);
          },
          /**
           * @param {?} opt_attributes
           * @param {?} value
           * @return {?}
           */
          contains : function(opt_attributes, value) {
            var attributes = opt_attributes.pos.getc(true);
            var actual = opt_attributes.getData("width");
            var epsilon = opt_attributes.getData("height");
            return this.nodeHelper.rectangle.contains(attributes, value, actual, epsilon);
          }
        },
        triangle : {
          /**
           * @param {?} adj
           * @param {?} lab
           * @return {undefined}
           */
          render : function(adj, lab) {
            var cycle = adj.pos.getc(true);
            var qualifier = adj.getData("dim");
            this.nodeHelper.triangle.render("fill", cycle, qualifier, lab);
          },
          /**
           * @param {?} opt_attributes
           * @param {?} value
           * @return {?}
           */
          contains : function(opt_attributes, value) {
            var attributes = opt_attributes.pos.getc(true);
            var actual = opt_attributes.getData("dim");
            return this.nodeHelper.triangle.contains(attributes, value, actual);
          }
        },
        star : {
          /**
           * @param {?} adj
           * @param {?} type
           * @return {undefined}
           */
          render : function(adj, type) {
            var lab = adj.pos.getc(true);
            var qualifier = adj.getData("dim");
            this.nodeHelper.star.render("fill", lab, qualifier, type);
          },
          /**
           * @param {?} opt_attributes
           * @param {?} value
           * @return {?}
           */
          contains : function(opt_attributes, value) {
            var attributes = opt_attributes.pos.getc(true);
            var actual = opt_attributes.getData("dim");
            return this.nodeHelper.star.contains(attributes, value, actual);
          }
        }
      });
      Hypertree.Plot.EdgeTypes = new Class({
        /** @type {function (): undefined} */
        none : $.empty,
        line : {
          /**
           * @param {?} adj
           * @param {?} type
           * @return {undefined}
           */
          render : function(adj, type) {
            var from = adj.nodeFrom.pos.getc(true);
            var lab = adj.nodeTo.pos.getc(true);
            this.edgeHelper.line.render(from, lab, type);
          },
          /**
           * @param {?} opt_attributes
           * @param {?} value
           * @return {?}
           */
          contains : function(opt_attributes, value) {
            var attributes = opt_attributes.nodeFrom.pos.getc(true);
            var pdataOld = opt_attributes.nodeTo.pos.getc(true);
            return this.edgeHelper.line.contains(attributes, pdataOld, value, this.edge.epsilon);
          }
        },
        arrow : {
          /**
           * @param {?} adj
           * @param {?} type
           * @return {undefined}
           */
          render : function(adj, type) {
            var from = adj.nodeFrom.pos.getc(true);
            var lab = adj.nodeTo.pos.getc(true);
            var qualifier = adj.getData("dim");
            var direction = adj.data.$direction;
            var cycle = direction && (direction.length > 1 && direction[0] != adj.nodeFrom.id);
            this.edgeHelper.arrow.render(from, lab, qualifier, cycle, type);
          },
          /**
           * @param {?} opt_attributes
           * @param {?} value
           * @return {?}
           */
          contains : function(opt_attributes, value) {
            var attributes = opt_attributes.nodeFrom.pos.getc(true);
            var pdataOld = opt_attributes.nodeTo.pos.getc(true);
            return this.edgeHelper.arrow.contains(attributes, pdataOld, value, this.edge.epsilon);
          }
        }
      });
    })($jit.RGraph);
    /**
     * @param {?} c
     * @return {?}
     */
    Vector.prototype.moebiusTransformation = function(c) {
      var num = this.add(c);
      var den = c.$conjugate().$prod(this);
      den.x++;
      return num.$div(den);
    };
    /**
     * @param {?} graph
     * @param {Array} pos
     * @param {Array} array
     * @param {Event} key
     * @param {string} flags
     * @return {undefined}
     */
    Graph.Util.moebiusTransformation = function(graph, pos, array, key, flags) {
      this.eachNode(graph, function(obj) {
        /** @type {number} */
        var i = 0;
        for (;i < array.length;i++) {
          var p = pos[i].scale(-1);
          var prop = key ? key : array[i];
          obj.getPos(array[i]).set(obj.getPos(prop).getc().moebiusTransformation(p));
        }
      }, flags);
    };
    $jit.Hypertree = new Class({
      Implements : [valid, Extras, Layout.Radial],
      /**
       * @param {?} controller
       * @return {undefined}
       */
      initialize : function(controller) {
        var $Hypertree = $jit.Hypertree;
        var config = {
          radius : "auto",
          offset : 0,
          Edge : {
            type : "hyperline"
          },
          duration : 1500,
          fps : 35
        };
        this.controller = this.config = $.merge(Options("Canvas", "Node", "Edge", "Fx", "Tips", "NodeStyles", "Events", "Navigation", "Controller", "Label"), config, controller);
        var canvasConfig = this.config;
        if (canvasConfig.useCanvas) {
          this.canvas = canvasConfig.useCanvas;
          /** @type {string} */
          this.config.labelContainer = this.canvas.id + "-label";
        } else {
          if (canvasConfig.background) {
            canvasConfig.background = $.merge({
              type : "Circles"
            }, canvasConfig.background);
          }
          this.canvas = new Canvas(this, canvasConfig);
          /** @type {string} */
          this.config.labelContainer = (typeof canvasConfig.injectInto == "string" ? canvasConfig.injectInto : canvasConfig.injectInto.id) + "-label";
        }
        this.graphOptions = {
          /** @type {function (number, number): undefined} */
          klass : Transform,
          Node : {
            selected : false,
            exist : true,
            drawn : true
          }
        };
        this.graph = new Graph(this.graphOptions, this.config.Node, this.config.Edge);
        this.labels = new $Hypertree.Label[canvasConfig.Label.type](this);
        this.fx = new $Hypertree.Plot(this, $Hypertree);
        this.op = new $Hypertree.Op(this);
        /** @type {null} */
        this.json = null;
        /** @type {null} */
        this.root = null;
        /** @type {boolean} */
        this.busy = false;
        this.initializeExtras();
      },
      /**
       * @return {?}
       */
      createLevelDistanceFunc : function() {
        var r = this.getRadius();
        /** @type {number} */
        var depth = 0;
        /** @type {function (...[*]): number} */
        var max = Math.max;
        var config = this.config;
        this.graph.eachNode(function(node) {
          /** @type {number} */
          depth = max(node._depth, depth);
        }, "ignore");
        depth++;
        /**
         * @param {number} a
         * @return {?}
         */
        var genDistFunc = function(a) {
          return function(node) {
            node.scale = r;
            var d = node._depth + 1;
            /** @type {number} */
            var acum = 0;
            /** @type {function (*, *): number} */
            var pow = Math.pow;
            for (;d;) {
              acum += pow(a, d--);
            }
            return acum - config.offset;
          };
        };
        /** @type {number} */
        var i = 0.51;
        for (;i <= 1;i += 0.01) {
          /** @type {number} */
          var y = (1 - Math.pow(i, depth)) / (1 - i);
          if (y >= 2) {
            return genDistFunc(i - 0.01);
          }
        }
        return genDistFunc(0.75);
      },
      /**
       * @return {?}
       */
      getRadius : function() {
        var rad = this.config.radius;
        if (rad !== "auto") {
          return rad;
        }
        var ul = this.canvas.getSize();
        return Math.min(ul.width, ul.height) / 2;
      },
      /**
       * @param {boolean} dataAndEvents
       * @return {undefined}
       */
      refresh : function(dataAndEvents) {
        if (dataAndEvents) {
          this.reposition();
          this.graph.eachNode(function(node) {
            node.startPos.rho = node.pos.rho = node.endPos.rho;
            node.startPos.theta = node.pos.theta = node.endPos.theta;
          });
        } else {
          this.compute();
        }
        this.plot();
      },
      /**
       * @return {undefined}
       */
      reposition : function() {
        this.compute("end");
        var vector = this.graph.getNode(this.root).pos.getc().scale(-1);
        Graph.Util.moebiusTransformation(this.graph, [vector], ["end"], "end", "ignore");
        this.graph.eachNode(function(node) {
          if (node.ignore) {
            node.endPos.rho = node.pos.rho;
            node.endPos.theta = node.pos.theta;
          }
        });
      },
      /**
       * @return {undefined}
       */
      plot : function() {
        this.fx.plot();
      },
      /**
       * @param {?} adj
       * @param {?} lab
       * @return {undefined}
       */
      onClick : function(adj, lab) {
        var to = this.graph.getNode(adj).pos.getc(true);
        this.move(to, lab);
      },
      /**
       * @param {?} pos
       * @param {Object} opt
       * @return {undefined}
       */
      move : function(pos, opt) {
        var versor = getIndex(pos.x, pos.y);
        if (this.busy === false && versor.norm() < 1) {
          /** @type {boolean} */
          this.busy = true;
          var from = this.graph.getClosestNodeToPos(versor);
          var that = this;
          this.graph.computeLevels(from.id, 0);
          this.controller.onBeforeCompute(from);
          opt = $.merge({
            /** @type {function (): undefined} */
            onComplete : $.empty
          }, opt || {});
          this.fx.animate($.merge({
            modes : ["moebius"],
            hideLabels : true
          }, opt, {
            /**
             * @return {undefined}
             */
            onComplete : function() {
              /** @type {boolean} */
              that.busy = false;
              opt.onComplete();
            }
          }), versor);
        }
      }
    });
    /** @type {boolean} */
    $jit.Hypertree.$extend = true;
    (function(Hypertree) {
      Hypertree.Op = new Class({
        Implements : Graph.Op
      });
      Hypertree.Plot = new Class({
        Implements : Graph.Plot
      });
      Hypertree.Label = {};
      Hypertree.Label.Native = new Class({
        Implements : Graph.Label.Native,
        /**
         * @param {?} viz
         * @return {undefined}
         */
        initialize : function(viz) {
          this.viz = viz;
        },
        /**
         * @param {?} canvas
         * @param {Object} node
         * @param {?} opt
         * @return {undefined}
         */
        renderLabel : function(canvas, node, opt) {
          var ctx = canvas.getCtx();
          var coord = node.pos.getc(true);
          var s = this.viz.getRadius();
          ctx.fillText(node.name, coord.x * s, coord.y * s);
        }
      });
      Hypertree.Label.SVG = new Class({
        Implements : Graph.Label.SVG,
        /**
         * @param {?} viz
         * @return {undefined}
         */
        initialize : function(viz) {
          this.viz = viz;
        },
        /**
         * @param {?} from
         * @param {?} lab
         * @param {?} options
         * @return {undefined}
         */
        placeLabel : function(from, lab, options) {
          var pos = lab.pos.getc(true);
          var canvas = this.viz.canvas;
          var ox = canvas.translateOffsetX;
          var oy = canvas.translateOffsetY;
          var sx = canvas.scaleOffsetX;
          var sy = canvas.scaleOffsetY;
          var $cont = canvas.getSize();
          var r = this.viz.getRadius();
          var tl = {
            x : Math.round(pos.x * sx * r + ox + $cont.width / 2),
            y : Math.round(pos.y * sy * r + oy + $cont.height / 2)
          };
          from.setAttribute("x", tl.x);
          from.setAttribute("y", tl.y);
          options.onPlaceLabel(from, lab);
        }
      });
      Hypertree.Label.HTML = new Class({
        Implements : Graph.Label.HTML,
        /**
         * @param {?} viz
         * @return {undefined}
         */
        initialize : function(viz) {
          this.viz = viz;
        },
        /**
         * @param {?} from
         * @param {?} lab
         * @param {?} options
         * @return {undefined}
         */
        placeLabel : function(from, lab, options) {
          var pos = lab.pos.getc(true);
          var canvas = this.viz.canvas;
          var ox = canvas.translateOffsetX;
          var oy = canvas.translateOffsetY;
          var sx = canvas.scaleOffsetX;
          var sy = canvas.scaleOffsetY;
          var $cont = canvas.getSize();
          var r = this.viz.getRadius();
          var labelPos = {
            x : Math.round(pos.x * sx * r + ox + $cont.width / 2),
            y : Math.round(pos.y * sy * r + oy + $cont.height / 2)
          };
          var style = from.style;
          /** @type {string} */
          style.left = labelPos.x + "px";
          /** @type {string} */
          style.top = labelPos.y + "px";
          /** @type {string} */
          style.display = this.fitsInCanvas(labelPos, canvas) ? "" : "none";
          options.onPlaceLabel(from, lab);
        }
      });
      Hypertree.Plot.NodeTypes = new Class({
        none : {
          /** @type {function (): undefined} */
          render : $.empty,
          contains : $.lambda(false)
        },
        circle : {
          /**
           * @param {?} adj
           * @param {?} lab
           * @return {undefined}
           */
          render : function(adj, lab) {
            var node = this.node;
            var qualifier = adj.getData("dim");
            var cycle = adj.pos.getc();
            qualifier = node.transform ? qualifier * (1 - cycle.squaredNorm()) : qualifier;
            cycle.$scale(adj.scale);
            if (qualifier > 0.2) {
              this.nodeHelper.circle.render("fill", cycle, qualifier, lab);
            }
          },
          /**
           * @param {?} opt_attributes
           * @param {?} value
           * @return {?}
           */
          contains : function(opt_attributes, value) {
            var actual = opt_attributes.getData("dim");
            var attributes = opt_attributes.pos.getc().$scale(opt_attributes.scale);
            return this.nodeHelper.circle.contains(attributes, value, actual);
          }
        },
        ellipse : {
          /**
           * @param {?} adj
           * @param {?} lab
           * @return {undefined}
           */
          render : function(adj, lab) {
            var cycle = adj.pos.getc().$scale(adj.scale);
            var qualifier = adj.getData("width");
            var fix = adj.getData("height");
            this.nodeHelper.ellipse.render("fill", cycle, qualifier, fix, lab);
          },
          /**
           * @param {?} opt_attributes
           * @param {?} value
           * @return {?}
           */
          contains : function(opt_attributes, value) {
            var actual = opt_attributes.getData("width");
            var epsilon = opt_attributes.getData("height");
            var attributes = opt_attributes.pos.getc().$scale(opt_attributes.scale);
            return this.nodeHelper.circle.contains(attributes, value, actual, epsilon);
          }
        },
        square : {
          /**
           * @param {?} adj
           * @param {?} lab
           * @return {undefined}
           */
          render : function(adj, lab) {
            var node = this.node;
            var qualifier = adj.getData("dim");
            var cycle = adj.pos.getc();
            qualifier = node.transform ? qualifier * (1 - cycle.squaredNorm()) : qualifier;
            cycle.$scale(adj.scale);
            if (qualifier > 0.2) {
              this.nodeHelper.square.render("fill", cycle, qualifier, lab);
            }
          },
          /**
           * @param {?} opt_attributes
           * @param {?} value
           * @return {?}
           */
          contains : function(opt_attributes, value) {
            var actual = opt_attributes.getData("dim");
            var attributes = opt_attributes.pos.getc().$scale(opt_attributes.scale);
            return this.nodeHelper.square.contains(attributes, value, actual);
          }
        },
        rectangle : {
          /**
           * @param {?} adj
           * @param {?} lab
           * @return {undefined}
           */
          render : function(adj, lab) {
            var node = this.node;
            var qualifier = adj.getData("width");
            var fix = adj.getData("height");
            var cycle = adj.pos.getc();
            qualifier = node.transform ? qualifier * (1 - cycle.squaredNorm()) : qualifier;
            fix = node.transform ? fix * (1 - cycle.squaredNorm()) : fix;
            cycle.$scale(adj.scale);
            if (qualifier > 0.2 && fix > 0.2) {
              this.nodeHelper.rectangle.render("fill", cycle, qualifier, fix, lab);
            }
          },
          /**
           * @param {?} opt_attributes
           * @param {?} value
           * @return {?}
           */
          contains : function(opt_attributes, value) {
            var actual = opt_attributes.getData("width");
            var epsilon = opt_attributes.getData("height");
            var attributes = opt_attributes.pos.getc().$scale(opt_attributes.scale);
            return this.nodeHelper.rectangle.contains(attributes, value, actual, epsilon);
          }
        },
        triangle : {
          /**
           * @param {?} adj
           * @param {?} lab
           * @return {undefined}
           */
          render : function(adj, lab) {
            var node = this.node;
            var qualifier = adj.getData("dim");
            var cycle = adj.pos.getc();
            qualifier = node.transform ? qualifier * (1 - cycle.squaredNorm()) : qualifier;
            cycle.$scale(adj.scale);
            if (qualifier > 0.2) {
              this.nodeHelper.triangle.render("fill", cycle, qualifier, lab);
            }
          },
          /**
           * @param {?} opt_attributes
           * @param {?} value
           * @return {?}
           */
          contains : function(opt_attributes, value) {
            var actual = opt_attributes.getData("dim");
            var attributes = opt_attributes.pos.getc().$scale(opt_attributes.scale);
            return this.nodeHelper.triangle.contains(attributes, value, actual);
          }
        },
        star : {
          /**
           * @param {?} adj
           * @param {?} lab
           * @return {undefined}
           */
          render : function(adj, lab) {
            var node = this.node;
            var qualifier = adj.getData("dim");
            var cycle = adj.pos.getc();
            qualifier = node.transform ? qualifier * (1 - cycle.squaredNorm()) : qualifier;
            cycle.$scale(adj.scale);
            if (qualifier > 0.2) {
              this.nodeHelper.star.render("fill", cycle, qualifier, lab);
            }
          },
          /**
           * @param {?} opt_attributes
           * @param {?} value
           * @return {?}
           */
          contains : function(opt_attributes, value) {
            var actual = opt_attributes.getData("dim");
            var attributes = opt_attributes.pos.getc().$scale(opt_attributes.scale);
            return this.nodeHelper.star.contains(attributes, value, actual);
          }
        }
      });
      Hypertree.Plot.EdgeTypes = new Class({
        /** @type {function (): undefined} */
        none : $.empty,
        line : {
          /**
           * @param {?} adj
           * @param {?} type
           * @return {undefined}
           */
          render : function(adj, type) {
            var to = adj.nodeFrom.pos.getc(true);
            var from = adj.nodeTo.pos.getc(true);
            var r = adj.nodeFrom.scale;
            this.edgeHelper.line.render({
              x : to.x * r,
              y : to.y * r
            }, {
              x : from.x * r,
              y : from.y * r
            }, type);
          },
          /**
           * @param {?} opt_attributes
           * @param {?} value
           * @return {undefined}
           */
          contains : function(opt_attributes, value) {
            var to = opt_attributes.nodeFrom.pos.getc(true);
            var from = opt_attributes.nodeTo.pos.getc(true);
            var r = opt_attributes.nodeFrom.scale;
            this.edgeHelper.line.contains({
              x : to.x * r,
              y : to.y * r
            }, {
              x : from.x * r,
              y : from.y * r
            }, value, this.edge.epsilon);
          }
        },
        arrow : {
          /**
           * @param {?} adj
           * @param {?} lab
           * @return {undefined}
           */
          render : function(adj, lab) {
            var to = adj.nodeFrom.pos.getc(true);
            var from = adj.nodeTo.pos.getc(true);
            var r = adj.nodeFrom.scale;
            var qualifier = adj.getData("dim");
            var direction = adj.data.$direction;
            var cycle = direction && (direction.length > 1 && direction[0] != adj.nodeFrom.id);
            this.edgeHelper.arrow.render({
              x : to.x * r,
              y : to.y * r
            }, {
              x : from.x * r,
              y : from.y * r
            }, qualifier, cycle, lab);
          },
          /**
           * @param {?} opt_attributes
           * @param {?} value
           * @return {undefined}
           */
          contains : function(opt_attributes, value) {
            var to = opt_attributes.nodeFrom.pos.getc(true);
            var from = opt_attributes.nodeTo.pos.getc(true);
            var r = opt_attributes.nodeFrom.scale;
            this.edgeHelper.arrow.contains({
              x : to.x * r,
              y : to.y * r
            }, {
              x : from.x * r,
              y : from.y * r
            }, value, this.edge.epsilon);
          }
        },
        hyperline : {
          /**
           * @param {?} adj
           * @param {?} lab
           * @return {undefined}
           */
          render : function(adj, lab) {
            var from = adj.nodeFrom.pos.getc();
            var cycle = adj.nodeTo.pos.getc();
            var qualifier = this.viz.getRadius();
            this.edgeHelper.hyperline.render(from, cycle, qualifier, lab);
          },
          contains : $.lambda(false)
        }
      });
    })($jit.Hypertree);
  })();
  </script>
  <!-- Example File -->
  <script>
  
  var labelType, useGradients, nativeTextSupport, animate;
  
  (function() {
    var ua = navigator.userAgent,
        iStuff = ua.match(/iPhone/i) || ua.match(/iPad/i),
        typeOfCanvas = typeof HTMLCanvasElement,
        nativeCanvasSupport = (typeOfCanvas == \'object\' || typeOfCanvas == \'function\'),
        textSupport = nativeCanvasSupport
          && (typeof document.createElement(\'canvas\').getContext(\'2d\').fillText == \'function\');
    //I\'m setting this based on the fact that ExCanvas provides text support for IE
    //and that as of today iPhone/iPad current text support is lame
    labelType = (!nativeCanvasSupport || (textSupport && !iStuff))? \'Native\' : \'HTML\';
    nativeTextSupport = labelType == \'Native\';
    useGradients = nativeCanvasSupport;
    animate = !(iStuff || !nativeCanvasSupport);
  })();
  
  var Log = {
    elem: false,
    write: function(text){
      if (!this.elem)
        this.elem = document.getElementById(\'log\');
      this.elem.innerHTML = text;
      this.elem.style.left = (500 - this.elem.offsetWidth / 2) + \'px\';
    }
  };
  
  
  function init(){
      //init data
      var json = __data_will_locate_here__;
      //end
      var infovis = document.getElementById(\'infovis\');
      var w = infovis.offsetWidth - 50, h = infovis.offsetHeight - 50;
  
      //init Hypertree
      var ht = new $jit.Hypertree({
        //id of the visualization container
        injectInto: \'infovis\',
        //canvas width and height
        width: w,
        height: h,
        //Change node and edge styles such as
        //color, width and dimensions.
        Node: {
            dim: 9,
            color: "#f00"
        },
        Edge: {
            lineWidth: 2,
            color: "#088"
        },
        onBeforeCompute: function(node){
            Log.write("centering");
        },
        //Attach event handlers and add text to the
        //labels. This method is only triggered on label
        //creation
        onCreateLabel: function(domElement, node){
            domElement.innerHTML = node.name;
            $jit.util.addEvent(domElement, \'click\', function () {
                ht.onClick(node.id, {
                    onComplete: function() {
                        ht.controller.onComplete();
                    }
                });
            });
        },
        //Change node styles when labels are placed
        //or moved.
        onPlaceLabel: function(domElement, node){
            var style = domElement.style;
            style.display = \'\';
            style.cursor = \'pointer\';
            if (node._depth <= 1) {
                style.fontSize = "0.8em";
                style.color = "#ddd";
  
            } else if(node._depth == 2){
                style.fontSize = "0.7em";
                style.color = "#555";
  
            } else {
                style.display = \'none\';
            }
  
            var left = parseInt(style.left);
            var w = domElement.offsetWidth;
            style.left = (left - w / 2) + \'px\';
        },
  
        onComplete: function(){
            Log.write("Network Map");
  
            //Build the right column relations list.
            //This is done by collecting the information (stored in the data property)
            //for all the nodes adjacent to the centered node.
            var node = ht.graph.getClosestNodeToOrigin("current");
            var html = "<h4>" + node.name + "</h4><b>Connections:</b>";
            html += "<ul>";
            node.eachAdjacency(function(adj){
                var child = adj.nodeTo;
                if (child.data) {
                    var rel = (child.data.band == node.name) ? child.data.relation : node.data.relation;
                    html += "<li>" + child.name + " " + "<div class=\\"relation\\">(relation: " + rel + ")</div></li>";
                }
            });
            html += "</ul>";
            $jit.id(\'inner-details\').innerHTML = html;
        }
      });
      //load JSON data.
      ht.loadJSON(json);
      //compute positions and plot.
      ht.refresh();
      //end
      ht.controller.onComplete();
  }
  </script>
  </head>
  
  <body onload="init();">
  <div id="container">
  
  <div id="left-container">
  
  
  
          <div class="text">
          <h4>
  __title_to_replace__
          </h4>
  <h5>__description_to_replace__</h5>
  
          </div>
  
          <div id="id-list"></div>
  
  
  <div style="text-align:center;"></div>
  </div>
  
  <div id="center-container">
      <div id="infovis"></div>
  </div>
  
  <div id="right-container">
  
  <div id="inner-details"></div>
  
  </div>
  
  <div id="log"></div>
  </div>
  </body>
  </html>'''.replace('__data_will_locate_here__', json.dumps(dgraph)) \
          .replace('__title_to_replace__', messages(language, 90)) \
          .replace('__description_to_replace__', messages(language, 91)) \
          .replace('__html_title_to_replace__', messages(language, 92))
      if version() is 2:
          return data.decode('utf8')
      return data
  