├── .gitignore ├── BST.java ├── README.md ├── css └── base.css ├── example1.png ├── example2.png ├── html └── binary_tree.html ├── js ├── binary_tree_visualizer.js ├── excanvas.js ├── jit-yc.js └── jit.js └── json_representation.png /.gitignore: -------------------------------------------------------------------------------- 1 | *.class -------------------------------------------------------------------------------- /BST.java: -------------------------------------------------------------------------------- 1 | 2 | import java.util.LinkedList; 3 | import java.util.Queue; 4 | 5 | /** 6 | * Simplistic implementation of Binary Search Trees in Java. Designed as an API 7 | * to create binary search trees in the code, and to encode them in a JSON 8 | * format, so that the trees can be visualised in the Javascript 9 | * BinaryTreeVisualizer. 10 | * 11 | * @author achantreau Copyright (c) 2014 12 | * @param key type of the binary search tree. 13 | */ 14 | public class BST> { 15 | 16 | /** 17 | * Root node of binary search tree. 18 | */ 19 | private BSTNode root; 20 | 21 | /** 22 | * Node of binary search tree. 23 | * 24 | * @param key type of the binary tree. 25 | */ 26 | public class BSTNode { 27 | 28 | public K key; 29 | public BSTNode parent; 30 | public BSTNode left; 31 | public BSTNode right; 32 | 33 | public BSTNode(K key, BSTNode left, BSTNode right) { 34 | this.key = key; 35 | this.left = left; 36 | this.right = right; 37 | } 38 | 39 | public BSTNode(K key) { 40 | this(key, null, null); 41 | } 42 | } 43 | 44 | /** 45 | * Inserts a new node in the tree using an iterative approach. The node will 46 | * be inserted in the appropriate position to maintain the characteristic 47 | * property of binary search trees. If the node trying to be inserted is 48 | * already present in the tree then nothing else will be done. 49 | * 50 | * @param key the key of the node to insert. 51 | */ 52 | public void insert(K key) { 53 | BSTNode parent = null; 54 | BSTNode current = root; 55 | BSTNode newNode = new BSTNode<>(key); 56 | 57 | while (current != null) { 58 | parent = current; 59 | 60 | if (key.compareTo(current.key) < 0) { 61 | current = current.left; 62 | } else if (key.compareTo(current.key) > 0) { 63 | current = current.right; 64 | } else { 65 | /* Node is already in the tree. */ 66 | return; 67 | } 68 | } 69 | 70 | newNode.parent = parent; 71 | 72 | if (parent == null) { 73 | root = newNode; 74 | } else { 75 | if (key.compareTo(parent.key) < 0) { 76 | parent.left = newNode; 77 | } else if (key.compareTo(parent.key) > 0) { 78 | parent.right = newNode; 79 | } 80 | } 81 | } 82 | 83 | /** 84 | * Searches for a node in the binary search tree. 85 | * 86 | * @param key the key to search for. 87 | * @return the node with the given key or null if not found. 88 | */ 89 | public BSTNode search(K key) { 90 | BSTNode current = root; 91 | 92 | while (current != null && key.compareTo(current.key) != 0) { 93 | if (key.compareTo(current.key) > 0) { 94 | current = current.right; 95 | } else { 96 | current = current.left; 97 | } 98 | } 99 | 100 | return current; 101 | } 102 | 103 | /** 104 | * Returns the space separated String, representing the in-order traversal 105 | * of the binary search tree. 106 | * 107 | * @return the in-order traversal of the binary search tree. 108 | */ 109 | public String inorder() { 110 | StringBuilder sb = new StringBuilder(); 111 | inorder(root, sb); 112 | return sb.toString(); 113 | } 114 | 115 | private void inorder(BSTNode root, StringBuilder sb) { 116 | if (root != null) { 117 | inorder(root.left, sb); 118 | sb.append(root.key).append(" "); 119 | inorder(root.right, sb); 120 | } 121 | } 122 | 123 | /** 124 | * Returns the space separated String, representing the pre-order traversal 125 | * of the binary search tree. 126 | * 127 | * @return the pre-order traversal of the binary search tree. 128 | */ 129 | public String preorder() { 130 | StringBuilder sb = new StringBuilder(); 131 | preorder(root, sb); 132 | return sb.toString(); 133 | } 134 | 135 | private void preorder(BSTNode root, StringBuilder sb) { 136 | if (root != null) { 137 | sb.append(root.key).append(" "); 138 | preorder(root.left, sb); 139 | preorder(root.right, sb); 140 | } 141 | } 142 | 143 | /** 144 | * Returns the space separated String, representing the post-order traversal 145 | * of the binary search tree. 146 | * 147 | * @return the post-order traversal of the binary search tree. 148 | */ 149 | public String postorder() { 150 | StringBuilder sb = new StringBuilder(); 151 | postorder(root, sb); 152 | return sb.toString(); 153 | } 154 | 155 | private void postorder(BSTNode root, StringBuilder sb) { 156 | if (root != null) { 157 | postorder(root.left, sb); 158 | postorder(root.right, sb); 159 | sb.append(root.key).append(" "); 160 | } 161 | } 162 | 163 | /** 164 | * Returns the space separated String, representing the level-order 165 | * traversal of the binary search tree. (Implementation based on 166 | * breadth-first search) 167 | * 168 | * @return the level-order traversal of the binary search tree. 169 | */ 170 | public String levelorder() { 171 | StringBuilder sb = new StringBuilder(); 172 | Queue> queue = new LinkedList<>(); 173 | queue.add(root); 174 | 175 | while (!queue.isEmpty()) { 176 | BSTNode current = queue.remove(); 177 | sb.append(current.key).append(" "); 178 | 179 | if (current.left != null) { 180 | queue.add(current.left); 181 | } 182 | 183 | if (current.right != null) { 184 | queue.add(current.right); 185 | } 186 | } 187 | 188 | return sb.toString().trim(); 189 | } 190 | 191 | /** 192 | * Returns the space separated String, representing the level-order 193 | * traversal of the binary search tree. The levels are numbered from 1, i.e. 194 | * the root node of the binary search tree is at level 1. 195 | * 196 | * @param level the desired level in the binary search tree. 197 | * @return the level-order traversal of the binary search tree. 198 | */ 199 | public String levelToString(int level) { 200 | StringBuilder sb = new StringBuilder(); 201 | levelToString(root, level, sb); 202 | return sb.toString(); 203 | } 204 | 205 | private void levelToString(BSTNode root, int level, StringBuilder sb) { 206 | if (root != null) { 207 | if (level == 1) { 208 | sb.append(root.key).append(" "); 209 | } else if (level > 1) { 210 | levelToString(root.left, level - 1, sb); 211 | levelToString(root.right, level - 1, sb); 212 | } 213 | } 214 | } 215 | 216 | /** 217 | * Returns the JSON String representation of the binary search tree. 218 | * 219 | * @return the JSON representation of the binary search tree. 220 | */ 221 | public String toJSON() { 222 | StringBuilder sb = new StringBuilder(); 223 | toJSON(root, sb); 224 | return sb.toString() + ";"; 225 | } 226 | 227 | private void toJSON(BSTNode root, StringBuilder sb) { 228 | if ((root.left != null) || (root.right != null)) { 229 | sb.append("{id: \"").append(root.key).append("00") 230 | .append("\", name: \"").append(root.key) 231 | .append("\", data: {}, children: ["); 232 | if (root.left != null) { 233 | toJSON(root.left, sb); 234 | sb.append(", "); 235 | } else { 236 | sb.append("{id: \"").append((Integer) root.key - 1).append("90") 237 | .append("\", name: \"null\", data: {}, children: []}, "); 238 | } 239 | if (root.right != null) { 240 | toJSON(root.right, sb); 241 | } else { 242 | sb.append("id: \"").append((Integer) root.key + 1).append("90") 243 | .append("\", name: \"null\", data: {}, children: []}"); 244 | } 245 | sb.append("]}"); 246 | } else { 247 | sb.append("{id: \"").append(root.key).append("00") 248 | .append("\", name: \"").append(root.key) 249 | .append("\", data: {}, children: []}"); 250 | } 251 | } 252 | 253 | /** 254 | * Returns the number of nodes in the binary search tree. 255 | * 256 | * @return the number of nodes in the binary search tree. 257 | */ 258 | public int size() { 259 | return size(root); 260 | } 261 | 262 | /** 263 | * Returns the number of nodes of a particular subtree. 264 | * 265 | * @param root the root node of the subtree. 266 | * @return the number of nodes in the subtree. 267 | */ 268 | public int size(BSTNode root) { 269 | if (root == null) { 270 | return 0; 271 | } 272 | 273 | return size(root.left) + 1 + size(root.right); 274 | } 275 | 276 | /** 277 | * Returns the height of the binary search tree. 278 | * 279 | * @return the height of the binary search tree. 280 | */ 281 | public int height() { 282 | return height(root); 283 | } 284 | 285 | /** 286 | * Returns the height of a particular subtree. 287 | * 288 | * @param root the root node of the subtree. 289 | * @return the height of the subtree. 290 | */ 291 | public int height(BSTNode root) { 292 | if (root == null) { 293 | return 0; 294 | } 295 | 296 | return Math.max(height(root.left) + 1, height(root.right) + 1); 297 | } 298 | 299 | /** 300 | * Returns the minimum key of the subtree rooted at root. 301 | * 302 | * @param root the root of the subtree. 303 | * @return the key of the minimum element. 304 | */ 305 | public BSTNode minimum(BSTNode root) { 306 | BSTNode current = root; 307 | 308 | while (current.left != null) { 309 | current = current.left; 310 | } 311 | 312 | return current; 313 | } 314 | 315 | /** 316 | * Returns the minimum key of the subtree rooted at root. 317 | * 318 | * @param root the root of the subtree. 319 | * @return the key of the maximum element. 320 | */ 321 | public BSTNode maximum(BSTNode root) { 322 | BSTNode current = root; 323 | 324 | while (current.right != null) { 325 | current = current.right; 326 | } 327 | 328 | return current; 329 | } 330 | 331 | /** 332 | * Finds the successor of a node. 333 | * 334 | * @param x the node to find the successor of. 335 | * @return the successor of node x. 336 | */ 337 | public BSTNode successor(BSTNode x) { 338 | if (x.right != null) { 339 | return minimum(x.right); 340 | } 341 | 342 | BSTNode parent = x.parent; 343 | while (parent != null && parent.right == x) { 344 | x = parent; 345 | parent = x.parent; 346 | } 347 | 348 | return parent; 349 | } 350 | 351 | /** 352 | * Finds the predecessor of a node. 353 | * 354 | * @param x the node to find the predecessor of. 355 | * @return the predecessor of node x. 356 | */ 357 | public BSTNode predecessor(BSTNode x) { 358 | if (x.left != null) { 359 | return maximum(x.left); 360 | } 361 | 362 | BSTNode parent = x.parent; 363 | while (parent != null && parent.left == x) { 364 | x = parent; 365 | parent = x.parent; 366 | } 367 | 368 | return parent; 369 | } 370 | 371 | public static void main(String[] args) { 372 | BST bst = new BST<>(); 373 | 374 | bst.insert(30); 375 | bst.insert(25); 376 | bst.insert(35); 377 | bst.insert(20); 378 | bst.insert(26); 379 | bst.insert(21); 380 | bst.insert(27); 381 | bst.insert(33); 382 | bst.insert(38); 383 | bst.insert(40); 384 | 385 | System.out.println(bst.inorder()); 386 | System.out.println(bst.levelorder()); 387 | System.out.println(bst.toJSON()); 388 | System.out.println(bst.levelToString(3)); 389 | System.out.println(bst.minimum(bst.root).key); 390 | System.out.println(bst.maximum(bst.root).key); 391 | System.out.println(bst.successor(bst.root).key); 392 | } 393 | } 394 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | BinaryTreeVisualizer 2 | ==================== 3 | **BinaryTreeVisualizer** consists of a Java implementation to create **Binary Search Trees** and some Javascript code to render these trees in a web page. This API is most useful 4 | for applications which have a Javascript capable web-component, with the Java code in the back-end to construct the binary trees, created by user input for instance. 5 | I used this API in my final year project (jSCAPE) to provide a visual aid to help students answer exercises on binary search trees. 6 | 7 | ###Files 8 | The Java implementation resides in a single file called `BST.java`. 9 | 10 | In order to visualize the binary trees, you must use the BinaryTreeVisualizer. It is programmed in Javascript using the JIT library. The files for the visualizer are: 11 | 12 | -binary_tree.js 13 | 14 | -binary_tree.html 15 | 16 | -jit.js (Javascript InfoVis Toolkit) 17 | 18 | -base.css are modified versions of the original CSS files used for the Spacetree demo. 19 | Links to the originals: 20 | http://philogb.github.io/jit/static/v20/Jit/Examples/css/base.css 21 | 22 | ###How to use 23 | 24 | ```java 25 | BST bst = new BST<>(); 26 | 27 | bst.insert(34); 28 | bst.insert(22); 29 | bst.insert(28); 30 | bst.insert(56); 31 | 32 | String bstJSON = bst.toJSON(); 33 | ``` 34 | The code snippet above shows the creation of a binary tree, how to insert a few elements and how to get the JSON representation of the tree, so that it can be used by the visualizer. Take 35 | a look at the rest of the API for other functions which can be called on binary search trees. 36 | 37 | 38 | The function responsible for rendering the tree is called visualize(binary_tree_json), and can be found in **binary_tree.js**. The function takes the JSON representation 39 | of the binary tree as a parameter. Call this function in the html file, for instance, . This will render the binary tree in the web page 40 | at loading time. To customize the BinaryTreeVisualizer, take a look at the References section, which gives links to the Javascript InfoVis Toolkit, where you can find tutorials 41 | and more examples of how to customize visualizations, animations, colors, etc... 42 | 43 | ###Binary Search Tree JSON Representation 44 | Using the API's toJSON() function makes it a lot easier to generate the JSON representation used by the BinaryTreeVisualizer to render the trees. However, to allow 45 | people to create their own trees manually, without using the Java implementation, the JSON format is shown and explained below: 46 | 47 | ![BST Representation](https://github.com/Morphage/BinaryTreeVisualizer/blob/master/json_representation.png) 48 | 49 | "null" nodes are necessary so that the visualizer can place single child nodes in the correct location, i.e. to the left or right of its parent. Without these "null" nodes, the child 50 | node would be placed directly under the parent node, thus the binary tree wouldn't have the expected layout. At the end of the render function, these "null" nodes are removed from 51 | the visualization, and since the other nodes have their location set, then this operation doesn't affect the rest of the tree. However, there is a small bug which very rarely where 52 | single child nodes won't be in the correct location. (see **Known Bugs**). 53 | 54 | Try to organize it so that the json code is on the left and the corresponding binary tree is on the right. If this isn't possible using layout, then make an image of the code 55 | and the tree side by side. 56 | 57 | ####Known bugs 58 | *For parent nodes which only have one child, sometimes the visualizer won't place the child node in the appropriate location (i.e. to the left or to the right of the parent node). Instead, 59 | the child node is placed directly under the parent node. The cause of this bug has to do with the usage of "null" nodes and when they are removed. Recall that "null" nodes are used 60 | to establish the location of single child nodes. Sometimes these "null" nodes are removed before the single child nodes have an established location, thus the renderer will default 61 | to placing these nodes directly under the parent node. 62 | 63 | ###References 64 | -http://philogb.github.io/jit/static/v20/Jit/Examples/css/base.css 65 | 66 | ###TODO list 67 | * Write small tutorial on how to use the visualizer. 68 | * Explain JSON format for binary tree + example. 69 | * Clean up javascript code, html code. 70 | * Add links to library and references used for this project. 71 | -------------------------------------------------------------------------------- /css/base.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | margin:0; 3 | padding:0; 4 | font-family: "Lucida Grande", Verdana; 5 | font-size: 0.9em; 6 | text-align: center; 7 | /*background-color:#1a1a1a;*/ 8 | } 9 | 10 | #center-container { 11 | height:550px; 12 | position:absolute; 13 | top:0; 14 | } 15 | 16 | #center-container { 17 | width:800px; 18 | background-color:#1a1a1a; 19 | color:#ccc; 20 | } 21 | 22 | #infovis { 23 | position:absolute; 24 | width:800px; 25 | height:550px; 26 | margin:auto; 27 | overflow:hidden; 28 | } -------------------------------------------------------------------------------- /example1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Morphage/BinaryTreeVisualizer/c1dcc64f4c63c1c1c040d13d71bf60a9364e22ea/example1.png -------------------------------------------------------------------------------- /example2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Morphage/BinaryTreeVisualizer/c1dcc64f4c63c1c1c040d13d71bf60a9364e22ea/example2.png -------------------------------------------------------------------------------- /html/binary_tree.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 20 | 21 | 22 | 23 | 24 | 25 |
26 |
27 |
28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /js/binary_tree_visualizer.js: -------------------------------------------------------------------------------- 1 | var labelType, useGradients, nativeTextSupport, animate; 2 | 3 | (function() { 4 | var ua = navigator.userAgent, 5 | iStuff = ua.match(/iPhone/i) || ua.match(/iPad/i), 6 | typeOfCanvas = typeof HTMLCanvasElement, 7 | nativeCanvasSupport = (typeOfCanvas == 'object' || typeOfCanvas == 'function'), 8 | textSupport = nativeCanvasSupport 9 | && (typeof document.createElement('canvas').getContext('2d').fillText == 'function'); 10 | 11 | /* 12 | * I'm setting this based on the fact that ExCanvas provides text support for IE 13 | * and that as of today iPhone/iPad current text support is lame. 14 | */ 15 | labelType = (!nativeCanvasSupport || (textSupport && !iStuff))? 'Native' : 'HTML'; 16 | nativeTextSupport = labelType == 'Native'; 17 | useGradients = nativeCanvasSupport; 18 | animate = !(iStuff || !nativeCanvasSupport); 19 | })(); 20 | 21 | function visualize(binary_tree_json){ 22 | // Initialize Spacetree 23 | // Create a new ST instance 24 | var st = new $jit.ST({ 25 | /* id of viz container element */ 26 | injectInto: 'infovis', 27 | 28 | /* Set duration for the animation. */ 29 | duration: 200, 30 | orientation: 'top', 31 | 32 | /* Set animation transition type. */ 33 | transition: $jit.Trans.Quart.easeInOut, 34 | 35 | /* Set distance between node and its children. */ 36 | levelDistance: 50, 37 | levelsToShow: 8, 38 | constrained: false, 39 | 40 | /* Enable panning. */ 41 | Navigation: { 42 | enable:true, 43 | panning:true 44 | }, 45 | 46 | /* Set node and edge styles 47 | Set overridable = true for styling individual 48 | Nodes or edges */ 49 | Node: { 50 | height: 20, 51 | width: 60, 52 | dim: 30, 53 | type: 'circle', 54 | color: '#aaa', 55 | overridable: true 56 | }, 57 | 58 | Edge: { 59 | type: 'line', 60 | overridable: true 61 | }, 62 | 63 | // This method is called on DOM label creation. 64 | // Use this method to add event handlers and styles to 65 | // your node. 66 | onCreateLabel: function(label, node){ 67 | label.id = node.id; 68 | label.innerHTML = node.name; 69 | //set label styles 70 | var style = label.style; 71 | style.width = 60 + 'px'; 72 | style.height = 17 + 'px'; 73 | style.cursor = 'pointer'; 74 | style.color = '#333'; 75 | style.fontSize = '0.8em'; 76 | style.textAlign= 'center'; 77 | style.paddingTop = '3px'; 78 | }, 79 | 80 | // This method is called right before plotting 81 | // a node. It's useful for changing an individual node 82 | // style properties before plotting it. 83 | // The data properties prefixed with a dollar 84 | // sign will override the global node style properties. 85 | onBeforePlotNode: function(node){ 86 | //add some color to the nodes in the path between the 87 | //root node and the selected node. 88 | if (node.selected) { 89 | node.data.$color = "#ff7"; 90 | } 91 | else { 92 | delete node.data.$color; 93 | //if the node belongs to the last plotted level 94 | if(!node.anySubnode("exist")) { 95 | //count children number 96 | var count = 0; 97 | node.eachSubnode(function(n) { count++; }); 98 | //assign a node color based on 99 | //how many children it has 100 | node.data.$color = ['#aaa', '#baa', '#caa', '#daa', '#eaa', '#faa'][count]; 101 | } 102 | } 103 | }, 104 | 105 | /* This method is called right before plotting an edge. It's useful for changing an 106 | * individual edge style properties before plotting it. Edge data properties prefixed 107 | * with a dollar sign will override the Edge global style properties. 108 | */ 109 | onBeforePlotLine: function(adj){ 110 | if (adj.nodeFrom.selected && adj.nodeTo.selected) { 111 | adj.data.$color = "#eed"; 112 | adj.data.$lineWidth = 3; 113 | } 114 | else { 115 | delete adj.data.$color; 116 | delete adj.data.$lineWidth; 117 | } 118 | } 119 | }); 120 | 121 | /* Load JSON data. */ 122 | st.loadJSON(binary_tree_json); 123 | 124 | /* Compute node positions and layout. */ 125 | st.compute(); 126 | 127 | /* Emulate a click on the root node to expand it. */ 128 | st.onClick(st.root); 129 | 130 | /* Remove null nodes from the visualization. */ 131 | st.graph.eachNode(function(node) { 132 | if (node.name == "null") { 133 | st.op.removeNode(node.id, { 134 | type: 'fade:seq', 135 | hideLabels: false, 136 | transition: $jit.Trans.Quart.easeOut 137 | }); 138 | } 139 | }); 140 | } 141 | -------------------------------------------------------------------------------- /js/excanvas.js: -------------------------------------------------------------------------------- 1 | // Copyright 2006 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | 16 | // Known Issues: 17 | // 18 | // * Patterns only support repeat. 19 | // * Radial gradient are not implemented. The VML version of these look very 20 | // different from the canvas one. 21 | // * Clipping paths are not implemented. 22 | // * Coordsize. The width and height attribute have higher priority than the 23 | // width and height style values which isn't correct. 24 | // * Painting mode isn't implemented. 25 | // * Canvas width/height should is using content-box by default. IE in 26 | // Quirks mode will draw the canvas using border-box. Either change your 27 | // doctype to HTML5 28 | // (http://www.whatwg.org/specs/web-apps/current-work/#the-doctype) 29 | // or use Box Sizing Behavior from WebFX 30 | // (http://webfx.eae.net/dhtml/boxsizing/boxsizing.html) 31 | // * Non uniform scaling does not correctly scale strokes. 32 | // * Optimize. There is always room for speed improvements. 33 | 34 | // Only add this code if we do not already have a canvas implementation 35 | if (!document.createElement('canvas').getContext) { 36 | 37 | (function() { 38 | 39 | // alias some functions to make (compiled) code shorter 40 | var m = Math; 41 | var mr = m.round; 42 | var ms = m.sin; 43 | var mc = m.cos; 44 | var abs = m.abs; 45 | var sqrt = m.sqrt; 46 | 47 | // this is used for sub pixel precision 48 | var Z = 10; 49 | var Z2 = Z / 2; 50 | 51 | var IE_VERSION = +navigator.userAgent.match(/MSIE ([\d.]+)?/)[1]; 52 | 53 | /** 54 | * This funtion is assigned to the elements as element.getContext(). 55 | * @this {HTMLElement} 56 | * @return {CanvasRenderingContext2D_} 57 | */ 58 | function getContext() { 59 | return this.context_ || 60 | (this.context_ = new CanvasRenderingContext2D_(this)); 61 | } 62 | 63 | var slice = Array.prototype.slice; 64 | 65 | /** 66 | * Binds a function to an object. The returned function will always use the 67 | * passed in {@code obj} as {@code this}. 68 | * 69 | * Example: 70 | * 71 | * g = bind(f, obj, a, b) 72 | * g(c, d) // will do f.call(obj, a, b, c, d) 73 | * 74 | * @param {Function} f The function to bind the object to 75 | * @param {Object} obj The object that should act as this when the function 76 | * is called 77 | * @param {*} var_args Rest arguments that will be used as the initial 78 | * arguments when the function is called 79 | * @return {Function} A new function that has bound this 80 | */ 81 | function bind(f, obj, var_args) { 82 | var a = slice.call(arguments, 2); 83 | return function() { 84 | return f.apply(obj, a.concat(slice.call(arguments))); 85 | }; 86 | } 87 | 88 | function encodeHtmlAttribute(s) { 89 | return String(s).replace(/&/g, '&').replace(/"/g, '"'); 90 | } 91 | 92 | function addNamespace(doc, prefix, urn) { 93 | if (!doc.namespaces[prefix]) { 94 | doc.namespaces.add(prefix, urn, '#default#VML'); 95 | } 96 | } 97 | 98 | function addNamespacesAndStylesheet(doc) { 99 | addNamespace(doc, 'g_vml_', 'urn:schemas-microsoft-com:vml'); 100 | addNamespace(doc, 'g_o_', 'urn:schemas-microsoft-com:office:office'); 101 | 102 | // Setup default CSS. Only add one style sheet per document 103 | if (!doc.styleSheets['ex_canvas_']) { 104 | var ss = doc.createStyleSheet(); 105 | ss.owningElement.id = 'ex_canvas_'; 106 | ss.cssText = 'canvas{display:inline-block;overflow:hidden;' + 107 | // default size is 300x150 in Gecko and Opera 108 | 'text-align:left;width:300px;height:150px}'; 109 | } 110 | } 111 | 112 | // Add namespaces and stylesheet at startup. 113 | addNamespacesAndStylesheet(document); 114 | 115 | var G_vmlCanvasManager_ = { 116 | init: function(opt_doc) { 117 | var doc = opt_doc || document; 118 | // Create a dummy element so that IE will allow canvas elements to be 119 | // recognized. 120 | doc.createElement('canvas'); 121 | doc.attachEvent('onreadystatechange', bind(this.init_, this, doc)); 122 | }, 123 | 124 | init_: function(doc) { 125 | // find all canvas elements 126 | var els = doc.getElementsByTagName('canvas'); 127 | for (var i = 0; i < els.length; i++) { 128 | this.initElement(els[i]); 129 | } 130 | }, 131 | 132 | /** 133 | * Public initializes a canvas element so that it can be used as canvas 134 | * element from now on. This is called automatically before the page is 135 | * loaded but if you are creating elements using createElement you need to 136 | * make sure this is called on the element. 137 | * @param {HTMLElement} el The canvas element to initialize. 138 | * @return {HTMLElement} the element that was created. 139 | */ 140 | initElement: function(el) { 141 | if (!el.getContext) { 142 | el.getContext = getContext; 143 | 144 | // Add namespaces and stylesheet to document of the element. 145 | addNamespacesAndStylesheet(el.ownerDocument); 146 | 147 | // Remove fallback content. There is no way to hide text nodes so we 148 | // just remove all childNodes. We could hide all elements and remove 149 | // text nodes but who really cares about the fallback content. 150 | el.innerHTML = ''; 151 | 152 | // do not use inline function because that will leak memory 153 | el.attachEvent('onpropertychange', onPropertyChange); 154 | el.attachEvent('onresize', onResize); 155 | 156 | var attrs = el.attributes; 157 | if (attrs.width && attrs.width.specified) { 158 | // TODO: use runtimeStyle and coordsize 159 | // el.getContext().setWidth_(attrs.width.nodeValue); 160 | el.style.width = attrs.width.nodeValue + 'px'; 161 | } else { 162 | el.width = el.clientWidth; 163 | } 164 | if (attrs.height && attrs.height.specified) { 165 | // TODO: use runtimeStyle and coordsize 166 | // el.getContext().setHeight_(attrs.height.nodeValue); 167 | el.style.height = attrs.height.nodeValue + 'px'; 168 | } else { 169 | el.height = el.clientHeight; 170 | } 171 | //el.getContext().setCoordsize_() 172 | } 173 | return el; 174 | } 175 | }; 176 | 177 | function onPropertyChange(e) { 178 | var el = e.srcElement; 179 | 180 | switch (e.propertyName) { 181 | case 'width': 182 | el.getContext().clearRect(); 183 | el.style.width = el.attributes.width.nodeValue + 'px'; 184 | // In IE8 this does not trigger onresize. 185 | el.firstChild.style.width = el.clientWidth + 'px'; 186 | break; 187 | case 'height': 188 | el.getContext().clearRect(); 189 | el.style.height = el.attributes.height.nodeValue + 'px'; 190 | el.firstChild.style.height = el.clientHeight + 'px'; 191 | break; 192 | } 193 | } 194 | 195 | function onResize(e) { 196 | var el = e.srcElement; 197 | if (el.firstChild) { 198 | el.firstChild.style.width = el.clientWidth + 'px'; 199 | el.firstChild.style.height = el.clientHeight + 'px'; 200 | } 201 | } 202 | 203 | G_vmlCanvasManager_.init(); 204 | 205 | // precompute "00" to "FF" 206 | var decToHex = []; 207 | for (var i = 0; i < 16; i++) { 208 | for (var j = 0; j < 16; j++) { 209 | decToHex[i * 16 + j] = i.toString(16) + j.toString(16); 210 | } 211 | } 212 | 213 | function createMatrixIdentity() { 214 | return [ 215 | [1, 0, 0], 216 | [0, 1, 0], 217 | [0, 0, 1] 218 | ]; 219 | } 220 | 221 | function matrixMultiply(m1, m2) { 222 | var result = createMatrixIdentity(); 223 | 224 | for (var x = 0; x < 3; x++) { 225 | for (var y = 0; y < 3; y++) { 226 | var sum = 0; 227 | 228 | for (var z = 0; z < 3; z++) { 229 | sum += m1[x][z] * m2[z][y]; 230 | } 231 | 232 | result[x][y] = sum; 233 | } 234 | } 235 | return result; 236 | } 237 | 238 | function copyState(o1, o2) { 239 | o2.fillStyle = o1.fillStyle; 240 | o2.lineCap = o1.lineCap; 241 | o2.lineJoin = o1.lineJoin; 242 | o2.lineWidth = o1.lineWidth; 243 | o2.miterLimit = o1.miterLimit; 244 | o2.shadowBlur = o1.shadowBlur; 245 | o2.shadowColor = o1.shadowColor; 246 | o2.shadowOffsetX = o1.shadowOffsetX; 247 | o2.shadowOffsetY = o1.shadowOffsetY; 248 | o2.strokeStyle = o1.strokeStyle; 249 | o2.globalAlpha = o1.globalAlpha; 250 | o2.font = o1.font; 251 | o2.textAlign = o1.textAlign; 252 | o2.textBaseline = o1.textBaseline; 253 | o2.arcScaleX_ = o1.arcScaleX_; 254 | o2.arcScaleY_ = o1.arcScaleY_; 255 | o2.lineScale_ = o1.lineScale_; 256 | } 257 | 258 | var colorData = { 259 | aliceblue: '#F0F8FF', 260 | antiquewhite: '#FAEBD7', 261 | aquamarine: '#7FFFD4', 262 | azure: '#F0FFFF', 263 | beige: '#F5F5DC', 264 | bisque: '#FFE4C4', 265 | black: '#000000', 266 | blanchedalmond: '#FFEBCD', 267 | blueviolet: '#8A2BE2', 268 | brown: '#A52A2A', 269 | burlywood: '#DEB887', 270 | cadetblue: '#5F9EA0', 271 | chartreuse: '#7FFF00', 272 | chocolate: '#D2691E', 273 | coral: '#FF7F50', 274 | cornflowerblue: '#6495ED', 275 | cornsilk: '#FFF8DC', 276 | crimson: '#DC143C', 277 | cyan: '#00FFFF', 278 | darkblue: '#00008B', 279 | darkcyan: '#008B8B', 280 | darkgoldenrod: '#B8860B', 281 | darkgray: '#A9A9A9', 282 | darkgreen: '#006400', 283 | darkgrey: '#A9A9A9', 284 | darkkhaki: '#BDB76B', 285 | darkmagenta: '#8B008B', 286 | darkolivegreen: '#556B2F', 287 | darkorange: '#FF8C00', 288 | darkorchid: '#9932CC', 289 | darkred: '#8B0000', 290 | darksalmon: '#E9967A', 291 | darkseagreen: '#8FBC8F', 292 | darkslateblue: '#483D8B', 293 | darkslategray: '#2F4F4F', 294 | darkslategrey: '#2F4F4F', 295 | darkturquoise: '#00CED1', 296 | darkviolet: '#9400D3', 297 | deeppink: '#FF1493', 298 | deepskyblue: '#00BFFF', 299 | dimgray: '#696969', 300 | dimgrey: '#696969', 301 | dodgerblue: '#1E90FF', 302 | firebrick: '#B22222', 303 | floralwhite: '#FFFAF0', 304 | forestgreen: '#228B22', 305 | gainsboro: '#DCDCDC', 306 | ghostwhite: '#F8F8FF', 307 | gold: '#FFD700', 308 | goldenrod: '#DAA520', 309 | grey: '#808080', 310 | greenyellow: '#ADFF2F', 311 | honeydew: '#F0FFF0', 312 | hotpink: '#FF69B4', 313 | indianred: '#CD5C5C', 314 | indigo: '#4B0082', 315 | ivory: '#FFFFF0', 316 | khaki: '#F0E68C', 317 | lavender: '#E6E6FA', 318 | lavenderblush: '#FFF0F5', 319 | lawngreen: '#7CFC00', 320 | lemonchiffon: '#FFFACD', 321 | lightblue: '#ADD8E6', 322 | lightcoral: '#F08080', 323 | lightcyan: '#E0FFFF', 324 | lightgoldenrodyellow: '#FAFAD2', 325 | lightgreen: '#90EE90', 326 | lightgrey: '#D3D3D3', 327 | lightpink: '#FFB6C1', 328 | lightsalmon: '#FFA07A', 329 | lightseagreen: '#20B2AA', 330 | lightskyblue: '#87CEFA', 331 | lightslategray: '#778899', 332 | lightslategrey: '#778899', 333 | lightsteelblue: '#B0C4DE', 334 | lightyellow: '#FFFFE0', 335 | limegreen: '#32CD32', 336 | linen: '#FAF0E6', 337 | magenta: '#FF00FF', 338 | mediumaquamarine: '#66CDAA', 339 | mediumblue: '#0000CD', 340 | mediumorchid: '#BA55D3', 341 | mediumpurple: '#9370DB', 342 | mediumseagreen: '#3CB371', 343 | mediumslateblue: '#7B68EE', 344 | mediumspringgreen: '#00FA9A', 345 | mediumturquoise: '#48D1CC', 346 | mediumvioletred: '#C71585', 347 | midnightblue: '#191970', 348 | mintcream: '#F5FFFA', 349 | mistyrose: '#FFE4E1', 350 | moccasin: '#FFE4B5', 351 | navajowhite: '#FFDEAD', 352 | oldlace: '#FDF5E6', 353 | olivedrab: '#6B8E23', 354 | orange: '#FFA500', 355 | orangered: '#FF4500', 356 | orchid: '#DA70D6', 357 | palegoldenrod: '#EEE8AA', 358 | palegreen: '#98FB98', 359 | paleturquoise: '#AFEEEE', 360 | palevioletred: '#DB7093', 361 | papayawhip: '#FFEFD5', 362 | peachpuff: '#FFDAB9', 363 | peru: '#CD853F', 364 | pink: '#FFC0CB', 365 | plum: '#DDA0DD', 366 | powderblue: '#B0E0E6', 367 | rosybrown: '#BC8F8F', 368 | royalblue: '#4169E1', 369 | saddlebrown: '#8B4513', 370 | salmon: '#FA8072', 371 | sandybrown: '#F4A460', 372 | seagreen: '#2E8B57', 373 | seashell: '#FFF5EE', 374 | sienna: '#A0522D', 375 | skyblue: '#87CEEB', 376 | slateblue: '#6A5ACD', 377 | slategray: '#708090', 378 | slategrey: '#708090', 379 | snow: '#FFFAFA', 380 | springgreen: '#00FF7F', 381 | steelblue: '#4682B4', 382 | tan: '#D2B48C', 383 | thistle: '#D8BFD8', 384 | tomato: '#FF6347', 385 | turquoise: '#40E0D0', 386 | violet: '#EE82EE', 387 | wheat: '#F5DEB3', 388 | whitesmoke: '#F5F5F5', 389 | yellowgreen: '#9ACD32' 390 | }; 391 | 392 | 393 | function getRgbHslContent(styleString) { 394 | var start = styleString.indexOf('(', 3); 395 | var end = styleString.indexOf(')', start + 1); 396 | var parts = styleString.substring(start + 1, end).split(','); 397 | // add alpha if needed 398 | if (parts.length != 4 || styleString.charAt(3) != 'a') { 399 | parts[3] = 1; 400 | } 401 | return parts; 402 | } 403 | 404 | function percent(s) { 405 | return parseFloat(s) / 100; 406 | } 407 | 408 | function clamp(v, min, max) { 409 | return Math.min(max, Math.max(min, v)); 410 | } 411 | 412 | function hslToRgb(parts){ 413 | var r, g, b, h, s, l; 414 | h = parseFloat(parts[0]) / 360 % 360; 415 | if (h < 0) 416 | h++; 417 | s = clamp(percent(parts[1]), 0, 1); 418 | l = clamp(percent(parts[2]), 0, 1); 419 | if (s == 0) { 420 | r = g = b = l; // achromatic 421 | } else { 422 | var q = l < 0.5 ? l * (1 + s) : l + s - l * s; 423 | var p = 2 * l - q; 424 | r = hueToRgb(p, q, h + 1 / 3); 425 | g = hueToRgb(p, q, h); 426 | b = hueToRgb(p, q, h - 1 / 3); 427 | } 428 | 429 | return '#' + decToHex[Math.floor(r * 255)] + 430 | decToHex[Math.floor(g * 255)] + 431 | decToHex[Math.floor(b * 255)]; 432 | } 433 | 434 | function hueToRgb(m1, m2, h) { 435 | if (h < 0) 436 | h++; 437 | if (h > 1) 438 | h--; 439 | 440 | if (6 * h < 1) 441 | return m1 + (m2 - m1) * 6 * h; 442 | else if (2 * h < 1) 443 | return m2; 444 | else if (3 * h < 2) 445 | return m1 + (m2 - m1) * (2 / 3 - h) * 6; 446 | else 447 | return m1; 448 | } 449 | 450 | var processStyleCache = {}; 451 | 452 | function processStyle(styleString) { 453 | if (styleString in processStyleCache) { 454 | return processStyleCache[styleString]; 455 | } 456 | 457 | var str, alpha = 1; 458 | 459 | styleString = String(styleString); 460 | if (styleString.charAt(0) == '#') { 461 | str = styleString; 462 | } else if (/^rgb/.test(styleString)) { 463 | var parts = getRgbHslContent(styleString); 464 | var str = '#', n; 465 | for (var i = 0; i < 3; i++) { 466 | if (parts[i].indexOf('%') != -1) { 467 | n = Math.floor(percent(parts[i]) * 255); 468 | } else { 469 | n = +parts[i]; 470 | } 471 | str += decToHex[clamp(n, 0, 255)]; 472 | } 473 | alpha = +parts[3]; 474 | } else if (/^hsl/.test(styleString)) { 475 | var parts = getRgbHslContent(styleString); 476 | str = hslToRgb(parts); 477 | alpha = parts[3]; 478 | } else { 479 | str = colorData[styleString] || styleString; 480 | } 481 | return processStyleCache[styleString] = {color: str, alpha: alpha}; 482 | } 483 | 484 | var DEFAULT_STYLE = { 485 | style: 'normal', 486 | variant: 'normal', 487 | weight: 'normal', 488 | size: 10, 489 | family: 'sans-serif' 490 | }; 491 | 492 | // Internal text style cache 493 | var fontStyleCache = {}; 494 | 495 | function processFontStyle(styleString) { 496 | if (fontStyleCache[styleString]) { 497 | return fontStyleCache[styleString]; 498 | } 499 | 500 | var el = document.createElement('div'); 501 | var style = el.style; 502 | try { 503 | style.font = styleString; 504 | } catch (ex) { 505 | // Ignore failures to set to invalid font. 506 | } 507 | 508 | return fontStyleCache[styleString] = { 509 | style: style.fontStyle || DEFAULT_STYLE.style, 510 | variant: style.fontVariant || DEFAULT_STYLE.variant, 511 | weight: style.fontWeight || DEFAULT_STYLE.weight, 512 | size: style.fontSize || DEFAULT_STYLE.size, 513 | family: style.fontFamily || DEFAULT_STYLE.family 514 | }; 515 | } 516 | 517 | function getComputedStyle(style, element) { 518 | var computedStyle = {}; 519 | 520 | for (var p in style) { 521 | computedStyle[p] = style[p]; 522 | } 523 | 524 | // Compute the size 525 | var canvasFontSize = parseFloat(element.currentStyle.fontSize), 526 | fontSize = parseFloat(style.size); 527 | 528 | if (typeof style.size == 'number') { 529 | computedStyle.size = style.size; 530 | } else if (style.size.indexOf('px') != -1) { 531 | computedStyle.size = fontSize; 532 | } else if (style.size.indexOf('em') != -1) { 533 | computedStyle.size = canvasFontSize * fontSize; 534 | } else if(style.size.indexOf('%') != -1) { 535 | computedStyle.size = (canvasFontSize / 100) * fontSize; 536 | } else if (style.size.indexOf('pt') != -1) { 537 | computedStyle.size = fontSize / .75; 538 | } else { 539 | computedStyle.size = canvasFontSize; 540 | } 541 | 542 | // Different scaling between normal text and VML text. This was found using 543 | // trial and error to get the same size as non VML text. 544 | computedStyle.size *= 0.981; 545 | 546 | return computedStyle; 547 | } 548 | 549 | function buildStyle(style) { 550 | return style.style + ' ' + style.variant + ' ' + style.weight + ' ' + 551 | style.size + 'px ' + style.family; 552 | } 553 | 554 | var lineCapMap = { 555 | 'butt': 'flat', 556 | 'round': 'round' 557 | }; 558 | 559 | function processLineCap(lineCap) { 560 | return lineCapMap[lineCap] || 'square'; 561 | } 562 | 563 | /** 564 | * This class implements CanvasRenderingContext2D interface as described by 565 | * the WHATWG. 566 | * @param {HTMLElement} canvasElement The element that the 2D context should 567 | * be associated with 568 | */ 569 | function CanvasRenderingContext2D_(canvasElement) { 570 | this.m_ = createMatrixIdentity(); 571 | 572 | this.mStack_ = []; 573 | this.aStack_ = []; 574 | this.currentPath_ = []; 575 | 576 | // Canvas context properties 577 | this.strokeStyle = '#000'; 578 | this.fillStyle = '#000'; 579 | 580 | this.lineWidth = 1; 581 | this.lineJoin = 'miter'; 582 | this.lineCap = 'butt'; 583 | this.miterLimit = Z * 1; 584 | this.globalAlpha = 1; 585 | this.font = '10px sans-serif'; 586 | this.textAlign = 'left'; 587 | this.textBaseline = 'alphabetic'; 588 | this.canvas = canvasElement; 589 | 590 | var cssText = 'width:' + canvasElement.clientWidth + 'px;height:' + 591 | canvasElement.clientHeight + 'px;overflow:hidden;position:absolute'; 592 | var el = canvasElement.ownerDocument.createElement('div'); 593 | el.style.cssText = cssText; 594 | canvasElement.appendChild(el); 595 | 596 | var overlayEl = el.cloneNode(false); 597 | // Use a non transparent background. 598 | overlayEl.style.backgroundColor = 'red'; 599 | overlayEl.style.filter = 'alpha(opacity=0)'; 600 | canvasElement.appendChild(overlayEl); 601 | 602 | this.element_ = el; 603 | this.arcScaleX_ = 1; 604 | this.arcScaleY_ = 1; 605 | this.lineScale_ = 1; 606 | } 607 | 608 | var contextPrototype = CanvasRenderingContext2D_.prototype; 609 | contextPrototype.clearRect = function() { 610 | if (this.textMeasureEl_) { 611 | this.textMeasureEl_.removeNode(true); 612 | this.textMeasureEl_ = null; 613 | } 614 | this.element_.innerHTML = ''; 615 | }; 616 | 617 | contextPrototype.beginPath = function() { 618 | // TODO: Branch current matrix so that save/restore has no effect 619 | // as per safari docs. 620 | this.currentPath_ = []; 621 | }; 622 | 623 | contextPrototype.moveTo = function(aX, aY) { 624 | var p = getCoords(this, aX, aY); 625 | this.currentPath_.push({type: 'moveTo', x: p.x, y: p.y}); 626 | this.currentX_ = p.x; 627 | this.currentY_ = p.y; 628 | }; 629 | 630 | contextPrototype.lineTo = function(aX, aY) { 631 | var p = getCoords(this, aX, aY); 632 | this.currentPath_.push({type: 'lineTo', x: p.x, y: p.y}); 633 | 634 | this.currentX_ = p.x; 635 | this.currentY_ = p.y; 636 | }; 637 | 638 | contextPrototype.bezierCurveTo = function(aCP1x, aCP1y, 639 | aCP2x, aCP2y, 640 | aX, aY) { 641 | var p = getCoords(this, aX, aY); 642 | var cp1 = getCoords(this, aCP1x, aCP1y); 643 | var cp2 = getCoords(this, aCP2x, aCP2y); 644 | bezierCurveTo(this, cp1, cp2, p); 645 | }; 646 | 647 | // Helper function that takes the already fixed cordinates. 648 | function bezierCurveTo(self, cp1, cp2, p) { 649 | self.currentPath_.push({ 650 | type: 'bezierCurveTo', 651 | cp1x: cp1.x, 652 | cp1y: cp1.y, 653 | cp2x: cp2.x, 654 | cp2y: cp2.y, 655 | x: p.x, 656 | y: p.y 657 | }); 658 | self.currentX_ = p.x; 659 | self.currentY_ = p.y; 660 | } 661 | 662 | contextPrototype.quadraticCurveTo = function(aCPx, aCPy, aX, aY) { 663 | // the following is lifted almost directly from 664 | // http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes 665 | 666 | var cp = getCoords(this, aCPx, aCPy); 667 | var p = getCoords(this, aX, aY); 668 | 669 | var cp1 = { 670 | x: this.currentX_ + 2.0 / 3.0 * (cp.x - this.currentX_), 671 | y: this.currentY_ + 2.0 / 3.0 * (cp.y - this.currentY_) 672 | }; 673 | var cp2 = { 674 | x: cp1.x + (p.x - this.currentX_) / 3.0, 675 | y: cp1.y + (p.y - this.currentY_) / 3.0 676 | }; 677 | 678 | bezierCurveTo(this, cp1, cp2, p); 679 | }; 680 | 681 | contextPrototype.arc = function(aX, aY, aRadius, 682 | aStartAngle, aEndAngle, aClockwise) { 683 | aRadius *= Z; 684 | var arcType = aClockwise ? 'at' : 'wa'; 685 | 686 | var xStart = aX + mc(aStartAngle) * aRadius - Z2; 687 | var yStart = aY + ms(aStartAngle) * aRadius - Z2; 688 | 689 | var xEnd = aX + mc(aEndAngle) * aRadius - Z2; 690 | var yEnd = aY + ms(aEndAngle) * aRadius - Z2; 691 | 692 | // IE won't render arches drawn counter clockwise if xStart == xEnd. 693 | if (xStart == xEnd && !aClockwise) { 694 | xStart += 0.125; // Offset xStart by 1/80 of a pixel. Use something 695 | // that can be represented in binary 696 | } 697 | 698 | var p = getCoords(this, aX, aY); 699 | var pStart = getCoords(this, xStart, yStart); 700 | var pEnd = getCoords(this, xEnd, yEnd); 701 | 702 | this.currentPath_.push({type: arcType, 703 | x: p.x, 704 | y: p.y, 705 | radius: aRadius, 706 | xStart: pStart.x, 707 | yStart: pStart.y, 708 | xEnd: pEnd.x, 709 | yEnd: pEnd.y}); 710 | 711 | }; 712 | 713 | contextPrototype.rect = function(aX, aY, aWidth, aHeight) { 714 | this.moveTo(aX, aY); 715 | this.lineTo(aX + aWidth, aY); 716 | this.lineTo(aX + aWidth, aY + aHeight); 717 | this.lineTo(aX, aY + aHeight); 718 | this.closePath(); 719 | }; 720 | 721 | contextPrototype.strokeRect = function(aX, aY, aWidth, aHeight) { 722 | var oldPath = this.currentPath_; 723 | this.beginPath(); 724 | 725 | this.moveTo(aX, aY); 726 | this.lineTo(aX + aWidth, aY); 727 | this.lineTo(aX + aWidth, aY + aHeight); 728 | this.lineTo(aX, aY + aHeight); 729 | this.closePath(); 730 | this.stroke(); 731 | 732 | this.currentPath_ = oldPath; 733 | }; 734 | 735 | contextPrototype.fillRect = function(aX, aY, aWidth, aHeight) { 736 | var oldPath = this.currentPath_; 737 | this.beginPath(); 738 | 739 | this.moveTo(aX, aY); 740 | this.lineTo(aX + aWidth, aY); 741 | this.lineTo(aX + aWidth, aY + aHeight); 742 | this.lineTo(aX, aY + aHeight); 743 | this.closePath(); 744 | this.fill(); 745 | 746 | this.currentPath_ = oldPath; 747 | }; 748 | 749 | contextPrototype.createLinearGradient = function(aX0, aY0, aX1, aY1) { 750 | var gradient = new CanvasGradient_('gradient'); 751 | gradient.x0_ = aX0; 752 | gradient.y0_ = aY0; 753 | gradient.x1_ = aX1; 754 | gradient.y1_ = aY1; 755 | return gradient; 756 | }; 757 | 758 | contextPrototype.createRadialGradient = function(aX0, aY0, aR0, 759 | aX1, aY1, aR1) { 760 | var gradient = new CanvasGradient_('gradientradial'); 761 | gradient.x0_ = aX0; 762 | gradient.y0_ = aY0; 763 | gradient.r0_ = aR0; 764 | gradient.x1_ = aX1; 765 | gradient.y1_ = aY1; 766 | gradient.r1_ = aR1; 767 | return gradient; 768 | }; 769 | 770 | contextPrototype.drawImage = function(image, var_args) { 771 | var dx, dy, dw, dh, sx, sy, sw, sh; 772 | 773 | // to find the original width we overide the width and height 774 | var oldRuntimeWidth = image.runtimeStyle.width; 775 | var oldRuntimeHeight = image.runtimeStyle.height; 776 | image.runtimeStyle.width = 'auto'; 777 | image.runtimeStyle.height = 'auto'; 778 | 779 | // get the original size 780 | var w = image.width; 781 | var h = image.height; 782 | 783 | // and remove overides 784 | image.runtimeStyle.width = oldRuntimeWidth; 785 | image.runtimeStyle.height = oldRuntimeHeight; 786 | 787 | if (arguments.length == 3) { 788 | dx = arguments[1]; 789 | dy = arguments[2]; 790 | sx = sy = 0; 791 | sw = dw = w; 792 | sh = dh = h; 793 | } else if (arguments.length == 5) { 794 | dx = arguments[1]; 795 | dy = arguments[2]; 796 | dw = arguments[3]; 797 | dh = arguments[4]; 798 | sx = sy = 0; 799 | sw = w; 800 | sh = h; 801 | } else if (arguments.length == 9) { 802 | sx = arguments[1]; 803 | sy = arguments[2]; 804 | sw = arguments[3]; 805 | sh = arguments[4]; 806 | dx = arguments[5]; 807 | dy = arguments[6]; 808 | dw = arguments[7]; 809 | dh = arguments[8]; 810 | } else { 811 | throw Error('Invalid number of arguments'); 812 | } 813 | 814 | var d = getCoords(this, dx, dy); 815 | 816 | var w2 = sw / 2; 817 | var h2 = sh / 2; 818 | 819 | var vmlStr = []; 820 | 821 | var W = 10; 822 | var H = 10; 823 | 824 | // For some reason that I've now forgotten, using divs didn't work 825 | vmlStr.push(' ' , 866 | '', 874 | ''); 875 | 876 | this.element_.insertAdjacentHTML('BeforeEnd', vmlStr.join('')); 877 | }; 878 | 879 | contextPrototype.stroke = function(aFill) { 880 | var lineStr = []; 881 | var lineOpen = false; 882 | 883 | var W = 10; 884 | var H = 10; 885 | 886 | lineStr.push(''); 954 | 955 | if (!aFill) { 956 | appendStroke(this, lineStr); 957 | } else { 958 | appendFill(this, lineStr, min, max); 959 | } 960 | 961 | lineStr.push(''); 962 | 963 | this.element_.insertAdjacentHTML('beforeEnd', lineStr.join('')); 964 | }; 965 | 966 | function appendStroke(ctx, lineStr) { 967 | var a = processStyle(ctx.strokeStyle); 968 | var color = a.color; 969 | var opacity = a.alpha * ctx.globalAlpha; 970 | var lineWidth = ctx.lineScale_ * ctx.lineWidth; 971 | 972 | // VML cannot correctly render a line if the width is less than 1px. 973 | // In that case, we dilute the color to make the line look thinner. 974 | if (lineWidth < 1) { 975 | opacity *= lineWidth; 976 | } 977 | 978 | lineStr.push( 979 | '' 986 | ); 987 | } 988 | 989 | function appendFill(ctx, lineStr, min, max) { 990 | var fillStyle = ctx.fillStyle; 991 | var arcScaleX = ctx.arcScaleX_; 992 | var arcScaleY = ctx.arcScaleY_; 993 | var width = max.x - min.x; 994 | var height = max.y - min.y; 995 | if (fillStyle instanceof CanvasGradient_) { 996 | // TODO: Gradients transformed with the transformation matrix. 997 | var angle = 0; 998 | var focus = {x: 0, y: 0}; 999 | 1000 | // additional offset 1001 | var shift = 0; 1002 | // scale factor for offset 1003 | var expansion = 1; 1004 | 1005 | if (fillStyle.type_ == 'gradient') { 1006 | var x0 = fillStyle.x0_ / arcScaleX; 1007 | var y0 = fillStyle.y0_ / arcScaleY; 1008 | var x1 = fillStyle.x1_ / arcScaleX; 1009 | var y1 = fillStyle.y1_ / arcScaleY; 1010 | var p0 = getCoords(ctx, x0, y0); 1011 | var p1 = getCoords(ctx, x1, y1); 1012 | var dx = p1.x - p0.x; 1013 | var dy = p1.y - p0.y; 1014 | angle = Math.atan2(dx, dy) * 180 / Math.PI; 1015 | 1016 | // The angle should be a non-negative number. 1017 | if (angle < 0) { 1018 | angle += 360; 1019 | } 1020 | 1021 | // Very small angles produce an unexpected result because they are 1022 | // converted to a scientific notation string. 1023 | if (angle < 1e-6) { 1024 | angle = 0; 1025 | } 1026 | } else { 1027 | var p0 = getCoords(ctx, fillStyle.x0_, fillStyle.y0_); 1028 | focus = { 1029 | x: (p0.x - min.x) / width, 1030 | y: (p0.y - min.y) / height 1031 | }; 1032 | 1033 | width /= arcScaleX * Z; 1034 | height /= arcScaleY * Z; 1035 | var dimension = m.max(width, height); 1036 | shift = 2 * fillStyle.r0_ / dimension; 1037 | expansion = 2 * fillStyle.r1_ / dimension - shift; 1038 | } 1039 | 1040 | // We need to sort the color stops in ascending order by offset, 1041 | // otherwise IE won't interpret it correctly. 1042 | var stops = fillStyle.colors_; 1043 | stops.sort(function(cs1, cs2) { 1044 | return cs1.offset - cs2.offset; 1045 | }); 1046 | 1047 | var length = stops.length; 1048 | var color1 = stops[0].color; 1049 | var color2 = stops[length - 1].color; 1050 | var opacity1 = stops[0].alpha * ctx.globalAlpha; 1051 | var opacity2 = stops[length - 1].alpha * ctx.globalAlpha; 1052 | 1053 | var colors = []; 1054 | for (var i = 0; i < length; i++) { 1055 | var stop = stops[i]; 1056 | colors.push(stop.offset * expansion + shift + ' ' + stop.color); 1057 | } 1058 | 1059 | // When colors attribute is used, the meanings of opacity and o:opacity2 1060 | // are reversed. 1061 | lineStr.push(''); 1070 | } else if (fillStyle instanceof CanvasPattern_) { 1071 | if (width && height) { 1072 | var deltaLeft = -min.x; 1073 | var deltaTop = -min.y; 1074 | lineStr.push(''); 1082 | } 1083 | } else { 1084 | var a = processStyle(ctx.fillStyle); 1085 | var color = a.color; 1086 | var opacity = a.alpha * ctx.globalAlpha; 1087 | lineStr.push(''); 1089 | } 1090 | } 1091 | 1092 | contextPrototype.fill = function() { 1093 | this.stroke(true); 1094 | }; 1095 | 1096 | contextPrototype.closePath = function() { 1097 | this.currentPath_.push({type: 'close'}); 1098 | }; 1099 | 1100 | function getCoords(ctx, aX, aY) { 1101 | var m = ctx.m_; 1102 | return { 1103 | x: Z * (aX * m[0][0] + aY * m[1][0] + m[2][0]) - Z2, 1104 | y: Z * (aX * m[0][1] + aY * m[1][1] + m[2][1]) - Z2 1105 | }; 1106 | }; 1107 | 1108 | contextPrototype.save = function() { 1109 | var o = {}; 1110 | copyState(this, o); 1111 | this.aStack_.push(o); 1112 | this.mStack_.push(this.m_); 1113 | this.m_ = matrixMultiply(createMatrixIdentity(), this.m_); 1114 | }; 1115 | 1116 | contextPrototype.restore = function() { 1117 | if (this.aStack_.length) { 1118 | copyState(this.aStack_.pop(), this); 1119 | this.m_ = this.mStack_.pop(); 1120 | } 1121 | }; 1122 | 1123 | function matrixIsFinite(m) { 1124 | return isFinite(m[0][0]) && isFinite(m[0][1]) && 1125 | isFinite(m[1][0]) && isFinite(m[1][1]) && 1126 | isFinite(m[2][0]) && isFinite(m[2][1]); 1127 | } 1128 | 1129 | function setM(ctx, m, updateLineScale) { 1130 | if (!matrixIsFinite(m)) { 1131 | return; 1132 | } 1133 | ctx.m_ = m; 1134 | 1135 | if (updateLineScale) { 1136 | // Get the line scale. 1137 | // Determinant of this.m_ means how much the area is enlarged by the 1138 | // transformation. So its square root can be used as a scale factor 1139 | // for width. 1140 | var det = m[0][0] * m[1][1] - m[0][1] * m[1][0]; 1141 | ctx.lineScale_ = sqrt(abs(det)); 1142 | } 1143 | } 1144 | 1145 | contextPrototype.translate = function(aX, aY) { 1146 | var m1 = [ 1147 | [1, 0, 0], 1148 | [0, 1, 0], 1149 | [aX, aY, 1] 1150 | ]; 1151 | 1152 | setM(this, matrixMultiply(m1, this.m_), false); 1153 | }; 1154 | 1155 | contextPrototype.rotate = function(aRot) { 1156 | var c = mc(aRot); 1157 | var s = ms(aRot); 1158 | 1159 | var m1 = [ 1160 | [c, s, 0], 1161 | [-s, c, 0], 1162 | [0, 0, 1] 1163 | ]; 1164 | 1165 | setM(this, matrixMultiply(m1, this.m_), false); 1166 | }; 1167 | 1168 | contextPrototype.scale = function(aX, aY) { 1169 | this.arcScaleX_ *= aX; 1170 | this.arcScaleY_ *= aY; 1171 | var m1 = [ 1172 | [aX, 0, 0], 1173 | [0, aY, 0], 1174 | [0, 0, 1] 1175 | ]; 1176 | 1177 | setM(this, matrixMultiply(m1, this.m_), true); 1178 | }; 1179 | 1180 | contextPrototype.transform = function(m11, m12, m21, m22, dx, dy) { 1181 | var m1 = [ 1182 | [m11, m12, 0], 1183 | [m21, m22, 0], 1184 | [dx, dy, 1] 1185 | ]; 1186 | 1187 | setM(this, matrixMultiply(m1, this.m_), true); 1188 | }; 1189 | 1190 | contextPrototype.setTransform = function(m11, m12, m21, m22, dx, dy) { 1191 | var m = [ 1192 | [m11, m12, 0], 1193 | [m21, m22, 0], 1194 | [dx, dy, 1] 1195 | ]; 1196 | 1197 | setM(this, m, true); 1198 | }; 1199 | 1200 | /** 1201 | * The text drawing function. 1202 | * The maxWidth argument isn't taken in account, since no browser supports 1203 | * it yet. 1204 | */ 1205 | contextPrototype.drawText_ = function(text, x, y, maxWidth, stroke) { 1206 | var m = this.m_, 1207 | delta = 1000, 1208 | left = 0, 1209 | right = delta, 1210 | offset = {x: 0, y: 0}, 1211 | lineStr = []; 1212 | 1213 | var fontStyle = getComputedStyle(processFontStyle(this.font), 1214 | this.element_); 1215 | 1216 | var fontStyleString = buildStyle(fontStyle); 1217 | 1218 | var elementStyle = this.element_.currentStyle; 1219 | var textAlign = this.textAlign.toLowerCase(); 1220 | switch (textAlign) { 1221 | case 'left': 1222 | case 'center': 1223 | case 'right': 1224 | break; 1225 | case 'end': 1226 | textAlign = elementStyle.direction == 'ltr' ? 'right' : 'left'; 1227 | break; 1228 | case 'start': 1229 | textAlign = elementStyle.direction == 'rtl' ? 'right' : 'left'; 1230 | break; 1231 | default: 1232 | textAlign = 'left'; 1233 | } 1234 | 1235 | // 1.75 is an arbitrary number, as there is no info about the text baseline 1236 | switch (this.textBaseline) { 1237 | case 'hanging': 1238 | case 'top': 1239 | offset.y = fontStyle.size / 1.75; 1240 | break; 1241 | case 'middle': 1242 | break; 1243 | default: 1244 | case null: 1245 | case 'alphabetic': 1246 | case 'ideographic': 1247 | case 'bottom': 1248 | offset.y = -fontStyle.size / 2.25; 1249 | break; 1250 | } 1251 | 1252 | switch(textAlign) { 1253 | case 'right': 1254 | left = delta; 1255 | right = 0.05; 1256 | break; 1257 | case 'center': 1258 | left = right = delta / 2; 1259 | break; 1260 | } 1261 | 1262 | var d = getCoords(this, x + offset.x, y + offset.y); 1263 | 1264 | lineStr.push(''); 1268 | 1269 | if (stroke) { 1270 | appendStroke(this, lineStr); 1271 | } else { 1272 | // TODO: Fix the min and max params. 1273 | appendFill(this, lineStr, {x: -left, y: 0}, 1274 | {x: right, y: fontStyle.size}); 1275 | } 1276 | 1277 | var skewM = m[0][0].toFixed(3) + ',' + m[1][0].toFixed(3) + ',' + 1278 | m[0][1].toFixed(3) + ',' + m[1][1].toFixed(3) + ',0,0'; 1279 | 1280 | var skewOffset = mr(d.x / Z) + ',' + mr(d.y / Z); 1281 | 1282 | lineStr.push('', 1284 | '', 1285 | ''); 1290 | 1291 | this.element_.insertAdjacentHTML('beforeEnd', lineStr.join('')); 1292 | }; 1293 | 1294 | contextPrototype.fillText = function(text, x, y, maxWidth) { 1295 | this.drawText_(text, x, y, maxWidth, false); 1296 | }; 1297 | 1298 | contextPrototype.strokeText = function(text, x, y, maxWidth) { 1299 | this.drawText_(text, x, y, maxWidth, true); 1300 | }; 1301 | 1302 | contextPrototype.measureText = function(text) { 1303 | if (!this.textMeasureEl_) { 1304 | var s = ''; 1307 | this.element_.insertAdjacentHTML('beforeEnd', s); 1308 | this.textMeasureEl_ = this.element_.lastChild; 1309 | } 1310 | var doc = this.element_.ownerDocument; 1311 | this.textMeasureEl_.innerHTML = ''; 1312 | this.textMeasureEl_.style.font = this.font; 1313 | // Don't use innerHTML or innerText because they allow markup/whitespace. 1314 | this.textMeasureEl_.appendChild(doc.createTextNode(text)); 1315 | return {width: this.textMeasureEl_.offsetWidth}; 1316 | }; 1317 | 1318 | /******** STUBS ********/ 1319 | contextPrototype.clip = function() { 1320 | // TODO: Implement 1321 | }; 1322 | 1323 | contextPrototype.arcTo = function() { 1324 | // TODO: Implement 1325 | }; 1326 | 1327 | contextPrototype.createPattern = function(image, repetition) { 1328 | return new CanvasPattern_(image, repetition); 1329 | }; 1330 | 1331 | // Gradient / Pattern Stubs 1332 | function CanvasGradient_(aType) { 1333 | this.type_ = aType; 1334 | this.x0_ = 0; 1335 | this.y0_ = 0; 1336 | this.r0_ = 0; 1337 | this.x1_ = 0; 1338 | this.y1_ = 0; 1339 | this.r1_ = 0; 1340 | this.colors_ = []; 1341 | } 1342 | 1343 | CanvasGradient_.prototype.addColorStop = function(aOffset, aColor) { 1344 | aColor = processStyle(aColor); 1345 | this.colors_.push({offset: aOffset, 1346 | color: aColor.color, 1347 | alpha: aColor.alpha}); 1348 | }; 1349 | 1350 | function CanvasPattern_(image, repetition) { 1351 | assertImageIsValid(image); 1352 | switch (repetition) { 1353 | case 'repeat': 1354 | case null: 1355 | case '': 1356 | this.repetition_ = 'repeat'; 1357 | break 1358 | case 'repeat-x': 1359 | case 'repeat-y': 1360 | case 'no-repeat': 1361 | this.repetition_ = repetition; 1362 | break; 1363 | default: 1364 | throwException('SYNTAX_ERR'); 1365 | } 1366 | 1367 | this.src_ = image.src; 1368 | this.width_ = image.width; 1369 | this.height_ = image.height; 1370 | } 1371 | 1372 | function throwException(s) { 1373 | throw new DOMException_(s); 1374 | } 1375 | 1376 | function assertImageIsValid(img) { 1377 | if (!img || img.nodeType != 1 || img.tagName != 'IMG') { 1378 | throwException('TYPE_MISMATCH_ERR'); 1379 | } 1380 | if (img.readyState != 'complete') { 1381 | throwException('INVALID_STATE_ERR'); 1382 | } 1383 | } 1384 | 1385 | function DOMException_(s) { 1386 | this.code = this[s]; 1387 | this.message = s +': DOM Exception ' + this.code; 1388 | } 1389 | var p = DOMException_.prototype = new Error; 1390 | p.INDEX_SIZE_ERR = 1; 1391 | p.DOMSTRING_SIZE_ERR = 2; 1392 | p.HIERARCHY_REQUEST_ERR = 3; 1393 | p.WRONG_DOCUMENT_ERR = 4; 1394 | p.INVALID_CHARACTER_ERR = 5; 1395 | p.NO_DATA_ALLOWED_ERR = 6; 1396 | p.NO_MODIFICATION_ALLOWED_ERR = 7; 1397 | p.NOT_FOUND_ERR = 8; 1398 | p.NOT_SUPPORTED_ERR = 9; 1399 | p.INUSE_ATTRIBUTE_ERR = 10; 1400 | p.INVALID_STATE_ERR = 11; 1401 | p.SYNTAX_ERR = 12; 1402 | p.INVALID_MODIFICATION_ERR = 13; 1403 | p.NAMESPACE_ERR = 14; 1404 | p.INVALID_ACCESS_ERR = 15; 1405 | p.VALIDATION_ERR = 16; 1406 | p.TYPE_MISMATCH_ERR = 17; 1407 | 1408 | // set up externs 1409 | G_vmlCanvasManager = G_vmlCanvasManager_; 1410 | CanvasRenderingContext2D = CanvasRenderingContext2D_; 1411 | CanvasGradient = CanvasGradient_; 1412 | CanvasPattern = CanvasPattern_; 1413 | DOMException = DOMException_; 1414 | })(); 1415 | 1416 | } // if -------------------------------------------------------------------------------- /js/jit-yc.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011 Sencha Inc. - Author: Nicolas Garcia Belmonte (http://philogb.github.com/) 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | 22 | */ 23 | (function(){window.$jit=function(x){x=x||window;for(var y in $jit){if($jit[y].$extend){x[y]=$jit[y]}}};$jit.version="2.0.1";var c=function(w){return document.getElementById(w)};c.empty=function(){};c.extend=function(y,w){for(var x in (w||{})){y[x]=w[x]}return y};c.lambda=function(w){return(typeof w=="function")?w:function(){return w}};c.time=Date.now||function(){return +new Date};c.splat=function(x){var w=c.type(x);return w?((w!="array")?[x]:x):[]};c.type=function(x){var w=c.type.s.call(x).match(/^\[object\s(.*)\]$/)[1].toLowerCase();if(w!="object"){return w}if(x&&x.$$family){return x.$$family}return(x&&x.nodeName&&x.nodeType==1)?"element":w};c.type.s=Object.prototype.toString;c.each=function(B,A){var z=c.type(B);if(z=="object"){for(var y in B){A(B[y],y)}}else{for(var x=0,w=B.length;x>16,y>>8&255,y&255]}};c.destroy=function(w){c.clean(w);if(w.parentNode){w.parentNode.removeChild(w)}if(w.clearAttributes){w.clearAttributes()}};c.clean=function(z){for(var y=z.childNodes,x=0,w=y.length;x-1};c.addClass=function(x,w){if(!c.hasClass(x,w)){x.className=(x.className+" "+w)}};c.removeClass=function(x,w){x.className=x.className.replace(new RegExp("(^|\\s)"+w+"(?:\\s|$)"),"$1")};c.getPos=function(y){var B=A(y);var w=z(y);return{x:B.x-w.x,y:B.y-w.y};function A(D){var C={x:0,y:0};while(D&&!x(D)){C.x+=D.offsetLeft;C.y+=D.offsetTop;D=D.offsetParent}return C}function z(D){var C={x:0,y:0};while(D&&!x(D)){C.x+=D.scrollLeft;C.y+=D.scrollTop;D=D.parentNode}return C}function x(C){return(/^(?:body|html)$/i).test(C.tagName)}};c.event={get:function(x,w){w=w||window;return x||w.event},getWheel:function(w){return w.wheelDelta?w.wheelDelta/120:-(w.detail||0)/3},isRightClick:function(w){return(w.which==3||w.button==2)},getPos:function(z,y){y=y||window;z=z||y.event;var x=y.document;x=x.documentElement||x.body;if(z.touches&&z.touches.length){z=z.touches[0]}var w={x:z.pageX||(z.clientX+x.scrollLeft),y:z.pageY||(z.clientY+x.scrollTop)};return w},stop:function(w){if(w.stopPropagation){w.stopPropagation()}w.cancelBubble=true;if(w.preventDefault){w.preventDefault()}else{w.returnValue=false}}};$jit.util=$jit.id=c;var q=function(x){x=x||{};var w=function(){for(var A in this){if(typeof this[A]!="function"){this[A]=c.unlink(this[A])}}this.constructor=w;if(q.prototyping){return this}var z=this.initialize?this.initialize.apply(this,arguments):this;this.$$family="class";return z};for(var y in q.Mutators){if(!x[y]){continue}x=q.Mutators[y](x,x[y]);delete x[y]}c.extend(w,this);w.constructor=q;w.prototype=x;return w};q.Mutators={Implements:function(w,x){c.each(c.splat(x),function(z){q.prototyping=z;var y=(typeof z=="function")?new z:z;for(var A in y){if(!(A in w)){w[A]=y[A]}}delete q.prototyping});return w}};c.extend(q,{inherit:function(w,z){for(var y in z){var x=z[y];var B=w[y];var A=c.type(x);if(B&&A=="function"){if(x!=B){q.override(w,y,x)}}else{if(A=="object"){w[y]=c.merge(B,x)}else{w[y]=x}}}return w},override:function(x,w,A){var z=q.prototyping;if(z&&x[w]!=z[w]){z=null}var y=function(){var B=this.parent;this.parent=z?z[w]:x[w];var C=A.apply(this,arguments);this.parent=B;return C};x[w]=y}});q.prototype.implement=function(){var w=this.prototype;c.each(Array.prototype.slice.call(arguments||[]),function(x){q.inherit(w,x)});return this};$jit.Class=q;$jit.json={prune:function(x,w){this.each(x,function(z,y){if(y==w&&z.children){delete z.children;z.children=[]}})},getParent:function(w,A){if(w.id==A){return false}var z=w.children;if(z&&z.length>0){for(var y=0;y=(7-4*z)/11){A=y*y-Math.pow((11-6*z-11*B)/4,2);break}}return A},Elastic:function(z,y){return Math.pow(2,10*--z)*Math.cos(20*z*Math.PI*(y[0]||1)/3)}};c.each(x,function(z,y){i[y]=w(z)});c.each(["Quad","Cubic","Quart","Quint"],function(z,y){i[z]=w(function(A){return Math.pow(A,[y+2])})})})();var u=new q({initialize:function(w){this.setOptions(w)},setOptions:function(w){var x={duration:2500,fps:40,transition:i.Quart.easeInOut,compute:c.empty,complete:c.empty,link:"ignore"};this.opt=c.merge(x,w||{});return this},step:function(){var x=c.time(),w=this.opt;if(xD.height)?(F.y-C.height-E):F.y+E)+"px";A.left=((F.x+C.width+w>D.width)?(F.x-C.width-w):F.x+w)+"px"},hide:function(w){this.tip.style.display="none";w&&this.config.onHide()}});o.Classes.NodeStyles=new q({Implements:[t,h],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;this.hoveredNode=false;this.fx.nodeFxAnimation=new u();this.down=false;this.move=false},onMouseOut:function(y,x){this.down=this.move=false;if(!this.hoveredNode){return}if(this.dom&&this.isLabel(y,x,true)){this.toggleStylesOnHover(this.hoveredNode,false)}var w=y.relatedTarget,z=this.canvas.getElement();while(w&&w.parentNode){if(z==w.parentNode){return}w=w.parentNode}this.toggleStylesOnHover(this.hoveredNode,false);this.hoveredNode=false},onMouseOver:function(z,y){var w;if(this.dom&&(w=this.isLabel(z,y,true))){var x=this.viz.graph.getNode(w.id);if(x.selected){return}this.hoveredNode=x;this.toggleStylesOnHover(this.hoveredNode,true)}},onMouseDown:function(A,z,x,y){if(y){return}var w;if(this.dom&&(w=this.isLabel(A,z))){this.down=this.viz.graph.getNode(w.id)}else{if(!this.dom){this.down=x.getNode()}}this.move=false},onMouseUp:function(z,y,w,x){if(x){return}if(!this.move){this.onClick(w.getNode())}this.down=this.move=false},getRestoredStyles:function(x,w){var z={},y=this["nodeStylesOn"+w];for(var A in y){z[A]=x.styles["$"+A]}return z},toggleStylesOnHover:function(w,x){if(this.nodeStylesOnHover){this.toggleStylesOn("Hover",w,x)}},toggleStylesOnClick:function(w,x){if(this.nodeStylesOnClick){this.toggleStylesOn("Click",w,x)}},toggleStylesOn:function(A,w,C){var D=this.viz;var B=this.nStyles;if(C){var z=this;if(!w.styles){w.styles=c.merge(w.data,{})}for(var E in this["nodeStylesOn"+A]){var x="$"+E;if(!(x in w.styles)){w.styles[x]=w.getData(E)}}D.fx.nodeFx(c.extend({elements:{id:w.id,properties:z["nodeStylesOn"+A]},transition:i.Quart.easeOut,duration:300,fps:40},this.config))}else{var y=this.getRestoredStyles(w,A);D.fx.nodeFx(c.extend({elements:{id:w.id,properties:y},transition:i.Quart.easeOut,duration:300,fps:40},this.config))}},onClick:function(w){if(!w){return}var x=this.nodeStylesOnClick;if(!x){return}if(w.selected){this.toggleStylesOnClick(w,false);delete w.selected}else{this.viz.graph.eachNode(function(z){if(z.selected){for(var y in x){z.setData(y,z.styles["$"+y],"end")}delete z.selected}});this.toggleStylesOnClick(w,true);w.selected=true;delete w.hovered;this.hoveredNode=false}},onMouseMove:function(C,B,z){if(this.down){this.move=true}if(this.dom&&this.isLabel(C,B)){return}var A=this.nodeStylesOnHover;if(!A){return}if(!this.dom){if(this.hoveredNode){var x=this.types[this.hoveredNode.getData("type")];var w=x&&x.contains&&x.contains.call(this.fx,this.hoveredNode,z.getPos());if(w){return}}var y=z.getNode();if(!this.hoveredNode&&!y){return}if(y.hovered){return}if(y&&!y.selected){this.fx.nodeFxAnimation.stopTimer();this.viz.graph.eachNode(function(E){if(E.hovered&&!E.selected){for(var D in A){E.setData(D,E.styles["$"+D],"end")}delete E.hovered}});y.hovered=true;this.hoveredNode=y;this.toggleStylesOnHover(y,true)}else{if(this.hoveredNode&&!this.hoveredNode.selected){this.fx.nodeFxAnimation.stopTimer();this.toggleStylesOnHover(this.hoveredNode,false);delete this.hoveredNode.hovered;this.hoveredNode=false}}}}});o.Classes.Navigation=new q({Implements:[t,h],initializePost:function(){this.pos=false;this.pressed=false},onMouseWheel:function(z,y,w){if(!this.config.zooming){return}c.event.stop(c.event.get(z,y));var A=this.config.zooming/1000,x=1+w*A;this.canvas.scale(x,x)},onMouseDown:function(B,A,z){if(!this.config.panning){return}if(this.config.panning=="avoid nodes"&&(this.dom?this.isLabel(B,A):z.getNode())){return}this.pressed=true;this.pos=z.getPos();var y=this.canvas,x=y.translateOffsetX,w=y.translateOffsetY,D=y.scaleOffsetX,C=y.scaleOffsetY;this.pos.x*=D;this.pos.x+=x;this.pos.y*=C;this.pos.y+=w},onMouseMove:function(D,C,F){if(!this.config.panning){return}if(!this.pressed){return}if(this.config.panning=="avoid nodes"&&(this.dom?this.isLabel(D,C):F.getNode())){return}var B=this.pos,E=F.getPos(),z=this.canvas,A=z.translateOffsetX,w=z.translateOffsetY,J=z.scaleOffsetX,H=z.scaleOffsetY;E.x*=J;E.y*=H;E.x+=A;E.y+=w;var I=E.x-B.x,G=E.y-B.y;this.pos=E;this.canvas.translate(I*1/J,G*1/H)},onMouseUp:function(z,y,x,w){if(!this.config.panning){return}this.pressed=false}});var l;(function(){var w=typeof HTMLCanvasElement,y=(w=="object"||w=="function");function x(z,A){var B=document.createElement(z);for(var C in A){if(typeof A[C]=="object"){c.extend(B[C],A[C])}else{B[C]=A[C]}}if(z=="canvas"&&!y&&G_vmlCanvasManager){B=G_vmlCanvasManager.initElement(document.body.appendChild(B))}return B}$jit.Canvas=l=new q({canvases:[],pos:false,element:false,labelContainer:false,translateOffsetX:0,translateOffsetY:0,scaleOffsetX:1,scaleOffsetY:1,initialize:function(L,E){this.viz=L;this.opt=this.config=E;var B=c.type(E.injectInto)=="string"?E.injectInto:E.injectInto.id,K=E.type,C=B+"-label",z=c(B),D=E.width||z.offsetWidth,M=E.height||z.offsetHeight;this.id=B;var F={injectInto:B,width:D,height:M};this.element=x("div",{id:B+"-canvaswidget",style:{position:"relative",width:D+"px",height:M+"px"}});this.labelContainer=this.createLabelContainer(E.Label.type,C,F);this.canvases.push(new l.Base[K]({config:c.extend({idSuffix:"-canvas"},F),plot:function(N){L.fx.plot()},resize:function(){L.refresh()}}));var G=E.background;if(G){var J=new l.Background[G.type](L,c.extend(G,F));this.canvases.push(new l.Base[K](J))}var I=this.canvases.length;while(I--){this.element.appendChild(this.canvases[I].canvas);if(I>0){this.canvases[I].plot()}}this.element.appendChild(this.labelContainer);z.appendChild(this.element);var A=null,H=this;c.addEvent(window,"scroll",function(){clearTimeout(A);A=setTimeout(function(){H.getPos(true)},500)})},getCtx:function(z){return this.canvases[z||0].getCtx()},getConfig:function(){return this.opt},getElement:function(){return this.element},getSize:function(z){return this.canvases[z||0].getSize()},resize:function(D,z){this.getPos(true);this.translateOffsetX=this.translateOffsetY=0;this.scaleOffsetX=this.scaleOffsetY=1;for(var B=0,A=this.canvases.length;BE){A=x((E+((B-C)-E)*F))}else{A=x((E-C+(B-(E))*F))}}else{if(D>=z){if(B>E){A=x((E+((B-C)-E)*F))}else{A=x((E-C+(B-(E-C))*F))}}else{A=x((E+(B-E)*F))}}var w=(this.rho-y.rho)*F+y.rho;return{theta:A,rho:w}}};var k=function(x,w){return new b(x,w)};b.KER=k(0,0);var p=function(w,z){this.x=w||0;this.y=z||0};$jit.Complex=p;p.prototype={getc:function(){return this},getp:function(w){return this.toPolar(w)},set:function(w){w=w.getc(true);this.x=w.x;this.y=w.y},setc:function(w,z){this.x=w;this.y=z},setp:function(x,w){this.x=Math.cos(x)*w;this.y=Math.sin(x)*w},clone:function(){return new p(this.x,this.y)},toPolar:function(y){var w=this.norm();var x=Math.atan2(this.y,this.x);if(x<0){x+=Math.PI*2}if(y){return{theta:x,rho:w}}return new b(x,w)},norm:function(){return Math.sqrt(this.squaredNorm())},squaredNorm:function(){return this.x*this.x+this.y*this.y},add:function(w){return new p(this.x+w.x,this.y+w.y)},prod:function(w){return new p(this.x*w.x-this.y*w.y,this.y*w.x+this.x*w.y)},conjugate:function(){return new p(this.x,-this.y)},scale:function(w){return new p(this.x*w,this.y*w)},equals:function(w){return this.x==w.x&&this.y==w.y},$add:function(w){this.x+=w.x;this.y+=w.y;return this},$prod:function(A){var w=this.x,z=this.y;this.x=w*A.x-z*A.y;this.y=z*A.x+w*A.y;return this},$conjugate:function(){this.y=-this.y;return this},$scale:function(w){this.x*=w;this.y*=w;return this},$div:function(B){var w=this.x,A=this.y;var z=B.squaredNorm();this.x=w*B.x+A*B.y;this.y=A*B.x-w*B.y;return this.$scale(1/z)},isZero:function(){var x=0.0001,w=Math.abs;return w(this.x)=F&&I<=G&&w(H)){B(H,I)}if(II){y(K,F,G)}})}})(A,E+D,x+D)},eachSubgraph:function(x,y,w){this.eachLevel(x,0,false,y,w)},eachSubnode:function(x,y,w){this.eachLevel(x,1,1,y,w)},anySubnode:function(z,y,x){var w=false;y=y||c.lambda(true);var A=c.type(y)=="string"?function(B){return B[y]}:y;this.eachSubnode(z,function(B){if(A(B)){w=true}},x);return w},getSubnodes:function(B,C,w){var y=[],A=this;C=C||0;var z,x;if(c.type(C)=="array"){z=C[0];x=C[1]}else{z=C;x=Number.MAX_VALUE-B._depth}this.eachLevel(B,z,x,function(D){y.push(D)},w);return y},getParents:function(x){var w=[];this.eachAdjacency(x,function(y){var z=y.nodeTo;if(z._depth-1)){H.endData[J]=G[J]}else{H.data[J]=G[J]}}}});B.graph.eachNode(function(G){if(G.ignore){return}G.eachAdjacency(function(H){if(H.nodeFrom.ignore||H.nodeTo.ignore){return}var I=C.getNode(H.nodeFrom.id);var J=C.getNode(H.nodeTo.id);if(!I.adjacentTo(J)){var H=B.graph.getAdjacence(I.id,J.id);w=true;H.setData("alpha",1);H.setData("alpha",1,"start");H.setData("alpha",0,"end")}})});var w=this.preprocessSum(C);var y=!w?["node-property:alpha"]:["node-property:alpha","edge-property:alpha"];y[0]=y[0]+(("node-property" in z)?(":"+c.splat(z["node-property"]).join(":")):"");y[1]=(y[1]||"edge-property:alpha")+(("edge-property" in z)?(":"+c.splat(z["edge-property"]).join(":")):"");if("label-property" in z){y.push("label-property:"+c.splat(z["label-property"]).join(":"))}if(B.reposition){B.reposition()}else{B.compute("end")}B.graph.eachNode(function(G){if(G.id!=A&&G.pos.getp().equals(b.KER)){G.pos.set(G.endPos);G.startPos.set(G.endPos)}});B.fx.animate(c.merge(F,{modes:[z.position||"polar"].concat(y),onComplete:function(){B.graph.eachNode(function(G){if(G.ignore){B.graph.removeNode(G.id)}});B.graph.eachNode(function(G){G.eachAdjacency(function(H){if(H.ignore){B.graph.removeAdjacence(H.nodeFrom.id,H.nodeTo.id)}})});F.onComplete()}}));break;default:}},contract:function(y,x){var w=this.viz;if(y.collapsed||!y.anySubnode(c.lambda(true))){return}x=c.merge(this.options,w.config,x||{},{modes:["node-property:alpha:span","linear"]});y.collapsed=true;(function z(A){A.eachSubnode(function(B){B.ignore=true;B.setData("alpha",0,x.type=="animate"?"end":"current");z(B)})})(y);if(x.type=="animate"){w.compute("end");if(w.rotated){w.rotate(w.rotated,"none",{property:"end"})}(function z(A){A.eachSubnode(function(B){B.setPos(y.getPos("end"),"end");z(B)})})(y);w.fx.animate(x)}else{if(x.type=="replot"){w.refresh()}}},expand:function(y,x){if(!("collapsed" in y)){return}var w=this.viz;x=c.merge(this.options,w.config,x||{},{modes:["node-property:alpha:span","linear"]});delete y.collapsed;(function z(A){A.eachSubnode(function(B){delete B.ignore;B.setData("alpha",1,x.type=="animate"?"end":"current");z(B)})})(y);if(x.type=="animate"){w.compute("end");if(w.rotated){w.rotate(w.rotated,"none",{property:"end"})}w.fx.animate(x)}else{if(x.type=="replot"){w.refresh()}}},preprocessSum:function(x){var w=this.viz;x.eachNode(function(z){if(!w.graph.hasNode(z.id)){w.graph.addNode(z);var A=w.graph.getNode(z.id);A.setData("alpha",0);A.setData("alpha",0,"start");A.setData("alpha",1,"end")}});var y=false;x.eachNode(function(z){z.eachAdjacency(function(A){var B=w.graph.getNode(A.nodeFrom.id);var C=w.graph.getNode(A.nodeTo.id);if(!B.adjacentTo(C)){var A=w.graph.addAdjacence(B,C,A.data);if(B.startAlpha==B.endAlpha&&C.startAlpha==C.endAlpha){y=true;A.setData("alpha",0);A.setData("alpha",0,"start");A.setData("alpha",1,"end")}}})});return y}};var a={none:{render:c.empty,contains:c.lambda(false)},circle:{render:function(z,A,w,y){var x=y.getCtx();x.beginPath();x.arc(A.x,A.y,w,0,Math.PI*2,true);x.closePath();x[z]()},contains:function(B,A,w){var y=B.x-A.x,x=B.y-A.y,z=y*y+x*x;return z<=w*w}},ellipse:{render:function(C,E,w,F,x){var G=x.getCtx(),z=1,y=1,D=1,B=1,A=0;if(w>F){A=w/2;y=F/w;B=w/F}else{A=F/2;z=w/F;D=F/w}G.save();G.scale(z,y);G.beginPath();G.arc(E.x*D,E.y*B,A,0,Math.PI*2,true);G.closePath();G[C]();G.restore()},contains:function(w,D,x,F){var C=0,B=1,A=1,z=0,y=0,E=0;if(x>F){C=x/2;A=F/x}else{C=F/2;B=x/F}z=(w.x-D.x)*(1/B);y=(w.y-D.y)*(1/A);E=z*z+y*y;return E<=C*C}},square:{render:function(x,z,y,w){w.getCtx()[x+"Rect"](z.x-y,z.y-y,2*y,2*y)},contains:function(y,x,w){return Math.abs(x.x-y.x)<=w&&Math.abs(x.y-y.y)<=w}},rectangle:{render:function(z,A,y,w,x){x.getCtx()[z+"Rect"](A.x-y/2,A.y-w/2,y,w)},contains:function(z,y,x,w){return Math.abs(y.x-z.x)<=x/2&&Math.abs(y.y-z.y)<=w/2}},triangle:{render:function(C,D,z,w){var G=w.getCtx(),y=D.x,x=D.y-z,F=y-z,E=D.y+z,B=y+z,A=E;G.beginPath();G.moveTo(y,x);G.lineTo(F,E);G.lineTo(B,A);G.closePath();G[C]()},contains:function(y,x,w){return a.circle.contains(y,x,w)}},star:{render:function(A,C,B,x){var w=x.getCtx(),z=Math.PI/5;w.save();w.translate(C.x,C.y);w.beginPath();w.moveTo(B,0);for(var y=0;y<9;y++){w.rotate(z);if(y%2==0){w.lineTo((B/0.525731)*0.200811,0)}else{w.lineTo(B,0)}}w.closePath();w[A]();w.restore()},contains:function(y,x,w){return a.circle.contains(y,x,w)}}};var m={line:{render:function(z,y,x){var w=x.getCtx();w.beginPath();w.moveTo(z.x,z.y);w.lineTo(y.x,y.y);w.stroke()},contains:function(G,y,B,E){var z=Math.min,C=Math.max,x=z(G.x,y.x),F=C(G.x,y.x),w=z(G.y,y.y),D=C(G.y,y.y);if(B.x>=x&&B.x<=F&&B.y>=w&&B.y<=D){if(Math.abs(y.x-G.x)<=E){return true}var A=(y.y-G.y)/(y.x-G.x)*(B.x-G.x)+G.y;return Math.abs(A-B.y)<=E}return false}},arrow:{render:function(F,G,z,x,w){var H=w.getCtx();if(x){var y=F;F=G;G=y}var C=new p(G.x-F.x,G.y-F.y);C.$scale(z/C.norm());var A=new p(G.x-C.x,G.y-C.y),B=new p(-C.y/2,C.x/2),E=A.add(B),D=A.$add(B.$scale(-1));H.beginPath();H.moveTo(F.x,F.y);H.lineTo(G.x,G.y);H.stroke();H.beginPath();H.moveTo(E.x,E.y);H.lineTo(D.x,D.y);H.lineTo(G.x,G.y);H.closePath();H.fill()},contains:function(x,w,z,y){return m.line.contains(x,w,z,y)}},hyperline:{render:function(D,E,w,y){var F=y.getCtx();var z=A(D,E);if(z.a>1000||z.b>1000||z.ratio<0){F.beginPath();F.moveTo(D.x*w,D.y*w);F.lineTo(E.x*w,E.y*w);F.stroke()}else{var C=Math.atan2(E.y-z.y,E.x-z.x);var B=Math.atan2(D.y-z.y,D.x-z.x);var x=x(C,B);F.beginPath();F.arc(z.x*w,z.y*w,z.ratio*w,C,B,x);F.stroke()}function A(S,R){var K=(S.x*R.y-S.y*R.x),G=K;var J=S.squaredNorm(),I=R.squaredNorm();if(K==0){return{x:0,y:0,ratio:-1}}var Q=(S.y*I-R.y*J+S.y-R.y)/K;var O=(R.x*J-S.x*I+R.x-S.x)/G;var P=-Q/2;var N=-O/2;var M=(Q*Q+O*O)/4-1;if(M<0){return{x:0,y:0,ratio:-1}}var L=Math.sqrt(M);var H={x:P,y:N,ratio:L>1000?-1:L,a:Q,b:O};return H}function x(G,H){return(GH)?false:true):((H+Math.PI>G)?true:false)}},contains:c.lambda(false)}};e.Plot={initialize:function(x,w){this.viz=x;this.config=x.config;this.node=x.config.Node;this.edge=x.config.Edge;this.animation=new u;this.nodeTypes=new w.Plot.NodeTypes;this.edgeTypes=new w.Plot.EdgeTypes;this.labels=x.labels},nodeHelper:a,edgeHelper:m,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"},compute:function(y,x,w){return y+(x-y)*w},moebius:function(D,C,F,z){var B=z.scale(-F);if(B.norm()<1){var w=B.x,E=B.y;var A=D.startPos.getc().moebiusTransformation(B);D.pos.setc(A.x,A.y);B.x=w;B.y=E}},linear:function(x,w,A){var z=x.startPos.getc(true);var y=x.endPos.getc(true);x.pos.setc(this.compute(z.x,y.x,A),this.compute(z.y,y.y,A))},polar:function(y,x,B){var A=y.startPos.getp(true);var z=y.endPos.getp();var w=z.interpolate(A,B);y.pos.setp(w.theta,w.rho)},number:function(x,C,B,w,A){var z=x[w](C,"start");var y=x[w](C,"end");x[A](C,this.compute(z,y,B))},color:function(y,w,E,B,z){var C=c.hexToRgb(y[B](w,"start"));var D=c.hexToRgb(y[B](w,"end"));var A=this.compute;var x=c.rgbToHex([parseInt(A(C[0],D[0],E)),parseInt(A(C[1],D[1],E)),parseInt(A(C[2],D[2],E))]);y[z](w,x)},"array-number":function(z,y,J,G,B){var H=z[G](y,"start"),I=z[G](y,"end"),K=[];for(var E=0,A=H.length;E=0.95){C.labels.plotLabel(y,I,x)}else{C.labels.hideLabel(I,false)}}I.visited=!z})},plotTree:function(A,x,E){var B=this,C=this.viz,y=C.canvas,z=this.config,D=y.getCtx();var w=A.getData("alpha");A.eachSubnode(function(G){if(x.plotSubtree(A,G)&&G.exist&&G.drawn){var F=A.getAdjacency(G.id);!E&&x.onBeforePlotLine(F);B.plotLine(F,y,E);!E&&x.onAfterPlotLine(F);B.plotTree(G,x,E)}});if(A.drawn){!E&&x.onBeforePlotNode(A);this.plotNode(A,y,E);!E&&x.onAfterPlotNode(A);if(!x.hideLabels&&x.withLabels&&w>=0.95){this.labels.plotLabel(y,A,x)}else{this.labels.hideLabel(A,false)}}else{this.labels.hideLabel(A,true)}},plotNode:function(y,x,F){var C=y.getData("type"),B=this.node.CanvasStyles;if(C!="none"){var w=y.getData("lineWidth"),A=y.getData("color"),z=y.getData("alpha"),D=x.getCtx();D.save();D.lineWidth=w;D.fillStyle=D.strokeStyle=A;D.globalAlpha=z;for(var E in B){D[E]=y.getCanvasStyle(E)}this.nodeTypes[C].render.call(this,y,x,F);D.restore()}},plotLine:function(C,x,G){var B=C.getData("type"),z=this.edge.CanvasStyles;if(B!="none"){var w=C.getData("lineWidth"),y=C.getData("color"),E=x.getCtx(),A=C.nodeFrom,D=C.nodeTo;E.save();E.lineWidth=w;E.fillStyle=E.strokeStyle=y;E.globalAlpha=Math.min(A.getData("alpha"),D.getData("alpha"),C.getData("alpha"));for(var F in z){E[F]=C.getCanvasStyle(F)}this.edgeTypes[B].render.call(this,C,x,G);E.restore()}}};e.Plot3D=c.merge(e.Plot,{Interpolator:{linear:function(x,w,A){var z=x.startPos.getc(true);var y=x.endPos.getc(true);x.pos.setc(this.compute(z.x,y.x,A),this.compute(z.y,y.y,A),this.compute(z.z,y.z,A))}},plotNode:function(x,w){if(x.getData("type")=="none"){return}this.plotElement(x,w,{getAlpha:function(){return x.getData("alpha")}})},plotLine:function(w,x){if(w.getData("type")=="none"){return}this.plotElement(w,x,{getAlpha:function(){return Math.min(w.nodeFrom.getData("alpha"),w.nodeTo.getData("alpha"),w.getData("alpha"))}})},plotElement:function(Y,E,z){var V=E.getCtx(),F=new Matrix4,x=E.config.Scene.Lighting,Z=E.canvases[0],K=Z.program,X=Z.camera;if(!Y.geometry){Y.geometry=new O3D[Y.getData("type")]}Y.geometry.update(Y);if(!Y.webGLVertexBuffer){var J=[],B=[],P=[],N=0,S=Y.geometry;for(var W=0,U=S.vertices,H=S.faces,G=H.length;W=x.width||y.x<0||y.y>=x.height||y.y<0){return false}return true}});e.Label.HTML=new q({Implements:e.Label.DOM,plotLabel:function(z,A,y){var B=A.id,w=this.getLabel(B);if(!w&&!(w=document.getElementById(B))){w=document.createElement("div");var x=this.getLabelContainer();w.id=B;w.className="node";w.style.position="absolute";y.onCreateLabel(w,A);x.appendChild(w);this.labels[A.id]=w}this.placeLabel(w,A,y)}});e.Label.SVG=new q({Implements:e.Label.DOM,plotLabel:function(z,B,y){var D=B.id,w=this.getLabel(D);if(!w&&!(w=document.getElementById(D))){var A="http://www.w3.org/2000/svg";w=document.createElementNS(A,"svg:text");var C=document.createElementNS(A,"svg:tspan");w.appendChild(C);var x=this.getLabelContainer();w.setAttribute("id",D);w.setAttribute("class","node");x.appendChild(w);y.onCreateLabel(w,B);this.labels[B.id]=w}this.placeLabel(w,B,y)}});e.Geom=new q({initialize:function(w){this.viz=w;this.config=w.config;this.node=w.config.Node;this.edge=w.config.Edge},translate:function(x,w){w=c.splat(w);this.viz.graph.eachNode(function(y){c.each(w,function(z){y.getPos(z).$add(x)})})},setRightLevelToShow:function(z,w,B){var A=this.getRightLevelToShow(z,w),y=this.viz.labels,x=c.merge({execShow:true,execHide:true,onHide:c.empty,onShow:c.empty},B||{});z.eachLevel(0,this.config.levelsToShow,function(D){var C=D._depth-z._depth;if(C>A){x.onHide(D);if(x.execHide){D.drawn=false;D.exist=false;y.hideLabel(D,false)}}else{x.onShow(D);if(x.execShow){D.exist=true}}});z.drawn=true},getRightLevelToShow:function(z,x){var w=this.config;var A=w.levelsToShow;var y=w.constrained;if(!y){return A}while(!this.treeFitsInCanvas(z,x,A)&&A>1){A--}return A}});var d={construct:function(x){var y=(c.type(x)=="array");var w=new e(this.graphOptions,this.config.Node,this.config.Edge,this.config.Label);if(!y){(function(z,B){z.addNode(B);if(B.children){for(var A=0,C=B.children;AC?F:C;D.setData("width",E);D.setData("height",E);D.setData("dim",E)}}})},initializeLabel:function(w){if(!this.label){this.label=document.createElement("div");document.body.appendChild(this.label)}this.setLabelStyles(w)},setLabelStyles:function(w){c.extend(this.label.style,{visibility:"hidden",position:"absolute",width:"auto",height:"auto"});this.label.className="jit-autoadjust-label"}};g.Tree=(function(){var F=Array.prototype.slice;function D(P,K,H,N,I){var M=K.Node;var J=K.multitree;if(M.overridable){var O=-1,L=-1;P.eachNode(function(S){if(S._depth==H&&(!J||("$orn" in S.data)&&S.data.$orn==N)){var Q=S.getData("width",I);var R=S.getData("height",I);O=(O0)?E[0]:null;D(E)}for(var z=0,A=[C.id].concat(x);z=D._depth)});for(var B=0;B0&&J.drawn){J.drawn=false;x[A.id].push(J)}else{if((!E||!D)&&J.drawn){J.drawn=false;x[A.id].push(J)}}});A.drawn=true}if(w.length>0){F.fx.plot()}for(B in x){c.each(x[B],function(J){J.drawn=true})}for(B=0;Bw?y:w)+this.config.subtreeOffset},getEdge:function(C,B,A){var y=function(E,w){return function(){return C.pos.add(new p(E,w))}};var D=this.node;var x=C.getData("width");var z=C.getData("height");if(B=="begin"){if(D.align=="center"){return this.dispatch(A,y(0,z/2),y(-x/2,0),y(0,-z/2),y(x/2,0))}else{if(D.align=="left"){return this.dispatch(A,y(0,z),y(0,0),y(0,0),y(x,0))}else{if(D.align=="right"){return this.dispatch(A,y(0,0),y(-x,0),y(0,-z),y(0,0))}else{throw"align: not implemented"}}}}else{if(B=="end"){if(D.align=="center"){return this.dispatch(A,y(0,-z/2),y(x/2,0),y(0,z/2),y(-x/2,0))}else{if(D.align=="left"){return this.dispatch(A,y(0,0),y(x,0),y(0,z),y(0,0))}else{if(D.align=="right"){return this.dispatch(A,y(0,-z),y(0,0),y(0,0),y(-x,0))}else{throw"align: not implemented"}}}}}},getScaledTreePosition:function(B,D){var C=this.node;var x=B.getData("width");var A=B.getData("height");var z=(this.config.multitree&&("$orn" in B.data)&&B.data.$orn)||this.config.orientation;var y=function(E,w){return function(){return B.pos.add(new p(E,w)).$scale(1-D)}};if(C.align=="left"){return this.dispatch(z,y(0,A),y(0,0),y(0,0),y(x,0))}else{if(C.align=="center"){return this.dispatch(z,y(0,A/2),y(-x/2,0),y(0,-A/2),y(x/2,0))}else{if(C.align=="right"){return this.dispatch(z,y(0,0),y(-x,0),y(0,-A),y(0,0))}else{throw"align: not implemented"}}}},treeFitsInCanvas:function(B,w,C){var y=w.getSize();var z=(this.config.multitree&&("$orn" in B.data)&&B.data.$orn)||this.config.orientation;var x=this.dispatch(z,y.width,y.height);var A=this.getTreeBaseSize(B,C,function(E,D){return E===0||!D.anySubnode()});return(A=0){z.drawn=false;var D=x.getCtx();var B=C.geom.getScaledTreePosition(z,A);D.translate(B.x,B.y);D.scale(A,A)}this.plotTree(z,c.merge(w,{withLabels:true,hideLabels:!!A,plotSubtree:function(I,G){var F=y.multitree&&!("$orn" in z.data);var H=F&&z.getData("orns");return !F||H.indexOf(z.getData("orn"))>-1}}),E);if(A>=0){z.drawn=true}},getAlignedPos:function(B,z,w){var y=this.node;var A,x;if(y.align=="center"){A={x:B.x-z/2,y:B.y-w/2}}else{if(y.align=="left"){x=this.config.orientation;if(x=="bottom"||x=="top"){A={x:B.x-z/2,y:B.y}}else{A={x:B.x,y:B.y-w/2}}}else{if(y.align=="right"){x=this.config.orientation;if(x=="bottom"||x=="top"){A={x:B.x-z/2,y:B.y-w}}else{A={x:B.x-z,y:B.y-w/2}}}else{throw"align: not implemented"}}}return A},getOrientation:function(w){var y=this.config;var x=y.orientation;if(y.multitree){var z=w.nodeFrom;var A=w.nodeTo;x=(("$orn" in z.data)&&z.data.$orn)||(("$orn" in A.data)&&A.data.$orn)}return x}});$jit.ST.Label={};$jit.ST.Label.Native=new q({Implements:e.Label.Native,renderLabel:function(z,B,y){var x=z.getCtx(),D=B.pos.getc(true),A=B.getData("width"),w=B.getData("height"),C=this.viz.fx.getAlignedPos(D,A,w);x.fillText(B.name,C.x+A/2,C.y+w/2)}});$jit.ST.Label.DOM=new q({Implements:e.Label.DOM,placeLabel:function(P,J,F){var B=J.pos.getc(true),O=this.viz.config,K=O.Node,x=this.viz.canvas,C=J.getData("width"),M=J.getData("height"),y=x.getSize(),G,N;var A=x.translateOffsetX,z=x.translateOffsetY,E=x.scaleOffsetX,D=x.scaleOffsetY,I=B.x*E+A,H=B.y*D+z;if(K.align=="center"){G={x:Math.round(I-C/2+y.width/2),y:Math.round(H-M/2+y.height/2)}}else{if(K.align=="left"){N=O.orientation;if(N=="bottom"||N=="top"){G={x:Math.round(I-C/2+y.width/2),y:Math.round(H+y.height/2)}}else{G={x:Math.round(I+y.width/2),y:Math.round(H-M/2+y.height/2)}}}else{if(K.align=="right"){N=O.orientation;if(N=="bottom"||N=="top"){G={x:Math.round(I-C/2+y.width/2),y:Math.round(H-M+y.height/2)}}else{G={x:Math.round(I-C+y.width/2),y:Math.round(H-M/2+y.height/2)}}}else{throw"align: not implemented"}}}var L=P.style;L.left=G.x+"px";L.top=G.y+"px";L.display=this.fitsInCanvas(G,x)?"":"none";F.onPlaceLabel(P,J)}});$jit.ST.Label.SVG=new q({Implements:[$jit.ST.Label.DOM,e.Label.SVG],initialize:function(w){this.viz=w}});$jit.ST.Label.HTML=new q({Implements:[$jit.ST.Label.DOM,e.Label.HTML],initialize:function(w){this.viz=w}});$jit.ST.Plot.NodeTypes=new q({none:{render:c.empty,contains:c.lambda(false)},circle:{render:function(x,w){var z=x.getData("dim"),A=this.getAlignedPos(x.pos.getc(true),z,z),y=z/2;this.nodeHelper.circle.render("fill",{x:A.x+y,y:A.y+y},y,w)},contains:function(w,A){var y=w.getData("dim"),z=this.getAlignedPos(w.pos.getc(true),y,y),x=y/2;this.nodeHelper.circle.contains({x:z.x+x,y:z.y+x},A,x)}},square:{render:function(x,w){var z=x.getData("dim"),y=z/2,A=this.getAlignedPos(x.pos.getc(true),z,z);this.nodeHelper.square.render("fill",{x:A.x+y,y:A.y+y},y,w)},contains:function(w,A){var y=w.getData("dim"),z=this.getAlignedPos(w.pos.getc(true),y,y),x=y/2;this.nodeHelper.square.contains({x:z.x+x,y:z.y+x},A,x)}},ellipse:{render:function(z,x){var y=z.getData("width"),w=z.getData("height"),A=this.getAlignedPos(z.pos.getc(true),y,w);this.nodeHelper.ellipse.render("fill",{x:A.x+y/2,y:A.y+w/2},y,w,x)},contains:function(y,A){var x=y.getData("width"),w=y.getData("height"),z=this.getAlignedPos(y.pos.getc(true),x,w);this.nodeHelper.ellipse.contains({x:z.x+x/2,y:z.y+w/2},A,x,w)}},rectangle:{render:function(z,x){var y=z.getData("width"),w=z.getData("height"),A=this.getAlignedPos(z.pos.getc(true),y,w);this.nodeHelper.rectangle.render("fill",{x:A.x+y/2,y:A.y+w/2},y,w,x)},contains:function(y,A){var x=y.getData("width"),w=y.getData("height"),z=this.getAlignedPos(y.pos.getc(true),x,w);this.nodeHelper.rectangle.contains({x:z.x+x/2,y:z.y+w/2},A,x,w)}}});$jit.ST.Plot.EdgeTypes=new q({none:c.empty,line:{render:function(x,z){var y=this.getOrientation(x),A=x.nodeFrom,B=x.nodeTo,w=A._depth1&&D[0]!=y.id);this.edgeHelper.arrow.render(E,F,A,z,x)},contains:function(x,D){var y=this.getOrientation(x),z=x.nodeFrom,A=x.nodeTo,w=z._depth0||F[ah][1]>0)){var R=K+F[ah][0],P=E+F[ah][1],ag=Math.atan((P-R)/w),Z=55;var V=M.createLinearGradient(ab+w/2,aa-(R+P)/2,ab+w/2+Z*Math.sin(ag),aa-(R+P)/2+Z*Math.cos(ag));var Q=c.rgbToHex(c.map(c.hexToRgb(I[ah%C].slice(1)),function(x){return(x*0.85)>>0}));V.addColorStop(0,I[ah%C]);V.addColorStop(1,Q);M.fillStyle=V}M.beginPath();M.moveTo(ab,aa-K);M.lineTo(ab+w,aa-E);M.lineTo(ab+w,aa-E-F[ah][1]);M.lineTo(ab,aa-K-F[ah][0]);M.lineTo(ab,aa-K);M.fill();M.restore();if(H){var S=H.name==L[ah];var z=S?0.7:0.8;var Q=c.rgbToHex(c.map(c.hexToRgb(I[ah%C].slice(1)),function(x){return(x*z)>>0}));M.strokeStyle=Q;M.lineWidth=S?4:1;M.save();M.beginPath();if(H.index===0){M.moveTo(ab,aa-K);M.lineTo(ab,aa-K-F[ah][0])}else{M.moveTo(ab+w,aa-E);M.lineTo(ab+w,aa-E-F[ah][1])}M.stroke();M.restore()}K+=(F[ah][0]||0);E+=(F[ah][1]||0);if(F[ah][0]>0){X+=(B[ah][0]||0)}}if(T&&ae.type=="Native"){M.save();M.beginPath();M.fillStyle=M.strokeStyle=ae.color;M.font=ae.style+" "+ae.size+"px "+ae.family;M.textAlign="center";M.textBaseline="middle";var O=N(W.name,ad,ac,W,X);if(O!==false){M.fillText(O!==true?O:X,ab,aa-K-Y.labelOffset-ae.size/2,w)}if(ai(W.name,ad,ac,W)){M.fillText(W.name,ab,aa+ae.size/2+Y.labelOffset)}M.restore()}}},contains:function(C,E){var J=C.pos.getc(true),z=C.getData("width"),N=C.getData("height"),M=this.getAlignedPos(J,z,N),L=M.x,K=M.y,O=C.getData("dimArray"),w=E.x-L;if(E.xL+z||E.y>K||E.y=G){var H=+(w>z/2);return{name:C.getData("stringArray")[F],color:C.getData("colorArray")[F],value:C.getData("valueArray")[F][H],index:H}}}return false}}});$jit.AreaChart=new q({st:null,colors:["#416D9C","#70A35E","#EBB056","#C74243","#83548B","#909291","#557EAA"],selected:{},busy:false,initialize:function(y){this.controller=this.config=c.merge(n("Canvas","Margin","Label","AreaChart"),{Label:{type:"Native"}},y);var z=this.config.showLabels,x=c.type(z),A=this.config.showAggregates,w=c.type(A);this.config.showLabels=x=="function"?z:c.lambda(z);this.config.showAggregates=w=="function"?A:c.lambda(A);this.initializeViz()},initializeViz:function(){var x=this.config,B=this,w=x.type.split(":")[0],A={};var z=new $jit.ST({injectInto:x.injectInto,width:x.width,height:x.height,orientation:"bottom",levelDistance:0,siblingOffset:0,subtreeOffset:0,withLabels:x.Label.type!="Native",useCanvas:x.useCanvas,Label:{type:x.Label.type},Node:{overridable:true,type:"areachart-"+w,align:"left",width:1,height:1},Edge:{type:"none"},Tips:{enable:x.Tips.enable,type:"Native",force:true,onShow:function(G,F,D){var E=D;x.Tips.onShow(G,E,F)}},Events:{enable:true,type:"Native",onClick:function(F,G,D){if(!x.filterOnClick&&!x.Events.enable){return}var E=G.getContains();if(E){x.filterOnClick&&B.filter(E.name)}x.Events.enable&&x.Events.onClick(E,G,D)},onRightClick:function(E,F,D){if(!x.restoreOnRightClick){return}B.restore()},onMouseMove:function(F,G,D){if(!x.selectOnHover){return}if(F){var E=G.getContains();B.select(F.id,E.name,E.index)}else{B.select(false,false,false)}}},onCreateLabel:function(J,G){var P=x.Label,O=G.getData("valueArray"),H=c.reduce(O,function(Q,R){return Q+R[0]},0),M=c.reduce(O,function(Q,R){return Q+R[1]},0);if(G.getData("prev")){var L={wrapper:document.createElement("div"),aggregate:document.createElement("div"),label:document.createElement("div")};var D=L.wrapper,N=L.label,E=L.aggregate,F=D.style,K=N.style,I=E.style;A[G.id]=L;D.appendChild(N);D.appendChild(E);if(!x.showLabels(G.name,H,M,G)){N.style.display="none"}if(!x.showAggregates(G.name,H,M,G)){E.style.display="none"}F.position="relative";F.overflow="visible";F.fontSize=P.size+"px";F.fontFamily=P.family;F.color=P.color;F.textAlign="center";I.position=K.position="absolute";J.style.width=G.getData("width")+"px";J.style.height=G.getData("height")+"px";N.innerHTML=G.name;J.appendChild(D)}},onPlaceLabel:function(V,P){if(!P.getData("prev")){return}var T=A[P.id],E=T.wrapper.style,D=T.label.style,O=T.aggregate.style,M=P.getData("width"),K=P.getData("height"),J=P.getData("dimArray"),G=P.getData("valueArray"),L=c.reduce(G,function(W,X){return W+X[0]},0),H=c.reduce(G,function(W,X){return W+X[1]},0),I=parseInt(E.fontSize,10),N=V.style;if(J&&G){if(x.showLabels(P.name,L,H,P)){D.display=""}else{D.display="none"}var F=x.showAggregates(P.name,L,H,P);if(F!==false){O.display=""}else{O.display="none"}E.width=O.width=D.width=V.style.width=M+"px";O.left=D.left=-M/2+"px";for(var S=0,Q=G.length,R=0,U=0;S0){R+=G[S][0];U+=J[S][0]}}O.top=(-I-x.labelOffset)+"px";D.top=(x.labelOffset+U)+"px";V.style.top=parseInt(V.style.top,10)-U+"px";V.style.height=E.height=U+"px";T.aggregate.innerHTML=F!==true?F:R}}});var y=z.canvas.getSize(),C=x.Margin;z.config.offsetY=-y.height/2+C.bottom+(x.showLabels&&(x.labelOffset+x.Label.size));z.config.offsetX=(C.right-C.left)/2;this.delegate=z;this.canvas=this.delegate.canvas},loadJSON:function(N){var J=c.time(),B=[],M=this.delegate,Q=c.splat(N.label),I=c.splat(N.color||this.colors),O=this.config,x=!!O.type.split(":")[1],z=O.animate;for(var K=0,y=N.values,H=y.length;K-1)?G:[0,0]}),"end")});this.delegate.fx.animate({modes:["node-property:dimArray"],duration:1500,onComplete:function(){y.busy=false;A&&A.onComplete()}})},restore:function(x){if(this.busy){return}this.busy=true;if(this.config.Tips.enable){this.delegate.tips.hide()}this.select(false,false,false);this.normalizeDims();var w=this;this.delegate.fx.animate({modes:["node-property:height:dimArray"],duration:1500,onComplete:function(){w.busy=false;x&&x.onComplete()}})},select:function(B,x,w){if(!this.config.selectOnHover){return}var y=this.selected;if(y.id!=B||y.name!=x||y.index!=w){y.id=B;y.name=x;y.index=w;this.delegate.graph.eachNode(function(C){C.setData("border",false)});if(B){var A=this.delegate.graph.getNode(B);A.setData("border",y);var z=w===0?"prev":"next";z=A.getData(z);if(z){A=this.delegate.graph.getByName(z);if(A){A.setData("border",{name:x,index:1-w})}}}this.delegate.plot()}},getLegend:function(){var y={};var z;this.delegate.graph.getNode(this.delegate.root).eachAdjacency(function(A){z=A.nodeTo});var x=z.getData("colorArray"),w=x.length;c.each(z.getData("stringArray"),function(B,A){y[B]=x[A%w]});return y},getMaxValue:function(){var w=0;this.delegate.graph.eachNode(function(B){var y=B.getData("valueArray"),x=0,A=0;c.each(y,function(C){x+=+C[0];A+=+C[1]});var z=A>x?A:x;w=w>z?w:z});return w},normalizeDims:function(){var C=this.delegate.graph.getNode(this.delegate.root),z=0;C.eachAdjacency(function(){z++});var B=this.getMaxValue()||1,F=this.delegate.canvas.getSize(),y=this.config,A=y.Margin,D=y.labelOffset+y.Label.size,w=(F.width-(A.left+A.right))/z,x=y.animate,E=F.height-(A.top+A.bottom)-(y.showAggregates&&D)-(y.showLabels&&D);this.delegate.graph.eachNode(function(L){var I=0,K=0,G=[];c.each(L.getData("valueArray"),function(M){I+=+M[0];K+=+M[1];G.push([0,0])});var J=K>I?K:I;L.setData("width",w);if(x){L.setData("height",J*E/B,"end");L.setData("dimArray",c.map(L.getData("valueArray"),function(M){return[M[0]*E/B,M[1]*E/B]}),"end");var H=L.getData("dimArray");if(!H){L.setData("dimArray",G)}}else{L.setData("height",J*E/B);L.setData("dimArray",c.map(L.getData("valueArray"),function(M){return[M[0]*E/B,M[1]*E/B]}))}})}});n.BarChart={$extend:true,animate:true,type:"stacked",labelOffset:3,barsOffset:0,hoveredColor:"#9fd4ff",orientation:"horizontal",showAggregates:true,showLabels:true,Tips:{enable:false,onShow:c.empty,onHide:c.empty},Events:{enable:false,onClick:c.empty}};$jit.ST.Plot.NodeTypes.implement({"barchart-stacked":{render:function(R,C){var H=R.pos.getc(true),Q=R.getData("width"),O=R.getData("height"),M=this.getAlignedPos(H,Q,O),L=M.x,K=M.y,N=R.getData("dimArray"),F=R.getData("valueArray"),E=R.getData("colorArray"),B=E.length,Y=R.getData("stringArray");var T=C.getCtx(),w={},U=R.getData("border"),z=R.getData("gradient"),aa=R.getData("config"),A=aa.orientation=="horizontal",D=aa.showAggregates,P=aa.showLabels,J=aa.Label;if(E&&N&&Y){for(var X=0,S=N.length,W=0,G=0;X>0}));Z.addColorStop(0,V);Z.addColorStop(0.5,E[X%B]);Z.addColorStop(1,V);T.fillStyle=Z}if(A){T.fillRect(L+W,K,N[X],O)}else{T.fillRect(L,K-W-N[X],Q,N[X])}if(U&&U.name==Y[X]){w.acum=W;w.dimValue=N[X]}W+=(N[X]||0);G+=(F[X]||0)}if(U){T.save();T.lineWidth=2;T.strokeStyle=U.color;if(A){T.strokeRect(L+w.acum+1,K+1,w.dimValue-2,O-2)}else{T.strokeRect(L+1,K-w.acum-w.dimValue+1,Q-2,w.dimValue-2)}T.restore()}if(J.type=="Native"){T.save();T.fillStyle=T.strokeStyle=J.color;T.font=J.style+" "+J.size+"px "+J.family;T.textBaseline="middle";var I=D(R.name,G,R);if(I!==false){I=I!==true?I:G;if(A){T.textAlign="right";T.fillText(I,L+W-aa.labelOffset,K+O/2)}else{T.textAlign="center";T.fillText(I,L+Q/2,K-O-J.size/2-aa.labelOffset)}}if(P(R.name,G,R)){if(A){T.textAlign="center";T.translate(L-aa.labelOffset-J.size/2,K+O/2);T.rotate(Math.PI/2);T.fillText(R.name,0,0)}else{T.textAlign="center";T.fillText(R.name,L+Q/2,K+J.size/2+aa.labelOffset)}}T.restore()}}},contains:function(D,F){var I=D.pos.getc(true),A=D.getData("width"),N=D.getData("height"),M=this.getAlignedPos(I,A,N),L=M.x,J=M.y,O=D.getData("dimArray"),B=D.getData("config"),z=F.x-L,w=B.orientation=="horizontal";if(w){if(F.xL+A||F.y>J+N||F.yL+A||F.y>J||F.y=H){return{name:D.getData("stringArray")[G],color:D.getData("colorArray")[G],value:D.getData("valueArray")[G],label:D.name}}}}return false}},"barchart-grouped":{render:function(S,C){var I=S.pos.getc(true),R=S.getData("width"),P=S.getData("height"),N=this.getAlignedPos(I,R,P),M=N.x,L=N.y,O=S.getData("dimArray"),G=S.getData("valueArray"),Y=G.length,F=S.getData("colorArray"),B=F.length,aa=S.getData("stringArray");var U=C.getCtx(),w={},V=S.getData("border"),z=S.getData("gradient"),ac=S.getData("config"),A=ac.orientation=="horizontal",E=ac.showAggregates,Q=ac.showLabels,K=ac.Label,D=(A?P:R)/Y;if(F&&O&&aa){for(var Z=0,T=Y,X=0,H=0;Z>0}));ab.addColorStop(0,W);ab.addColorStop(0.5,F[Z%B]);ab.addColorStop(1,W);U.fillStyle=ab}if(A){U.fillRect(M,L+D*Z,O[Z],D)}else{U.fillRect(M+D*Z,L-O[Z],D,O[Z])}if(V&&V.name==aa[Z]){w.acum=D*Z;w.dimValue=O[Z]}X+=(O[Z]||0);H+=(G[Z]||0)}if(V){U.save();U.lineWidth=2;U.strokeStyle=V.color;if(A){U.strokeRect(M+1,L+w.acum+1,w.dimValue-2,D-2)}else{U.strokeRect(M+w.acum+1,L-w.dimValue+1,D-2,w.dimValue-2)}U.restore()}if(K.type=="Native"){U.save();U.fillStyle=U.strokeStyle=K.color;U.font=K.style+" "+K.size+"px "+K.family;U.textBaseline="middle";var J=E(S.name,H,S);if(J!==false){J=J!==true?J:H;if(A){U.textAlign="right";U.fillText(J,M+Math.max.apply(null,O)-ac.labelOffset,L+P/2)}else{U.textAlign="center";U.fillText(J,M+R/2,L-Math.max.apply(null,O)-K.size/2-ac.labelOffset)}}if(Q(S.name,H,S)){if(A){U.textAlign="center";U.translate(M-ac.labelOffset-K.size/2,L+P/2);U.rotate(Math.PI/2);U.fillText(S.name,0,0)}else{U.textAlign="center";U.fillText(S.name,M+R/2,L+K.size/2+ac.labelOffset)}}U.restore()}}},contains:function(J,F){var B=J.pos.getc(true),I=J.getData("width"),H=J.getData("height"),E=this.getAlignedPos(B,I,H),D=E.x,C=E.y,G=J.getData("dimArray"),M=G.length,P=J.getData("config"),A=F.x-D,w=P.orientation=="horizontal",z=(w?H:I)/M;if(w){if(F.xD+I||F.y>C+H||F.yD+I||F.y>C||F.y=N&&F.y<=N+z){return{name:J.getData("stringArray")[L],color:J.getData("colorArray")[L],value:J.getData("valueArray")[L],label:J.name}}}else{var N=D+z*L;if(F.x>=N&&F.x<=N+z&&F.y>=C-O){return{name:J.getData("stringArray")[L],color:J.getData("colorArray")[L],value:J.getData("valueArray")[L],label:J.name}}}}return false}}});$jit.BarChart=new q({st:null,colors:["#416D9C","#70A35E","#EBB056","#C74243","#83548B","#909291","#557EAA"],selected:{},busy:false,initialize:function(y){this.controller=this.config=c.merge(n("Canvas","Margin","Label","BarChart"),{Label:{type:"Native"}},y);var z=this.config.showLabels,x=c.type(z),A=this.config.showAggregates,w=c.type(A);this.config.showLabels=x=="function"?z:c.lambda(z);this.config.showAggregates=w=="function"?A:c.lambda(A);this.initializeViz()},initializeViz:function(){var x=this.config,B=this;var w=x.type.split(":")[0],D=x.orientation=="horizontal",A={};var z=new $jit.ST({injectInto:x.injectInto,width:x.width,height:x.height,orientation:D?"left":"bottom",levelDistance:0,siblingOffset:x.barsOffset,subtreeOffset:0,withLabels:x.Label.type!="Native",useCanvas:x.useCanvas,Label:{type:x.Label.type},Node:{overridable:true,type:"barchart-"+w,align:"left",width:1,height:1},Edge:{type:"none"},Tips:{enable:x.Tips.enable,type:"Native",force:true,onShow:function(H,G,E){var F=E;x.Tips.onShow(H,F,G)}},Events:{enable:true,type:"Native",onClick:function(G,H,E){if(!x.Events.enable){return}var F=H.getContains();x.Events.onClick(F,H,E)},onMouseMove:function(G,H,E){if(!x.hoveredColor){return}if(G){var F=H.getContains();B.select(G.id,F.name,F.index)}else{B.select(false,false,false)}}},onCreateLabel:function(J,H){var P=x.Label,N=H.getData("valueArray"),M=c.reduce(N,function(Q,R){return Q+R},0);var L={wrapper:document.createElement("div"),aggregate:document.createElement("div"),label:document.createElement("div")};var E=L.wrapper,O=L.label,F=L.aggregate,G=E.style,K=O.style,I=F.style;A[H.id]=L;E.appendChild(O);E.appendChild(F);if(!x.showLabels(H.name,M,H)){K.display="none"}if(!x.showAggregates(H.name,M,H)){I.display="none"}G.position="relative";G.overflow="visible";G.fontSize=P.size+"px";G.fontFamily=P.family;G.color=P.color;G.textAlign="center";I.position=K.position="absolute";J.style.width=H.getData("width")+"px";J.style.height=H.getData("height")+"px";I.left=K.left="0px";O.innerHTML=H.name;J.appendChild(E)},onPlaceLabel:function(U,P){if(!A[P.id]){return}var T=A[P.id],G=T.wrapper.style,E=T.label.style,O=T.aggregate.style,V=x.type.split(":")[0]=="grouped",F=x.orientation=="horizontal",K=P.getData("dimArray"),I=P.getData("valueArray"),M=(V&&F)?Math.max.apply(null,K):P.getData("width"),L=(V&&!F)?Math.max.apply(null,K):P.getData("height"),J=parseInt(G.fontSize,10),N=U.style;if(K&&I){G.width=O.width=E.width=U.style.width=M+"px";for(var S=0,Q=I.length,R=0;S0){R+=I[S]}}if(x.showLabels(P.name,R,P)){E.display=""}else{E.display="none"}var H=x.showAggregates(P.name,R,P);if(H!==false){O.display=""}else{O.display="none"}if(x.orientation=="horizontal"){O.textAlign="right";E.textAlign="left";E.textIndex=O.textIndent=x.labelOffset+"px";O.top=E.top=(L-J)/2+"px";U.style.height=G.height=L+"px"}else{O.top=(-J-x.labelOffset)+"px";E.top=(x.labelOffset+L)+"px";U.style.top=parseInt(U.style.top,10)-L+"px";U.style.height=G.height=L+"px"}T.aggregate.innerHTML=H!==true?H:R}}});var y=z.canvas.getSize(),C=x.Margin;if(D){z.config.offsetX=y.width/2-C.left-(x.showLabels&&(x.labelOffset+x.Label.size));z.config.offsetY=(C.bottom-C.top)/2}else{z.config.offsetY=-y.height/2+C.bottom+(x.showLabels&&(x.labelOffset+x.Label.size));z.config.offsetX=(C.right-C.left)/2}this.delegate=z;this.canvas=this.delegate.canvas},loadJSON:function(K){if(this.busy){return}this.busy=true;var H=c.time(),C=[],J=this.delegate,N=c.splat(K.label),G=c.splat(K.color||this.colors),L=this.config,w=!!L.type.split(":")[1],z=L.animate,y=L.orientation=="horizontal",A=this;for(var I=0,x=K.values,E=x.length;Iz?x:z});return x},setBarType:function(w){this.config.type=w;this.delegate.config.Node.type="barchart-"+w.split(":")[0]},normalizeDims:function(){var G=this.delegate.graph.getNode(this.delegate.root),B=0;G.eachAdjacency(function(){B++});var D=this.getMaxValue()||1,J=this.delegate.canvas.getSize(),z=this.config,C=z.Margin,H=C.left+C.right,A=C.top+C.bottom,x=z.orientation=="horizontal",w=(J[x?"height":"width"]-(x?A:H)-(B-1)*z.barsOffset)/B,y=z.animate,I=J[x?"width":"height"]-(x?H:A)-(!x&&z.showAggregates&&(z.Label.size+z.labelOffset))-(z.showLabels&&(z.Label.size+z.labelOffset)),F=x?"height":"width",E=x?"width":"height";this.delegate.graph.eachNode(function(N){var M=0,K=[];c.each(N.getData("valueArray"),function(O){M+=+O;K.push(0)});N.setData(F,w);if(y){N.setData(E,M*I/D,"end");N.setData("dimArray",c.map(N.getData("valueArray"),function(O){return O*I/D}),"end");var L=N.getData("dimArray");if(!L){N.setData("dimArray",K)}}else{N.setData(E,M*I/D);N.setData("dimArray",c.map(N.getData("valueArray"),function(O){return O*I/D}))}})}});n.PieChart={$extend:true,animate:true,offset:25,sliceOffset:0,labelOffset:3,type:"stacked",hoveredColor:"#9fd4ff",Events:{enable:false,onClick:c.empty},Tips:{enable:false,onShow:c.empty,onHide:c.empty},showLabels:true,resizeLabels:false,updateHeights:false};g.Radial=new q({compute:function(x){var y=c.splat(x||["current","start","end"]);f.compute(this.graph,y,this.config);this.graph.computeLevels(this.root,0,"ignore");var w=this.createLevelDistanceFunc();this.computeAngularWidths(y);this.computePositions(y,w)},computePositions:function(D,A){var F=D;var E=this.graph;var B=E.getNode(this.root);var C=this.parent;var w=this.config;for(var y=0,x=F.length;yJ[Z]?Y:J[Z]):Y}G.push(W)},"ignore");if(C&&C.id==K.id&&G.length>0&&G[0].dist){G.sort(function(W,V){return(W.dist>=V.dist)-(W.dist<=V.dist)})}for(var M=0,O=G.length;MF/2&&B.theta<3*F/2);var L=I?B.theta+F:B.theta;if(I){M-=Math.abs(Math.cos(B.theta)*A.width);K+=Math.sin(B.theta)*A.width}else{if(E.id==this.viz.root){M-=A.width/2}}}O.save();O.translate(M,K);O.rotate(L);O.fillText(E.name,0,0);O.restore()}});w.Label.SVG=new q({Implements:e.Label.SVG,initialize:function(x){this.viz=x},placeLabel:function(N,C,E){var J=C.pos.getc(true),M=this.viz,A=this.viz.canvas;var F=A.getSize();var B={x:Math.round(J.x+F.width/2),y:Math.round(J.y+F.height/2)};N.setAttribute("x",B.x);N.setAttribute("y",B.y);var G=N.getBBox();if(G){var L=N.getAttribute("x");var I=N.getAttribute("y");var z=C.pos.getp(true);var D=Math.PI;var H=(z.theta>D/2&&z.theta<3*D/2);if(H){N.setAttribute("x",L-G.width);N.setAttribute("y",I-G.height)}else{if(C.id==M.root){N.setAttribute("x",L-G.width/2)}}var K=H?z.theta+D:z.theta;if(C._depth){N.setAttribute("transform","rotate("+K*360/(2*D)+" "+L+" "+I+")")}}E.onPlaceLabel(N,C)}});w.Label.HTML=new q({Implements:e.Label.HTML,initialize:function(x){this.viz=x},placeLabel:function(G,A,C){var E=A.pos.clone(),y=this.viz.canvas,F=A.getData("height"),B=((F||A._depth==0)?F:this.viz.config.levelDistance)/2,D=y.getSize();E.rho+=B;E=E.getc(true);var z={x:Math.round(E.x+D.width/2),y:Math.round(E.y+D.height/2)};var x=G.style;x.left=z.x+"px";x.top=z.y+"px";x.display=this.fitsInCanvas(z,y)?"":"none";C.onPlaceLabel(G,A)}});w.Plot.NodeTypes=new q({none:{render:c.empty,contains:c.lambda(false),anglecontains:function(B,D){var A=B.getData("span")/2,y=B.pos.theta;var z=y-A,x=y+A;if(z<0){z+=Math.PI*2}var C=Math.atan2(D.y,D.x);if(C<0){C+=Math.PI*2}if(z>x){return(C>z&&C<=Math.PI*2)||Cz&&C=z*C)&&(y<=(z*C+B))}return false}},"gradient-multipie":{render:function(A,x){var F=x.getCtx();var E=A.getData("height");var B=E?E:this.config.levelDistance;var y=F.createRadialGradient(0,0,A.getPos().rho,0,0,A.getPos().rho+B);var D=c.hexToRgb(A.getData("color")),C=[];c.each(D,function(G){C.push(parseInt(G*0.5,10))});var z=c.rgbToHex(C);y.addColorStop(0,z);y.addColorStop(1,A.getData("color"));F.fillStyle=y;this.nodeTypes.multipie.render.call(this,A,x)},contains:function(x,y){return this.nodeTypes.multipie.contains.call(this,x,y)}},"gradient-pie":{render:function(C,z){var x=z.getCtx();var D=x.createRadialGradient(0,0,0,0,0,C.getPos().rho);var B=c.hexToRgb(C.getData("color")),y=[];c.each(B,function(E){y.push(parseInt(E*0.5,10))});var A=c.rgbToHex(y);D.addColorStop(1,A);D.addColorStop(0,C.getData("color"));x.fillStyle=D;this.nodeTypes.pie.render.call(this,C,z)},contains:function(x,y){return this.nodeTypes.pie.contains.call(this,x,y)}}});w.Plot.EdgeTypes=new q({none:c.empty,line:{render:function(x,y){var A=x.nodeFrom.pos.getc(true),z=x.nodeTo.pos.getc(true);this.edgeHelper.line.render(A,z,y)},contains:function(x,A){var z=x.nodeFrom.pos.getc(true),y=x.nodeTo.pos.getc(true);return this.edgeHelper.line.contains(z,y,A,this.edge.epsilon)}},arrow:{render:function(y,z){var D=y.nodeFrom.pos.getc(true),C=y.nodeTo.pos.getc(true),B=y.getData("dim"),A=y.data.$direction,x=(A&&A.length>1&&A[0]!=y.nodeFrom.id);this.edgeHelper.arrow.render(D,C,B,x,z)},contains:function(x,A){var z=x.nodeFrom.pos.getc(true),y=x.nodeTo.pos.getc(true);return this.edgeHelper.arrow.contains(z,y,A,this.edge.epsilon)}},hyperline:{render:function(x,y){var B=x.nodeFrom.pos.getc(),A=x.nodeTo.pos.getc(),z=Math.max(B.norm(),A.norm());this.edgeHelper.hyperline.render(B.$scale(1/z),A.$scale(1/z),z,y)},contains:c.lambda(false)}})})($jit.Sunburst);$jit.Sunburst.Plot.NodeTypes.implement({"piechart-stacked":{render:function(U,A){var T=U.pos.getp(true),C=U.getData("dimArray"),S=U.getData("valueArray"),G=U.getData("colorArray"),z=G.length,M=U.getData("stringArray"),P=U.getData("span")/2,K=U.pos.theta,F=K-P,J=K+P,R=new b;var N=A.getCtx(),L={},I=U.getData("gradient"),D=U.getData("border"),Z=U.getData("config"),ai=Z.showLabels,Y=Z.resizeLabels,ab=Z.Label;var ae=Z.sliceOffset*Math.cos((F+J)/2);var E=Z.sliceOffset*Math.sin((F+J)/2);if(G&&C&&M){for(var af=0,ac=C.length,w=0,X=0;af>0}),y=c.rgbToHex(W);ad.addColorStop(0,ag);ad.addColorStop(0.5,ag);ad.addColorStop(1,y);N.fillStyle=ad}R.rho=w+Z.sliceOffset;R.theta=F;var ah=R.getc(true);R.theta=J;var O=R.getc(true);R.rho+=B;var aj=R.getc(true);R.theta=F;var Q=R.getc(true);N.beginPath();N.arc(ae,E,w+0.01,F,J,false);N.arc(ae,E,w+B+0.01,J,F,true);N.fill();if(D&&D.name==M[af]){L.acum=w;L.dimValue=C[af];L.begin=F;L.end=J}w+=(B||0);X+=(S[af]||0)}if(D){N.save();N.globalCompositeOperation="source-over";N.lineWidth=2;N.strokeStyle=D.color;var aa=F>0;H=H<+Y?+Y:H;N.font=ab.style+" "+H+"px "+ab.family;N.textBaseline="middle";N.textAlign="center";R.rho=w+Z.labelOffset+Z.sliceOffset;R.theta=U.pos.theta;var ak=R.getc(true);N.fillText(U.name,ak.x,ak.y);N.restore()}}},contains:function(z,D){if(this.nodeTypes.none.anglecontains.call(this,z,D)){var F=Math.sqrt(D.x*D.x+D.y*D.y);var w=this.config.levelDistance,C=z._depth;var x=z.getData("config");if(F<=w*C+x.sliceOffset){var G=z.getData("dimArray");for(var B=0,A=G.length,E=x.sliceOffset;B=E&&F<=E+y){return{name:z.getData("stringArray")[B],color:z.getData("colorArray")[B],value:z.getData("valueArray")[B],label:z.name}}E+=y}}return false}return false}}});$jit.PieChart=new q({sb:null,colors:["#416D9C","#70A35E","#EBB056","#C74243","#83548B","#909291","#557EAA"],selected:{},busy:false,initialize:function(w){this.controller=this.config=c.merge(n("Canvas","PieChart","Label"),{Label:{type:"Native"}},w);this.initializeViz()},initializeViz:function(){var x=this.config,B=this;var w=x.type.split(":")[0];var A=new $jit.Sunburst({injectInto:x.injectInto,width:x.width,height:x.height,useCanvas:x.useCanvas,withLabels:x.Label.type!="Native",Label:{type:x.Label.type},Node:{overridable:true,type:"piechart-"+w,width:1,height:1},Edge:{type:"none"},Tips:{enable:x.Tips.enable,type:"Native",force:true,onShow:function(F,E,C){var D=C;x.Tips.onShow(F,D,E)}},Events:{enable:true,type:"Native",onClick:function(E,F,C){if(!x.Events.enable){return}var D=F.getContains();x.Events.onClick(D,F,C)},onMouseMove:function(E,F,C){if(!x.hoveredColor){return}if(E){var D=F.getContains();B.select(E.id,D.name,D.index)}else{B.select(false,false,false)}}},onCreateLabel:function(F,E){var C=x.Label;if(x.showLabels){var D=F.style;D.fontSize=C.size+"px";D.fontFamily=C.family;D.color=C.color;D.textAlign="center";F.innerHTML=E.name}},onPlaceLabel:function(S,M){if(!x.showLabels){return}var G=M.pos.getp(true),J=M.getData("dimArray"),P=M.getData("span")/2,H=M.pos.theta,R=H-P,D=H+P,U=new b;var L=x.showLabels,F=x.resizeLabels,I=x.Label;if(J){for(var Q=0,N=J.length,O=0;Q>0;C=C<+F?+F:C;S.style.fontSize=C+"px";U.rho=O+x.labelOffset+x.sliceOffset;U.theta=(R+D)/2;var G=U.getc(true);var E=B.canvas.getSize();var K={x:Math.round(G.x+E.width/2),y:Math.round(G.y+E.height/2)};S.style.left=K.x+"px";S.style.top=K.y+"px"}}});var z=A.canvas.getSize(),y=Math.min;A.config.levelDistance=y(z.width,z.height)/2-x.offset-x.sliceOffset;this.delegate=A;this.canvas=this.delegate.canvas;this.canvas.getCtx().globalCompositeOperation="lighter"},loadJSON:function(K){var H=c.time(),B=[],J=this.delegate,N=c.splat(K.label),D=N.length,G=c.splat(K.color||this.colors),y=G.length,L=this.config,w=!!L.type.split(":")[1],z=L.animate,F=D==1;for(var I=0,x=K.values,E=x.length;Iy?w:y});return w},normalizeDims:function(){var x=this.delegate.graph.getNode(this.delegate.root),w=0;x.eachAdjacency(function(){w++});var B=this.getMaxValue()||1,A=this.config,y=A.animate,z=this.delegate.config.levelDistance;this.delegate.graph.eachNode(function(G){var F=0,C=[];c.each(G.getData("valueArray"),function(H){F+=+H;C.push(1)});var E=(C.length==1)&&!A.updateHeights;if(y){G.setData("dimArray",c.map(G.getData("valueArray"),function(H){return E?z:(H*z/B)}),"end");var D=G.getData("dimArray");if(!D){G.setData("dimArray",C)}}else{G.setData("dimArray",c.map(G.getData("valueArray"),function(H){return E?z:(H*z/B)}))}G.setData("normalizedDim",F/B)})}});g.TM={};g.TM.SliceAndDice=new q({compute:function(B){var x=this.graph.getNode(this.clickedNode&&this.clickedNode.id||this.root);this.controller.onBeforeCompute(x);var z=this.canvas.getSize(),y=this.config,A=z.width,w=z.height;this.graph.computeLevels(this.root,0,"ignore");x.getPos(B).setc(-A/2,-w/2);x.setData("width",A,B);x.setData("height",w+y.titleHeight,B);this.computePositions(x,x,this.layout.orientation,B);this.controller.onAfterCompute(x)},computePositions:function(F,D,P,y){var M=0;F.eachSubnode(function(R){M+=R.getData("area",y)});var Q=this.config,N=Q.offset,J=F.getData("width",y),H=Math.max(F.getData("height",y)-Q.titleHeight,0),x=F==D?1:(D.getData("area",y)/M);var I,G,L,B,A,E,C;var O=(P=="h");if(O){P="v";I=H;G=J*x;L="height";B="y";A="x";E=Q.titleHeight;C=0}else{P="h";I=H*x;G=J;L="width";B="x";A="y";E=0;C=Q.titleHeight}var w=D.getPos(y);D.setData("width",G,y);D.setData("height",I,y);var K=0,z=this;D.eachSubnode(function(S){var R=S.getPos(y);R[B]=K+w[B]+E;R[A]=w[A]+C;z.computePositions(D,S,P,y);K+=S.getData(L,y)})}});g.TM.Area={compute:function(w){w=w||"current";var C=this.graph.getNode(this.clickedNode&&this.clickedNode.id||this.root);this.controller.onBeforeCompute(C);var y=this.config,F=this.canvas.getSize(),x=F.width,E=F.height,D=y.offset,z=x-D,B=E-D;this.graph.computeLevels(this.root,0,"ignore");C.getPos(w).setc(-x/2,-E/2);C.setData("width",x,w);C.setData("height",E,w);var A={top:-E/2+y.titleHeight,left:-x/2,width:z,height:B-y.titleHeight};this.computePositions(C,A,w);this.controller.onAfterCompute(C)},computeDim:function(B,C,E,A,z,x){if(B.length+C.length==1){var y=(B.length==1)?B:C;this.layoutLast(y,E,A,x);return}if(B.length>=2&&C.length==0){C=[B.shift()]}if(B.length==0){if(C.length>0){this.layoutRow(C,E,A,x)}return}var D=B[0];if(z(C,E)>=z([D].concat(C),E)){this.computeDim(B.slice(1),C.concat([D]),E,A,z,x)}else{var F=this.layoutRow(C,E,A,x);this.computeDim(B,[],F.dim,F,z,x)}},worstAspectRatio:function(x,F){if(!x||x.length==0){return Number.MAX_VALUE}var y=0,G=0,B=Number.MAX_VALUE;for(var D=0,C=x.length;Dz?G:z}var E=F*F,A=y*y;return Math.max(E*G/A,A/(E*B))},avgAspectRatio:function(B,y){if(!B||B.length==0){return Number.MAX_VALUE}var D=0;for(var z=0,x=B.length;zA?y/A:A/y}return D/x},layoutLast:function(y,x,B,A){var z=y[0];z.getPos(A).setc(B.left,B.top);z.setData("width",B.width,A);z.setData("height",B.height,A)}};g.TM.Squarified=new q({Implements:g.TM.Area,computePositions:function(A,D,x){var z=this.config,F=Math.max;if(D.width>=D.height){this.layout.orientation="h"}else{this.layout.orientation="v"}var w=A.getSubnodes([1,1],"ignore");if(w.length>0){this.processChildrenLayout(A,w,D,x);for(var C=0,B=w.length;C0){this.processChildrenLayout(A,w,D,x);for(var C=0,B=w.length;CF){F=I}});var y=this.graph.getNode(this.clickedNode&&this.clickedNode.id||D.id);var x=Math.min(F,C-1);var B=y._depth;if(this.layout.horizontal()){this.computeSubtree(y,-w/2,-G/2,w/(x+1),G,B,x,E)}else{this.computeSubtree(y,-w/2,-G/2,w,G/(x+1),B,x,E)}},computeSubtree:function(G,I,F,w,L,E,A,H){G.getPos(H).setc(I,F);G.setData("width",w,H);G.setData("height",L,H);var C,K=0,J=0;var z=e.Util.getSubnodes(G,[1,1],"ignore");if(!z.length){return}c.each(z,function(x){J+=x.getData("dim")});for(var D=0,B=z.length;D>0}));I.addColorStop(0,A);I.addColorStop(1,E);J.fillStyle=I}if(B){J.strokeStyle=B;J.lineWidth=3}J.fillRect(F,D,Math.max(0,w-C),Math.max(0,H-C));B&&J.strokeRect(G.x,G.y,w,H)},contains:function(y,A){if(this.viz.clickedNode&&!$jit.Graph.Util.isDescendantOf(y,this.viz.clickedNode.id)){return false}var z=y.pos.getc(true),x=y.getData("width"),w=y.getData("height");return this.nodeHelper.rectangle.contains({x:z.x+x/2,y:z.y+w/2},A,x,w)}}});$jit.Icicle.Plot.EdgeTypes=new q({none:c.empty});g.ForceDirected=new q({getOptions:function(D){var B=this.canvas.getSize();var y=B.width,A=B.height;var C=0;this.graph.eachNode(function(w){C++});var E=y*A/C,z=Math.sqrt(E);var x=this.config.levelDistance;return{width:y,height:A,tstart:y*0.1,nodef:function(w){return E/(w||1)},edgef:function(w){return z*(w-x)}}},compute:function(x,y){var z=c.splat(x||["current","start","end"]);var w=this.getOptions();f.compute(this.graph,z,this.config);this.graph.computeLevels(this.root,0,"ignore");this.graph.eachNode(function(A){c.each(z,function(B){var C=A.getPos(B);if(C.equals(p.KER)){C.x=w.width/5*(Math.random()-0.5);C.y=w.height/5*(Math.random()-0.5)}A.disp={};c.each(z,function(D){A.disp[D]=r(0,0)})})});this.computePositions(z,w,y)},computePositions:function(A,y,B){var C=this.config.iterations,x=0,z=this;if(B){(function w(){for(var E=B.iter,D=0;D=C){B.onComplete();return}}B.onStep(Math.round(x/(C-1)*100));setTimeout(w,1)})()}else{for(;x1&&A[0]!=y.nodeFrom.id);this.edgeHelper.arrow.render(D,C,B,x,z)},contains:function(x,A){var z=x.nodeFrom.pos.getc(true),y=x.nodeTo.pos.getc(true);return this.edgeHelper.arrow.contains(z,y,A,this.edge.epsilon)}}})})($jit.ForceDirected);$jit.TM={};var v=$jit.TM;$jit.TM.$extend=true;v.Base={layout:{orientation:"h",vertical:function(){return this.orientation=="v"},horizontal:function(){return this.orientation=="h"},change:function(){this.orientation=this.vertical()?"h":"v"}},initialize:function(w){var x={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=c.merge(n("Canvas","Node","Edge","Fx","Controller","Tips","NodeStyles","Events","Navigation","Label"),x,w);this.layout.orientation=this.config.orientation;var y=this.config;if(y.useCanvas){this.canvas=y.useCanvas;this.config.labelContainer=this.canvas.id+"-label"}else{if(y.background){y.background=c.merge({type:"Circles"},y.background)}this.canvas=new l(this,y);this.config.labelContainer=(typeof y.injectInto=="string"?y.injectInto:y.injectInto.id)+"-label"}this.graphOptions={klass:p,Node:{selected:false,exist:true,drawn:true}};this.graph=new e(this.graphOptions,this.config.Node,this.config.Edge);this.labels=new v.Label[y.Label.type](this);this.fx=new v.Plot(this);this.op=new v.Op(this);this.group=new v.Group(this);this.geom=new v.Geom(this);this.clickedNode=null;this.busy=false;this.initializeExtras()},refresh:function(){if(this.busy){return}this.busy=true;var x=this;if(this.config.animate){this.compute("end");this.config.levelsToShow>0&&this.geom.setRightLevelToShow(this.graph.getNode(this.clickedNode&&this.clickedNode.id||this.root));this.fx.animate(c.merge(this.config,{modes:["linear","node-property:width:height"],onComplete:function(){x.busy=false}}))}else{var w=this.config.Label.type;if(w!="Native"){var x=this;this.graph.eachNode(function(y){x.labels.hideLabel(y,false)})}this.busy=false;this.compute();this.config.levelsToShow>0&&this.geom.setRightLevelToShow(this.graph.getNode(this.clickedNode&&this.clickedNode.id||this.root));this.plot()}},plot:function(){this.fx.plot()},leaf:function(w){return w.getSubnodes([1,1],"ignore").length==0},enter:function(C){if(this.busy){return}this.busy=true;var y=this,x=this.config,A=this.graph,w=C,z=this.clickedNode;var B={onComplete:function(){if(x.levelsToShow>0){y.geom.setRightLevelToShow(C)}if(x.levelsToShow>0||x.request){y.compute()}if(x.animate){A.nodeList.setData("alpha",0,"end");C.eachSubgraph(function(D){D.setData("alpha",1,"end")},"ignore");y.fx.animate({duration:500,modes:["node-property:alpha"],onComplete:function(){y.clickedNode=w;y.compute("end");y.clickedNode=z;y.fx.animate({modes:["linear","node-property:width:height"],duration:1000,onComplete:function(){y.busy=false;y.clickedNode=w}})}})}else{y.busy=false;y.clickedNode=C;y.refresh()}}};if(x.request){this.requestNodes(w,B)}else{B.onComplete()}},out:function(){if(this.busy){return}this.busy=true;this.events.hoveredNode=false;var A=this,y=this.config,C=this.graph,x=C.getNode(this.clickedNode&&this.clickedNode.id||this.root).getParents(),z=x[0],w=z,B=this.clickedNode;if(!z){this.busy=false;return}callback={onComplete:function(){A.clickedNode=z;if(y.request){A.requestNodes(z,{onComplete:function(){A.compute();A.plot();A.busy=false}})}else{A.compute();A.plot();A.busy=false}}};if(y.levelsToShow>0){this.geom.setRightLevelToShow(z)}if(y.animate){this.clickedNode=w;this.compute("end");this.clickedNode=B;this.fx.animate({modes:["linear","node-property:width:height"],duration:1000,onComplete:function(){A.clickedNode=w;C.eachNode(function(D){D.setDataset(["current","end"],{alpha:[0,1]})},"ignore");B.eachSubgraph(function(D){D.setData("alpha",1)},"ignore");A.fx.animate({duration:500,modes:["node-property:alpha"],onComplete:function(){callback.onComplete()}})}})}else{callback.onComplete()}},requestNodes:function(y,z){var x=c.merge(this.controller,z),w=this.config.levelsToShow;if(x.request){var B=[],A=y._depth;y.eachLevel(0,w,function(D){var C=w-(D._depth-A);if(D.drawn&&!D.anySubnode()&&C>0){B.push(D);D._level=C}});this.group.requestNodes(B,x)}else{x.onComplete()}},reposition:function(){this.compute("end")}};v.Op=new q({Implements:e.Op,initialize:function(w){this.viz=w}});v.Geom=new q({Implements:e.Geom,getRightLevelToShow:function(){return this.viz.config.levelsToShow},setRightLevelToShow:function(x){var y=this.getRightLevelToShow(),w=this.viz.labels;x.eachLevel(0,y+1,function(A){var z=A._depth-x._depth;if(z>y){A.drawn=false;A.exist=false;A.ignore=true;w.hideLabel(A,false)}else{A.drawn=true;A.exist=true;delete A.ignore}});x.drawn=true;delete x.ignore}});v.Group=new q({initialize:function(w){this.viz=w;this.canvas=w.canvas;this.config=w.config},requestNodes:function(B,A){var z=0,x=B.length,D={};var y=function(){A.onComplete()};var w=this.viz;if(x==0){y()}for(var C=0;C>0}));K.addColorStop(0,A);K.addColorStop(1,F);L.fillStyle=K}L.fillRect(G,E,w-I,J-I);if(B){L.save();L.strokeStyle=B;L.strokeRect(G,E,w-I,J-I);L.restore()}}else{if(C>0){L.fillRect(H.x+I/2,H.y+I/2,w-I,C-I);if(B){L.save();L.strokeStyle=B;L.strokeRect(H.x+I/2,H.y+I/2,w-I,J-I);L.restore()}}}},contains:function(z,B){if(this.viz.clickedNode&&!z.isDescendantOf(this.viz.clickedNode.id)||z.ignore){return false}var A=z.pos.getc(true),y=z.getData("width"),x=this.viz.leaf(z),w=x?z.getData("height"):this.config.titleHeight;return this.nodeHelper.rectangle.contains({x:A.x+y/2,y:A.y+w/2},B,y,w)}}});v.Plot.EdgeTypes=new q({none:c.empty});v.SliceAndDice=new q({Implements:[d,o,v.Base,g.TM.SliceAndDice]});v.Squarified=new q({Implements:[d,o,v.Base,g.TM.Squarified]});v.Strip=new q({Implements:[d,o,v.Base,g.TM.Strip]});$jit.RGraph=new q({Implements:[d,o,g.Radial],initialize:function(w){var x=$jit.RGraph;var y={interpolation:"linear",levelDistance:100};this.controller=this.config=c.merge(n("Canvas","Node","Edge","Fx","Controller","Tips","NodeStyles","Events","Navigation","Label"),y,w);var z=this.config;if(z.useCanvas){this.canvas=z.useCanvas;this.config.labelContainer=this.canvas.id+"-label"}else{if(z.background){z.background=c.merge({type:"Circles"},z.background)}this.canvas=new l(this,z);this.config.labelContainer=(typeof z.injectInto=="string"?z.injectInto:z.injectInto.id)+"-label"}this.graphOptions={klass:b,Node:{selected:false,exist:true,drawn:true}};this.graph=new e(this.graphOptions,this.config.Node,this.config.Edge);this.labels=new x.Label[z.Label.type](this);this.fx=new x.Plot(this,x);this.op=new x.Op(this);this.json=null;this.root=null;this.busy=false;this.parent=false;this.initializeExtras()},createLevelDistanceFunc:function(){var w=this.config.levelDistance;return function(x){return(x._depth+1)*w}},refresh:function(){this.compute();this.plot()},reposition:function(){this.compute("end")},plot:function(){this.fx.plot()},getNodeAndParentAngle:function(D){var y=false;var C=this.graph.getNode(D);var A=C.getParents();var z=(A.length>0)?A[0]:false;if(z){var w=z.pos.getc(),B=C.pos.getc();var x=w.add(B.scale(-1));y=Math.atan2(x.y,x.x);if(y<0){y+=2*Math.PI}}return{parent:z,theta:y}},tagChildren:function(A,C){if(A.angleSpan){var B=[];A.eachAdjacency(function(D){B.push(D.nodeTo)},"ignore");var w=B.length;for(var z=0;z1&&A[0]!=y.nodeFrom.id);this.edgeHelper.arrow.render(D,C,B,x,z)},contains:function(x,A){var z=x.nodeFrom.pos.getc(true),y=x.nodeTo.pos.getc(true);return this.edgeHelper.arrow.contains(z,y,A,this.edge.epsilon)}}})})($jit.RGraph);p.prototype.moebiusTransformation=function(y){var w=this.add(y);var x=y.$conjugate().$prod(this);x.x++;return w.$div(x)};e.Util.moebiusTransformation=function(y,A,z,x,w){this.eachNode(y,function(C){for(var B=0;B=2){return B(z-0.01)}}return B(0.75)},getRadius:function(){var w=this.config.radius;if(w!=="auto"){return w}var x=this.canvas.getSize();return Math.min(x.width,x.height)/2},refresh:function(w){if(w){this.reposition();this.graph.eachNode(function(x){x.startPos.rho=x.pos.rho=x.endPos.rho;x.startPos.theta=x.pos.theta=x.endPos.theta})}else{this.compute()}this.plot()},reposition:function(){this.compute("end");var w=this.graph.getNode(this.root).pos.getc().scale(-1);e.Util.moebiusTransformation(this.graph,[w],["end"],"end","ignore");this.graph.eachNode(function(x){if(x.ignore){x.endPos.rho=x.pos.rho;x.endPos.theta=x.pos.theta}})},plot:function(){this.fx.plot()},onClick:function(y,w){var x=this.graph.getNode(y).pos.getc(true);this.move(x,w)},move:function(A,y){var x=r(A.x,A.y);if(this.busy===false&&x.norm()<1){this.busy=true;var w=this.graph.getClosestNodeToPos(x),z=this;this.graph.computeLevels(w.id,0);this.controller.onBeforeCompute(w);y=c.merge({onComplete:c.empty},y||{});this.fx.animate(c.merge({modes:["moebius"],hideLabels:true},y,{onComplete:function(){z.busy=false;y.onComplete()}}),x)}}});$jit.Hypertree.$extend=true;(function(w){w.Op=new q({Implements:e.Op});w.Plot=new q({Implements:e.Plot});w.Label={};w.Label.Native=new q({Implements:e.Label.Native,initialize:function(x){this.viz=x},renderLabel:function(z,B,y){var x=z.getCtx();var C=B.pos.getc(true);var A=this.viz.getRadius();x.fillText(B.name,C.x*A,C.y*A)}});w.Label.SVG=new q({Implements:e.Label.SVG,initialize:function(x){this.viz=x},placeLabel:function(I,C,D){var G=C.pos.getc(true),z=this.viz.canvas,A=z.translateOffsetX,y=z.translateOffsetY,H=z.scaleOffsetX,F=z.scaleOffsetY,E=z.getSize(),x=this.viz.getRadius();var B={x:Math.round((G.x*H)*x+A+E.width/2),y:Math.round((G.y*F)*x+y+E.height/2)};I.setAttribute("x",B.x);I.setAttribute("y",B.y);D.onPlaceLabel(I,C)}});w.Label.HTML=new q({Implements:e.Label.HTML,initialize:function(x){this.viz=x},placeLabel:function(J,D,E){var H=D.pos.getc(true),A=this.viz.canvas,B=A.translateOffsetX,z=A.translateOffsetY,I=A.scaleOffsetX,G=A.scaleOffsetY,F=A.getSize(),x=this.viz.getRadius();var C={x:Math.round((H.x*I)*x+B+F.width/2),y:Math.round((H.y*G)*x+z+F.height/2)};var y=J.style;y.left=C.x+"px";y.top=C.y+"px";y.display=this.fitsInCanvas(C,A)?"":"none";E.onPlaceLabel(J,D)}});w.Plot.NodeTypes=new q({none:{render:c.empty,contains:c.lambda(false)},circle:{render:function(z,x){var y=this.node,B=z.getData("dim"),A=z.pos.getc();B=y.transform?B*(1-A.squaredNorm()):B;A.$scale(z.scale);if(B>0.2){this.nodeHelper.circle.render("fill",A,B,x)}},contains:function(x,A){var y=x.getData("dim"),z=x.pos.getc().$scale(x.scale);return this.nodeHelper.circle.contains(z,A,y)}},ellipse:{render:function(A,y){var B=A.pos.getc().$scale(A.scale),z=A.getData("width"),x=A.getData("height");this.nodeHelper.ellipse.render("fill",B,z,x,y)},contains:function(z,B){var y=z.getData("width"),x=z.getData("height"),A=z.pos.getc().$scale(z.scale);return this.nodeHelper.circle.contains(A,B,y,x)}},square:{render:function(z,x){var y=this.node,B=z.getData("dim"),A=z.pos.getc();B=y.transform?B*(1-A.squaredNorm()):B;A.$scale(z.scale);if(B>0.2){this.nodeHelper.square.render("fill",A,B,x)}},contains:function(x,A){var y=x.getData("dim"),z=x.pos.getc().$scale(x.scale);return this.nodeHelper.square.contains(z,A,y)}},rectangle:{render:function(B,y){var A=this.node,z=B.getData("width"),x=B.getData("height"),C=B.pos.getc();z=A.transform?z*(1-C.squaredNorm()):z;x=A.transform?x*(1-C.squaredNorm()):x;C.$scale(B.scale);if(z>0.2&&x>0.2){this.nodeHelper.rectangle.render("fill",C,z,x,y)}},contains:function(z,B){var y=z.getData("width"),x=z.getData("height"),A=z.pos.getc().$scale(z.scale);return this.nodeHelper.rectangle.contains(A,B,y,x)}},triangle:{render:function(z,x){var y=this.node,B=z.getData("dim"),A=z.pos.getc();B=y.transform?B*(1-A.squaredNorm()):B;A.$scale(z.scale);if(B>0.2){this.nodeHelper.triangle.render("fill",A,B,x)}},contains:function(x,A){var y=x.getData("dim"),z=x.pos.getc().$scale(x.scale);return this.nodeHelper.triangle.contains(z,A,y)}},star:{render:function(z,x){var y=this.node,B=z.getData("dim"),A=z.pos.getc();B=y.transform?B*(1-A.squaredNorm()):B;A.$scale(z.scale);if(B>0.2){this.nodeHelper.star.render("fill",A,B,x)}},contains:function(x,A){var y=x.getData("dim"),z=x.pos.getc().$scale(x.scale);return this.nodeHelper.star.contains(z,A,y)}}});w.Plot.EdgeTypes=new q({none:c.empty,line:{render:function(x,y){var B=x.nodeFrom.pos.getc(true),A=x.nodeTo.pos.getc(true),z=x.nodeFrom.scale;this.edgeHelper.line.render({x:B.x*z,y:B.y*z},{x:A.x*z,y:A.y*z},y)},contains:function(x,B){var A=x.nodeFrom.pos.getc(true),z=x.nodeTo.pos.getc(true),y=x.nodeFrom.scale;this.edgeHelper.line.contains({x:A.x*y,y:A.y*y},{x:z.x*y,y:z.y*y},B,this.edge.epsilon)}},arrow:{render:function(y,z){var E=y.nodeFrom.pos.getc(true),D=y.nodeTo.pos.getc(true),A=y.nodeFrom.scale,C=y.getData("dim"),B=y.data.$direction,x=(B&&B.length>1&&B[0]!=y.nodeFrom.id);this.edgeHelper.arrow.render({x:E.x*A,y:E.y*A},{x:D.x*A,y:D.y*A},C,x,z)},contains:function(x,B){var A=x.nodeFrom.pos.getc(true),z=x.nodeTo.pos.getc(true),y=x.nodeFrom.scale;this.edgeHelper.arrow.contains({x:A.x*y,y:A.y*y},{x:z.x*y,y:z.y*y},B,this.edge.epsilon)}},hyperline:{render:function(x,y){var B=x.nodeFrom.pos.getc(),A=x.nodeTo.pos.getc(),z=this.viz.getRadius();this.edgeHelper.hyperline.render(B,A,z,y)},contains:c.lambda(false)}})})($jit.Hypertree)})(); -------------------------------------------------------------------------------- /json_representation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Morphage/BinaryTreeVisualizer/c1dcc64f4c63c1c1c040d13d71bf60a9364e22ea/json_representation.png --------------------------------------------------------------------------------