Mercurial > repos > iuc > taxonomy_krona_chart
comparison test-data/krona_test1.html @ 0:0728a518491b draft default tip
planemo upload for repository https://github.com/galaxyproject/tools-iuc/tree/master/tools/taxonomy_krona_chart commit 3c29d3c8710ca23b4c59c2cf8501697331a65ac0
| author | iuc |
|---|---|
| date | Thu, 15 Oct 2015 15:46:25 -0400 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:0728a518491b |
|---|---|
| 1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | |
| 2 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> | |
| 3 <head> | |
| 4 <meta charset="utf-8"/> | |
| 5 <link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAMAEBAAAAEAIABoBAAANgAAABgYAAABACAAiAkAAJ4EAAAgIAAAAQAgAKgQAAAmDgAAKAAAABAAAAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wCAgIAC////AP///wC0tKJHlZWSqI6OmuRxcXn9koaK9J2Ym8uNm5V73PPoFv///wAAAAAD////AP///wCAgIAC////AP//7xCHh4OodHSi/2xqw/9nZ9H/XVuU/9eFqv/njb7/yYiq/5GAiOifqKJV////AAAAAAL///8A////AO3t2w56eoHPdHLH/3Vz3vx4dtz7dXTb/mVimv/XhKn/8ZPG/fSVx/vvlcT/kG+E/32Himb///8AAAAAA////wB0dHageHbO/3585fl5d9r/dnTX/3V02/5mYpz80X2i/eaIuv/ukcD/wXak/I6Maf+OkXD9iIKjL////wCEhFo+c3Kv/4F+6P18etz/e3nd/3l33P57eeT/aWag/9F5oP/sib3+tmuZ/3uEXf/P1YX7zdKG/3x9dbX///8Ac3J9nYKA4f+CgOb7fnzf/3174P58et//bm2v8Vtgc7SdbIHNqWmM/4aLZP/L0YT/0dWH/tfdiP+go3P6YlyWJ3BvmduGg+n/g4Hk/oF+4/6AfuX/bm2k7pCUUUX///8Ajv/jCVBmXqPBxoH/1NmI/c7Uhf/b4Yn9wMWB/2BdcGBraqX6iYbs/4SB5P+Eguj7hYPm/2Vlcov///8AACRtB////wAAAIAWrLB29uHojv/X3on+3eSK+9PYh/9XWVN7bWum+ouI7/+Gg+f/hoTq+4iF6f9lZXKL////ACQAbQf///8AAABoFoF+Xva0snL/y818/uDmiPvZ34n/WVlTe3Jxm9uMiu//iofr/oiG6f6Jhu3/cnGo7oyQQ0X///8A/znjCXF8dKNotpL/Za2O/WuVe/+FkGv9mJ5w/2pqaGBzc3+djYrr/42K8fuKh+v/i4js/oqH6/93dbfxYmJztGaTec1yxJr/gOGu/4nptf+K6Lf+i+q8/2eXhfqDNG8ngIBKPnt6uP+Rjvf9jInr/4yJ7f+Miu3+jor0/25xoP920pz/gOGw/oHerv+H4rL/kPC9+5Ttvf98ioC1////AP///wB0dHSgioff/5SR+/mOi+7/jYvt/4+L8P5scJ78etKe/YXis/+H47P/kO+8/JLxv/92m4f9nXKNL////wD///8A7e3bDnt7hM+Ihdv/ko/4/JSS+PuUkPb+bnKg/4Lapv+Q7739kfO/+43puf9/qJH/mYeUZv///wAAAAADgICAAv///wD//+8QhoaBqIB/sf+Iht7/jYjw/2tvnf+D3Kf/ieS1/4bIpP+AkYfoqJOfVf///wAAAAAC////AP///wCAgIAC////AP///wCwsJdHlZWQqJORn+RzdHv9hZOK9Jecmcuej5l7/+jzFv///wAAAAAD////AP///wD4PwAA4A8AAMAHAACAAwAAgAEAAAABAAADgQAAA8EAAAPBAAADgQAAAAEAAIABAACAAwAAwAcAAOAPAAD4PwAAKAAAABgAAAAwAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wEAAAAB////AP///wi6urdDubmxj6ennsuQkIzvc3Nz/YSIh/WWoZzWrrezoLe6t1X///8T////AP///wCAgIAC////AP///wD///8A////AP///wD///8A////Af///wD///8Ax8fDQJeYkLV4eH39WlmM/19dpP9dX6b/WFRr/611j/+/fJ//om2I/4N3ff+Jko7Ntri2Xv///wD///8AgICAAv///wD///8A////AP///wD///8B////AP///wOfn5t9c3N29GRjn/9oZ8P/c3LU/XVy3P9rbdH/YVt8/9eIrv/5mcz/75bE/t+Mt/67epz/fnN5/4uTkKP///8W////AAAAAAL///8A////AP///wH///8A////A5iYkZJoaH3/bWvB/3h23Px4dt3+dHPX/3Rx1v9sbcv/YFp6/82ApP/rj8D/65DA//KVxv/1mcn83424/5t1iP98gX7A////F////wCAgIAC////AAAAAAL///8AkJCHdWZlf/9zcs7/fHrj/Hh32/92ddn/dXTX/3Zz2P9vcM//YFt7/8t9of/pi73/54u7/+eNvf/vk8P/7ZbE/pxsh/xLVU7/iomJqP///wD///8A////Af///wCfn5Y4ZmV07XZ0zv9+fOX8enjc/3p43P94dtv/dnXZ/3d12f9xcdD/YFp6/8h5nf/mh7r/44e3/+qMvf/ljrv/jV59/3F+V/68wH3/gYNp/4uIlWf///8AAAAAAv///wJ3d22pc3G6/4KA5/t9e9//fHre/3t53f96eNz/eHbZ/3l23P50dNb9Ylx+/ch2m/3jg7b+44W2/+CItv+NXXz/bnpW/8XIgv/Y3Yv8ur57/3Fxbtni4uIa////AHl5XTdoZ4b4f3zb/4F+5P5+fN//fXvf/3x63v96edz/ennf/nt54P9xccb/Xlxy/7Rvj//gf7P/3IKw/olZeP9te1b/xsqD/9DVh//P1IX/09iG/pWYcf9nZ3Nt////AISEb35ycLb/hYPr/YB+4f9/feH/fnzh/3173/99e+L+enjX/2dmmfhlZXiwYGBiknJkaqGVZ37pg11x/3eBXP7GyoP/0NWH/87Uhf/R1ob/2eCK+8HFff9tbXWv////BG9vdb16eMn/hYPq/4F/4/+BfuP/gH7i/4B94v59e93/Y2KO+ISEbX7y8uQT////AP///weAjo5aT1tP5L7Cf//T2Ij+ztOF/9HWhv/S14b/2d+K/s3Sgv+BgnLcg4OSI11deeaBf9r/hYPo/4OB5P+DgeX/gX/i/4OB6v10c77/cXFjmv///wL///8A////AP///wD///8AeHN9apuecP/U2Yf/0teG/tPZhv/V24f/2uCJ/tTahv+KjGj6AABINV9egfuGg+P/hoTp/4WC5v+EgeX/goDk/4WD6/1wbq//WFgAQ////wAzMzMFAAAAAVVVVQP///8AAABjEoiLZ/Df5Y7/2d+J/dfch//W3If/2d+H/9vhif6ZnW3/AAA5P19egvuHheX/iIXq/4eE6P+Gg+f/hILl/4eE7P1xb7D/WFgAQ////wAzMzMFAAAAAVVVVQP///8AAABVEnd1WvDEx37/y9GD/dnfif/g54z/4OeL/97kiv6anWz/AAA1P15eeuaFg93/ioft/4iF6f+Hhen/hoPn/4mG7/14d8L/cXFimv///wP///8A////AP///wD///8AenV3a1dyZf9QY1//bGVW/pORY/+trnD/xch7/tTZhP+Pkmn6AABINXBwdb1/fc7/jYry/4mH6v+Jhur/iIbp/4iG6/6Gg+X/Z2aT+IKEaX7y8uQT////AP///wiXgY9bXndp5HXLnv95z6P+dsif/2Wrjv9RcWv/Z3Rk/nyBYv9xcWbbfHyDI4SEa354d73/kI31/YuI6/+LiOz/iofr/4mG6v+Lh+3+hoPi/21soPhnZ3qwYGBikmNza6JhinXpcsWa/3vdq/6C4a//iOi1/4zpt/+O6Lf/kOu8+3O1l/9jVmKv////BHl5WDdraor4iofn/4+M8v6Miez/i4nt/4uI7P+LiOv/i4jt/ouJ8P9+e83/XGBt/223jv952aj/e9uq/n3aqv+D36//huKy/4rntv+Q7r3/lvTC/nupj/9zYmxt////AP///wJ3d2qpfXvF/5KP9vyNiu7/jYru/4yJ7f+Miu7/i4jr/46L8P6FgN79XmZ5/XTMm/1+3q7+f9ys/4Lgr/+G5LP/iue2/4zot/+V9cL8iNWs/3BxcNni4uIa////AP///wCfn5I4aGd17YeE3v+Ukfj8jovu/46L7/+Oi+//jYru/46M8f+FgNv/XmV3/3jMnf+D4rH/g+Cw/4fks/+K57b/jem4/5TywP6Q57n/bYh5/5KIjWf///8AAAAAAgAAAAL///8AkJCFdWtqhf+IheL/lZL5/JCN8P+PjO//j4zv/5GO8/+Hgt3/X2Z4/33Rof+I5rb/h+Sz/4nmtf+N6rn/lPPB/pHruvxxm4T/h3yDqP///wD///8A////Af///wH///8A////A5aWj5JtbIP/g4DW/5SR9vyVkvj+kY7y/5KP9P+Ig97/X2d4/4DUpf+N6rn/jeq4/5Lwvv+U88D8h9qu/3KVgv+Lgoe/////F////wCAgIAC////AP///wD///8B////AP///wOfn5l9dHR39HVzsf+Fg97/k4/x/ZiW/v+Nh+f/YWl7/4ngrv+W+MX/kuy8/onesP52uZb/dH54/5aOk6P///8W////AAAAAAL///8A////AP///wD///8A////Af///wD///8Ax8fDQJWVjbV5eX79amic/3h2vv93crf/WV9r/3W1kP95vZn/aZ2C/3eCfP+Ui5DNu7i7Xv///wD///8AgICAAv///wD///8A////AP///wD///8A////AP///wEAAAAB////AP///wi6urdDuLitj6WmmcuPj4rvc3Nz/YiEh/Whlp3Wt66zoLq3ulX///8T////AP///wD///8C////AP///wD///8A////AP8B/wD8AH8A+AAfAOAADwDgAAcAwAAHAIAAAwCAAAMAgAABAAB8AQAAfgEAAP4BAAD+AQAAfgEAAHwBAIAAAQCAAAMAgAADAMAABwDgAAcA4AAPAPgAHwD8AH8A/wH/ACgAAAAgAAAAQAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////KO3t7XXFxcWwqqqq25eXl/RwcHD9mJiY9Kurq9vHx8ew8PDwdf///yj///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AAAAAAP///wD///8A////AP///wD///8A////AP///wD///8A9/f3TLS0tLpvb3H/UVFR/1VVWf9bW27/WVlz/1FRUf96ZG//c2Nr/1lWV/9RUVH/d3V2/7m5ubr5+flM////AP///wD///8A////AP///wD///8A////AP///wAAAAAA////AP///wD///8A////AP///wD///8A/f39HLW1tatbW13/V1de/2FgkP9rabv/cG7S/29u0/9paL7/UVFR/9GIrv/wlcX/75bE/8+Irf+ZcYb/XVhb/2FfYP+8vLyr/v7+HP///wD///8A////AP///wD///8A////AAAAAAD///8A////AP///wD///8A////AOvr60l2dnnpVVVZ/2ZlnP9ycdL/c3LW/3Jx1f9xcNT/cG/T/2ppvv9RUVH/zoSs/+yRwf/uk8L/7pPC/++UxP/pk8D/pXaP/1hVV/9/fn/p7+/vSf///wD///8A////AP///wD///8AAAAAAP///wD///8A////AP///wDg4OBWX19h/11dcv9zccr/d3XZ/3Z02P91c9f/dHLX/3Nx1v9xcNT/a2q//1FRUf/Ngqn/6o6+/+uQwP/tksH/7pPD/++Vxf/xl8b/3I64/3Jiav9lY2T/2dnZVv///wD///8A////AP///wAAAAAA////AP///wD///8A5OTkQ1tbXf9hYIH/eHfY/3l32/94dtr/d3ba/3Z12f90c9f/c3LW/3Nx1f9tbMH/UVFR/8l/pf/mirr/6Iy8/+mOvv/qj7//7JHB/++VxP/xl8b/04qx/1RUUv9ZWVj/6+vrQ////wD///8A////AAAAAAD///8A////APX19Q9mZmjfYF98/3t53P97ed3/enjc/3l32/93dtr/dnXZ/3Z02P91c9f/dXTY/25tw/9RUVH/yHyj/+WIuf/libn/54q7/+mNvf/rj7//7JHB/9WJsf9XVlX/Z2hb/3BxX/9vb23f+fn5D////wD///8AAAAAAP///wD///8AkZCSnFpaZ/97edn/fHre/3t53v97ed3/enjc/3l33P94dtv/dnXZ/3V02P92dNj/cG7D/1FRUf/FeaH/4oW2/+OGt//liLj/54q6/+mOvv/Sha3/V1VV/2doW//FyYL/xcqC/19fWf+fn5+c////AP///wAAAAAA////ANjY2DhRUVL/dXPA/3994f9+fOD/fXvf/3x63v97ed3/enjc/3l32/94dtr/eHba/3d12f9xb8T/UVFR/8J1nf/fgLH/4IOz/+KEtf/liLn/zoCq/1dVVf9naFr/w8eA/83Shf/P1Yb/q652/1JSUf/h4eE4////AAAAAAD///8Afn6ApWNigv+BfuL/gH3h/3584P99e9//fHre/3x63v97ed3/eXjc/3l32/95d9v/d3ba/3Fwxf9RUVH/wXSc/9x9r//fgbL/4YO0/8t9pv9XVVT/Z2ha/8LHgf/N0oX/ztOF/8/Uhv/S14b/c3Vh/42Ni6X///8AAAAAAOfn5xJRUVH/eXbG/4F/4/+AfuL/f33h/3584P99e+D/fHrf/3x63v97ed3/e3ne/3p43f9ycMH/Y2OR/1FRUf+NY3j/w3Sd/9x9rv/IeaL/V1VU/2doWv/DyIH/zNGF/87Thf/Q1Yb/0daG/9TZh/+ytnj/UlJS/+/v7xIAAAAAq6urYF1cav+DgeX/goDk/4F/4/+AfuL/f33h/3994f9+fOD/fXvf/3x63v95d9X/YF99/1FRUb9sbG+AZmVmgG1qa4BRUVG/fWBv/1dVVP9naFr/xMiB/83Shv/O04b/z9WG/9HWhv/S14b/1NqH/9TZhv9fYFn/t7e3YAAAAAB6enyeaGeV/4SC5v+DgeX/goDk/4F+4/+BfuP/f33h/3584P9+fOD/fHrb/1xcbv9sbG6g39/fQP///wD///8A////ANra2kBbWlugVVVT/8LHgf/O04X/ztOF/8/Uhf/Q1Yb/0teG/9PZh//V24f/2N6I/4aIZv+IiIeeAAAAAFpaXMx0crX/hYLm/4SC5v+DgeX/goDk/4KA5P+Bf+P/gH7i/3994f9oZ5v/X19hv/Hx8SD///8A////AP///wD///8A////AOfn5yBcXFq/m55w/9DVhv/R1ob/0teG/9LXhv/T2Yb/1dqH/9jdiP/Z34j/o6Zy/2VlY8wAAAAAUVFR7Hx7zP+GhOj/hYLm/4SB5f+Egub/g4Hl/4KA5P+Bf+P/gX/j/1paZP+xsbFA////AP///wD///8A////AP///wD///8A////AKampkBqa17/0teG/9LXhv/T2Yf/1dqH/9bciP/Y3oj/2N6H/9rgiP+5vXr/UlJS7AAAAABRUVH7gX7W/4eE6P+GhOj/hYPn/4WC5v+EgeX/g4Hl/4OB5f9/fNv/UVFR/9zc3AD///8A////AP///wD///8A////AP///wD///8A0NDQAFRUU//R14b/1dqH/9Xbh//W3If/192H/9jeh//a4Ij/3OKI/8PJfv9RUVH7AAAAAFFRUfuCgNj/iIbq/4eE6P+HhOj/hoPn/4WC5v+Egub/hILm/4B+3f9RUVH/3NzcAP///wD///8A////AP///wD///8A////AP///wDLy8sAUlJR/87ThP/W3Ij/192H/9nfiP/Z34j/2uCI/9vhiP/d44j/xMh+/1FRUfsAAAAAUVFR7H99zv+Jh+v/iIXq/4iF6v+Hhen/hoTo/4aD5/+Fgub/hYLm/1taZP+xsbFA////AP///wD///8A////AP///wD///8A////AJWVlUBSUlH/UlJR/25vXf+Ul2z/ur97/9jeh//c44j/3uWJ/97lif+8wXr/UVFR7AAAAABaWlzMd3W4/4qH6/+Kh+v/iYbq/4iF6f+HhOj/iIXp/4aE6P+GhOj/a2qe/19fYr/x8fEg////AP///wD///8A////AP///wDp6ekgW1xcv2SWe/9nm4D/Wm9k/1daVv9RUVH/VlZT/3p8Yf+ipnH/ys+A/6ircv9lZmPMAAAAAHp6fJ5rapf/i4js/4uJ7f+KiOz/iYbq/4mG6v+Ihur/iIbq/4eE6P+FguP/Xl5w/2xsb6Df399A////AP///wD///8A2traQGVnZqBccGX/d9Sk/3vZqf+B367/g+Gw/33No/9qk33/W2lh/1ZXVP9RUVH/U1NR/3d3dZ4AAAAAq6urYF5ea/+Ni+//jInt/4uI7P+LiOz/iofr/4mG6v+Jhur/iIXp/4iF6f+EguD/ZGSB/1FRUb9ra2+AZWdmgGpubIBRUVG/Xn1u/3TOn/9516f/ftyr/4Herf+G47L/iOa1/4vot/+Q7bz/k+++/4PHo/9SVFP/oaGhYAAAAADn5+cSUVFR/4F/z/+Niu7/jInt/4yJ7f+LiOz/i4nt/4qI7P+LiOz/iofr/4mG6v+Jhur/fnzN/2ZljP9RUVH/YpB5/2++lf921KT/edem/3vZqf+A3q3/hOGw/4bjs/+J5rX/juu6/5Dsu/+U8cD/g8Wi/1JTUv/t7e0SAAAAAP///wB+foClZ2aG/46L8P+Oi/D/jYru/4yJ7f+Mie3/i4js/4uI7P+LiOz/iofr/4qH6/+KiOz/eXjA/1FRUf9zyJz/eden/3zaqf982qn/ftys/4Hfrv+G47L/iOW0/4rntv+N6rn/ku++/5XxwP9le2//iYqKpf///wAAAAAA////ANjY2DhRUVL/gH7L/4+M8P+Oi+//jovv/42K7v+Niu7/jIru/4yK7v+Mie3/jInt/4uI7P96eMD/UVFR/3bLoP972an/ftyr/4Herv+D4K//heOy/4jmtf+L6Lf/juu5/5DtvP+V8cD/gcGf/1NUU//l5eU4////AAAAAAD///8A////AJGRkpxdXGn/jYrp/5CN8f+PjPD/j4zw/46L7/+Oi+//jYru/42K7v+Niu7/jYru/3x5wv9RUVH/ec6i/4Herv+B367/hOGw/4bjsv+I5bT/i+i3/43quf+Q7bz/k+++/4/jtv9bY17/nZ6enP///wD///8AAAAAAP///wD///8A9vb2D2Zmad9mZYL/kI3w/5CN8f+QjfH/j4zw/4+M8P+PjPD/j4zw/46L7/+Oi+//fHrD/1FRUf9/0qb/hOGw/4Xjsv+F47L/iOa1/4vot/+O67r/kO28/5Lvvf+S7b3/ZHht/3N1dN/6+voP////AP///wAAAAAA////AP///wD///8A5OTkQ1xcXv9oZ4j/kI3v/5GN8f+RjfH/kI3x/5CN8f+QjfH/kI3x/5CN8f99e8P/UVFR/3/UqP+I5bT/iOW0/4vot/+L6Lf/juu5/4/su/+S773/kem6/2Z9cf9gYmH/6+vrQ////wD///8A////AAAAAAD///8A////AP///wD///8A4ODgVl9fYf9iYnf/iojh/5KP8/+Sj/P/kY7y/5GO8v+RjvL/kY7y/358xf9RUVH/hdmt/4vot/+O67n/juu5/5Dtu/+Q7bv/ku++/4rXrv9hcGj/aGpp/+np6Vb///8A////AP///wD///8AAAAAAP///wD///8A////AP///wD///8A7OzsSXd3eelWVlr/dXOr/5CO7v+TkPT/k5D0/5KP8/+Sj/P/f33F/1FRUf+I267/kO28/5Dsu/+Q7Lv/ku+9/5Douv9zoon/U1VU/4GDgunx8fFJ////AP///wD///8A////AP///wAAAAAA////AP///wD///8A////AP///wD///8A/f39HLW1tatbW13/WVlg/3Bunv+Gg9T/k4/y/5SR9f+Bfsf/UVFR/4vfsv+S773/kOu7/4TKpf9ul4H/VlpY/19hYP/AwMCr////HP///wD///8A////AP///wD///8A////AAAAAAD///8A////AP///wD///8A////AP///wD///8A////APf390y0tLS6cG9y/1FRUf9WVlr/Y2J2/2Fgdv9RUVH/ZYBx/2N1a/9VV1b/UVFR/3R2df+/v7+6+vr6TP///wD///8A////AP///wD///8A////AP///wD///8AAAAAAP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8o7e3tdcXFxbCqqqrbl5eX9HBwcP2YmJj0q6ur28fHx7Dv7+91////KP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wAAAAAA//Af//+AA//+AAD//AAAf/gAAD/wAAAf4AAAD8AAAAfAAAAHgAAAA4AAAAOAAAADAAfAAQAP4AEAH/ABAB/wAQAf8AEAH/ABAA/gAQAHwAGAAAADgAAAA4AAAAPAAAAHwAAAB+AAAA/wAAAf+AAAP/wAAH/+AAD//4AD///wH/8="/> | |
| 6 <script id="notfound">window.onload=function(){document.body.innerHTML=""}</script> | |
| 7 <script language="javascript" type="text/javascript"> | |
| 8 {//----------------------------------------------------------------------------- | |
| 9 // | |
| 10 // PURPOSE | |
| 11 // | |
| 12 // Krona is a flexible tool for exploring the relative proportions of | |
| 13 // hierarchical data, such as metagenomic classifications, using a | |
| 14 // radial, space-filling display. It is implemented using HTML5 and | |
| 15 // JavaScript, allowing charts to be explored locally or served over the | |
| 16 // Internet, requiring only a current version of any major web | |
| 17 // browser. Krona charts can be created using an Excel template or from | |
| 18 // common bioinformatic formats using the provided conversion scripts. | |
| 19 // | |
| 20 // | |
| 21 // COPYRIGHT LICENSE | |
| 22 // | |
| 23 // Copyright (c) 2011, Battelle National Biodefense Institute (BNBI); | |
| 24 // all rights reserved. Authored by: Brian Ondov, Nicholas Bergman, and | |
| 25 // Adam Phillippy | |
| 26 // | |
| 27 // This Software was prepared for the Department of Homeland Security | |
| 28 // (DHS) by the Battelle National Biodefense Institute, LLC (BNBI) as | |
| 29 // part of contract HSHQDC-07-C-00020 to manage and operate the National | |
| 30 // Biodefense Analysis and Countermeasures Center (NBACC), a Federally | |
| 31 // Funded Research and Development Center. | |
| 32 // | |
| 33 // Redistribution and use in source and binary forms, with or without | |
| 34 // modification, are permitted provided that the following conditions are | |
| 35 // met: | |
| 36 // | |
| 37 // * Redistributions of source code must retain the above copyright | |
| 38 // notice, this list of conditions and the following disclaimer. | |
| 39 // | |
| 40 // * Redistributions in binary form must reproduce the above copyright | |
| 41 // notice, this list of conditions and the following disclaimer in the | |
| 42 // documentation and/or other materials provided with the distribution. | |
| 43 // | |
| 44 // * Neither the name of the Battelle National Biodefense Institute nor | |
| 45 // the names of its contributors may be used to endorse or promote | |
| 46 // products derived from this software without specific prior written | |
| 47 // permission. | |
| 48 // | |
| 49 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 50 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 51 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| 52 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| 53 // HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| 54 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| 55 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| 56 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| 57 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 58 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| 59 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 60 // | |
| 61 // | |
| 62 // TRADEMARK LICENSE | |
| 63 // | |
| 64 // KRONA(TM) is a trademark of the Department of Homeland Security, and use | |
| 65 // of the trademark is subject to the following conditions: | |
| 66 // | |
| 67 // * Distribution of the unchanged, official code/software using the | |
| 68 // KRONA(TM) mark is hereby permitted by the Department of Homeland | |
| 69 // Security, provided that the software is distributed without charge | |
| 70 // and modification. | |
| 71 // | |
| 72 // * Distribution of altered source code/software using the KRONA(TM) mark | |
| 73 // is not permitted unless written permission has been granted by the | |
| 74 // Department of Homeland Security. | |
| 75 // | |
| 76 // | |
| 77 // FOR MORE INFORMATION VISIT | |
| 78 // | |
| 79 // http://krona.sourceforge.net | |
| 80 // | |
| 81 //----------------------------------------------------------------------------- | |
| 82 } | |
| 83 | |
| 84 | |
| 85 var canvas; | |
| 86 var context; | |
| 87 var svg; // for snapshot mode | |
| 88 var collapse = true; | |
| 89 var collapseCheckBox; | |
| 90 var collapseLast; | |
| 91 var compress; | |
| 92 var compressCheckBox; | |
| 93 var maxAbsoluteDepthText; | |
| 94 var maxAbsoluteDepthButtonDecrease; | |
| 95 var maxAbsoluteDepthButtonIncrease; | |
| 96 var fontSize = 11; | |
| 97 var fontSizeText; | |
| 98 var fontSizeButtonDecrease; | |
| 99 var fontSizeButtonIncrease; | |
| 100 var fontSizeLast; | |
| 101 var radiusButtonDecrease; | |
| 102 var radiusButtonIncrease; | |
| 103 var shorten; | |
| 104 var shortenCheckBox; | |
| 105 var maxAbsoluteDepth; | |
| 106 var backButton; | |
| 107 var upButton; | |
| 108 var forwardButton; | |
| 109 var snapshotButton; | |
| 110 var snapshotMode = false; | |
| 111 var details; | |
| 112 var detailsName; | |
| 113 var search; | |
| 114 var searchResults; | |
| 115 var nSearchResults; | |
| 116 var useHueCheckBox; | |
| 117 var useHueDiv; | |
| 118 var datasetDropDown; | |
| 119 var datasetButtonLast; | |
| 120 var datasetButtonPrev; | |
| 121 var datasetButtonNext; | |
| 122 var keyControl; | |
| 123 var showKeys = true; | |
| 124 var linkButton; | |
| 125 var linkText; | |
| 126 var frame; | |
| 127 | |
| 128 // Node references. Note that the meanings of 'selected' and 'focused' are | |
| 129 // swapped in the docs. | |
| 130 // | |
| 131 var head; // the root of the entire tree | |
| 132 var selectedNode = 0; // the root of the current view | |
| 133 var focusNode = 0; // a node chosen for more info (single-click) | |
| 134 var highlightedNode = 0; // mouse hover node | |
| 135 var highlightingHidden = false; | |
| 136 var nodes = new Array(); | |
| 137 var currentNodeID = 0; // to iterate while loading | |
| 138 | |
| 139 var nodeHistory = new Array(); | |
| 140 var nodeHistoryPosition = 0; | |
| 141 | |
| 142 var dataEnabled = false; // true when supplemental files are present | |
| 143 | |
| 144 // store non-Krona GET variables so they can be passed on to links | |
| 145 // | |
| 146 var getVariables = new Array(); | |
| 147 | |
| 148 // selectedNodeLast is separate from the history, since we need to check | |
| 149 // properties of the last node viewed when browsing through the history | |
| 150 // | |
| 151 var selectedNodeLast = 0; | |
| 152 var zoomOut = false; | |
| 153 | |
| 154 // temporary zoom-in while holding the mouse button on a wedge | |
| 155 // | |
| 156 var quickLook = false; // true when in quick look state | |
| 157 var mouseDown = false; | |
| 158 var mouseDownTime; // to detect mouse button hold | |
| 159 var quickLookHoldLength = 200; | |
| 160 | |
| 161 var imageWidth; | |
| 162 var imageHeight; | |
| 163 var centerX; | |
| 164 var centerY; | |
| 165 var gRadius; | |
| 166 var updateViewNeeded = false; | |
| 167 | |
| 168 // Determines the angle that the pie chart starts at. 90 degrees makes the | |
| 169 // center label consistent with the children. | |
| 170 // | |
| 171 var rotationOffset = Math.PI / 2; | |
| 172 | |
| 173 var buffer; | |
| 174 var bufferFactor = .1; | |
| 175 | |
| 176 // The maps are the small pie charts showing the current slice being viewed. | |
| 177 // | |
| 178 var mapBuffer = 10; | |
| 179 var mapRadius = 0; | |
| 180 var maxMapRadius = 25; | |
| 181 var mapWidth = 150; | |
| 182 var maxLabelOverhang = Math.PI * 4.18; | |
| 183 | |
| 184 // Keys are the labeled boxes for slices in the highest level that are too thin | |
| 185 // to label. | |
| 186 // | |
| 187 var maxKeySizeFactor = 2; // will be multiplied by font size | |
| 188 var keySize; | |
| 189 var keys; | |
| 190 var keyBuffer = 10; | |
| 191 var currentKey; | |
| 192 var keyMinTextLeft; | |
| 193 var keyMinAngle; | |
| 194 | |
| 195 var minRingWidthFactor = 5; // will be multiplied by font size | |
| 196 var maxPossibleDepth; // the theoretical max that can be displayed | |
| 197 var maxDisplayDepth; // the actual depth that will be displayed | |
| 198 var headerHeight = 0;//document.getElementById('options').clientHeight; | |
| 199 var historySpacingFactor = 1.6; // will be multiplied by font size | |
| 200 var historyAlphaDelta = .25; | |
| 201 | |
| 202 // appearance | |
| 203 // | |
| 204 var lineOpacity = 0.3; | |
| 205 var saturation = 0.5; | |
| 206 var lightnessBase = 0.6; | |
| 207 var lightnessMax = .8; | |
| 208 var thinLineWidth = .3; | |
| 209 var highlightLineWidth = 1.5; | |
| 210 var labelBoxBuffer = 6; | |
| 211 var labelBoxRounding = 15; | |
| 212 var labelWidthFudge = 1.05; // The width of unshortened labels are set slightly | |
| 213 // longer than the name width so the animation | |
| 214 // finishes faster. | |
| 215 var fontNormal; | |
| 216 var fontBold; | |
| 217 var fontFamily = 'sans-serif'; | |
| 218 //var fontFaceBold = 'bold Arial'; | |
| 219 var nodeRadius; | |
| 220 var angleFactor; | |
| 221 var tickLength; | |
| 222 var compressedRadii; | |
| 223 | |
| 224 // colors | |
| 225 // | |
| 226 var highlightFill = 'rgba(255, 255, 255, .3)'; | |
| 227 var colorUnclassified = 'rgb(220,220,220)'; | |
| 228 | |
| 229 // label staggering | |
| 230 // | |
| 231 var labelOffsets; // will store the current offset at each depth | |
| 232 // | |
| 233 // This will store pointers to the last node that had a label in each offset (or "track") of a | |
| 234 // each depth. These will be used to shorten neighboring labels that would overlap. | |
| 235 // The [nLabelNodes] index will store the last node with a radial label. | |
| 236 // labelFirstNodes is the same, but to check for going all the way around and | |
| 237 // overlapping the first labels. | |
| 238 // | |
| 239 var labelLastNodes; | |
| 240 var labelFirstNodes; | |
| 241 // | |
| 242 var nLabelOffsets = 3; // the number of offsets to use | |
| 243 | |
| 244 var mouseX = -1; | |
| 245 var mouseY = -1; | |
| 246 | |
| 247 // tweening | |
| 248 // | |
| 249 var progress = 0; // for tweening; goes from 0 to 1. | |
| 250 var progressLast = 0; | |
| 251 var tweenFactor = 0; // progress converted by a curve for a smoother effect. | |
| 252 var tweenLength = 850; // in ms | |
| 253 var tweenCurvature = 13; | |
| 254 // | |
| 255 // tweenMax is used to scale the sigmoid function so its range is [0,1] for the | |
| 256 // domain [0,1] | |
| 257 // | |
| 258 var tweenMax = 1 / (1 + Math.exp(-tweenCurvature / 2)); | |
| 259 // | |
| 260 var tweenStartTime; | |
| 261 | |
| 262 // for framerate debug | |
| 263 // | |
| 264 var tweenFrames = 0; | |
| 265 var fpsDisplay = document.getElementById('frameRate'); | |
| 266 | |
| 267 // Arrays to translate xml attribute names into displayable attribute names | |
| 268 // | |
| 269 var attributes = new Array(); | |
| 270 // | |
| 271 var magnitudeIndex; // the index of attribute arrays used for magnitude | |
| 272 var membersAssignedIndex; | |
| 273 var membersSummaryIndex; | |
| 274 | |
| 275 // For defining gradients | |
| 276 // | |
| 277 var hueDisplayName; | |
| 278 var hueStopPositions; | |
| 279 var hueStopHues; | |
| 280 var hueStopText; | |
| 281 | |
| 282 // multiple datasets | |
| 283 // | |
| 284 var currentDataset = 0; | |
| 285 var lastDataset = 0; | |
| 286 var datasets = 1; | |
| 287 var datasetNames; | |
| 288 var datasetSelectSize = 30; | |
| 289 var datasetAlpha = new Tween(0, 0); | |
| 290 var datasetWidths = new Array(); | |
| 291 var datasetChanged; | |
| 292 var datasetSelectWidth = 50; | |
| 293 | |
| 294 window.onload = load; | |
| 295 | |
| 296 var image; | |
| 297 var hiddenPattern; | |
| 298 var loadingImage; | |
| 299 var logoImage; | |
| 300 | |
| 301 function resize() | |
| 302 { | |
| 303 imageWidth = window.innerWidth; | |
| 304 imageHeight = window.innerHeight; | |
| 305 | |
| 306 if ( ! snapshotMode ) | |
| 307 { | |
| 308 context.canvas.width = imageWidth; | |
| 309 context.canvas.height = imageHeight; | |
| 310 } | |
| 311 | |
| 312 if ( datasetDropDown ) | |
| 313 { | |
| 314 var ratio = | |
| 315 (datasetDropDown.offsetTop + datasetDropDown.clientHeight) * 2 / | |
| 316 imageHeight; | |
| 317 | |
| 318 if ( ratio > 1 ) | |
| 319 { | |
| 320 ratio = 1; | |
| 321 } | |
| 322 | |
| 323 ratio = Math.sqrt(ratio); | |
| 324 | |
| 325 datasetSelectWidth = | |
| 326 (datasetDropDown.offsetLeft + datasetDropDown.clientWidth) * ratio; | |
| 327 } | |
| 328 var leftMargin = datasets > 1 ? datasetSelectWidth + 30 : 0; | |
| 329 var minDimension = imageWidth - mapWidth - leftMargin > imageHeight ? | |
| 330 imageHeight : | |
| 331 imageWidth - mapWidth - leftMargin; | |
| 332 | |
| 333 maxMapRadius = minDimension * .03; | |
| 334 buffer = minDimension * bufferFactor; | |
| 335 margin = minDimension * .015; | |
| 336 centerX = (imageWidth - mapWidth - leftMargin) / 2 + leftMargin; | |
| 337 centerY = imageHeight / 2; | |
| 338 gRadius = minDimension / 2 - buffer; | |
| 339 //context.font = '11px sans-serif'; | |
| 340 } | |
| 341 | |
| 342 function handleResize() | |
| 343 { | |
| 344 updateViewNeeded = true; | |
| 345 } | |
| 346 | |
| 347 function Attribute() | |
| 348 { | |
| 349 } | |
| 350 | |
| 351 function Tween(start, end) | |
| 352 { | |
| 353 this.start = start; | |
| 354 this.end = end; | |
| 355 this.current = this.start; | |
| 356 | |
| 357 this.current = function() | |
| 358 { | |
| 359 if ( progress == 1 || this.start == this.end ) | |
| 360 { | |
| 361 return this.end; | |
| 362 } | |
| 363 else | |
| 364 { | |
| 365 return this.start + tweenFactor * (this.end - this.start); | |
| 366 } | |
| 367 }; | |
| 368 | |
| 369 this.setTarget = function(target) | |
| 370 { | |
| 371 this.start = this.current(); | |
| 372 this.end = target; | |
| 373 } | |
| 374 } | |
| 375 | |
| 376 function Node() | |
| 377 { | |
| 378 this.id = currentNodeID; | |
| 379 currentNodeID++; | |
| 380 nodes[this.id] = this; | |
| 381 | |
| 382 this.angleStart = new Tween(Math.PI, 0); | |
| 383 this.angleEnd = new Tween(Math.PI, 0); | |
| 384 this.radiusInner = new Tween(1, 1); | |
| 385 this.labelRadius = new Tween(1, 1); | |
| 386 this.labelWidth = new Tween(0, 0); | |
| 387 this.scale = new Tween(1, 1); // TEMP | |
| 388 this.radiusOuter = new Tween(1, 1); | |
| 389 | |
| 390 this.r = new Tween(255, 255); | |
| 391 this.g = new Tween(255, 255); | |
| 392 this.b = new Tween(255, 255); | |
| 393 | |
| 394 this.alphaLabel = new Tween(0, 1); | |
| 395 this.alphaLine = new Tween(0, 1); | |
| 396 this.alphaArc = new Tween(0, 0); | |
| 397 this.alphaWedge = new Tween(0, 1); | |
| 398 this.alphaOther = new Tween(0, 1); | |
| 399 this.alphaPattern = new Tween(0, 0); | |
| 400 this.children = Array(); | |
| 401 this.parent = 0; | |
| 402 | |
| 403 this.attributes = new Array(attributes.length); | |
| 404 | |
| 405 this.addChild = function(child) | |
| 406 { | |
| 407 this.children.push(child); | |
| 408 }; | |
| 409 | |
| 410 this.addLabelNode = function(depth, labelOffset) | |
| 411 { | |
| 412 if ( labelHeadNodes[depth][labelOffset] == 0 ) | |
| 413 { | |
| 414 // this will become the head node for this list | |
| 415 | |
| 416 labelHeadNodes[depth][labelOffset] = this; | |
| 417 this.labelPrev = this; | |
| 418 } | |
| 419 | |
| 420 var head = labelHeadNodes[depth][labelOffset]; | |
| 421 | |
| 422 this.labelNext = head; | |
| 423 this.labelPrev = head.labelPrev; | |
| 424 head.labelPrev.labelNext = this; | |
| 425 head.labelPrev = this; | |
| 426 } | |
| 427 | |
| 428 this.canDisplayDepth = function() | |
| 429 { | |
| 430 // whether this node is at a depth that can be displayed, according | |
| 431 // to the max absolute depth | |
| 432 | |
| 433 return this.depth <= maxAbsoluteDepth; | |
| 434 } | |
| 435 | |
| 436 this.canDisplayHistory = function() | |
| 437 { | |
| 438 var radiusInner; | |
| 439 | |
| 440 if ( compress ) | |
| 441 { | |
| 442 radiusInner = compressedRadii[0]; | |
| 443 } | |
| 444 else | |
| 445 { | |
| 446 radiusInner = nodeRadius; | |
| 447 } | |
| 448 | |
| 449 return ( | |
| 450 -this.labelRadius.end * gRadius + | |
| 451 historySpacingFactor * fontSize / 2 < | |
| 452 radiusInner * gRadius | |
| 453 ); | |
| 454 } | |
| 455 | |
| 456 this.canDisplayLabelCurrent = function() | |
| 457 { | |
| 458 return ( | |
| 459 (this.angleEnd.current() - this.angleStart.current()) * | |
| 460 (this.radiusInner.current() * gRadius + gRadius) >= | |
| 461 minWidth()); | |
| 462 } | |
| 463 | |
| 464 this.checkHighlight = function() | |
| 465 { | |
| 466 if ( this.children.length == 0 && this == focusNode ) | |
| 467 { | |
| 468 //return false; | |
| 469 } | |
| 470 | |
| 471 if ( this.hide ) | |
| 472 { | |
| 473 return false; | |
| 474 } | |
| 475 | |
| 476 if ( this.radiusInner.end == 1 ) | |
| 477 { | |
| 478 // compressed to the outside; don't check | |
| 479 | |
| 480 return false; | |
| 481 } | |
| 482 | |
| 483 var highlighted = false; | |
| 484 | |
| 485 var angleStartCurrent = this.angleStart.current() + rotationOffset; | |
| 486 var angleEndCurrent = this.angleEnd.current() + rotationOffset; | |
| 487 var radiusInner = this.radiusInner.current() * gRadius; | |
| 488 | |
| 489 for ( var i = 0; i < this.children.length; i++ ) | |
| 490 { | |
| 491 highlighted = this.children[i].checkHighlight(); | |
| 492 | |
| 493 if ( highlighted ) | |
| 494 { | |
| 495 return true; | |
| 496 } | |
| 497 } | |
| 498 | |
| 499 if ( this != selectedNode && ! this.getCollapse() ) | |
| 500 { | |
| 501 context.beginPath(); | |
| 502 context.arc(0, 0, radiusInner, angleStartCurrent, angleEndCurrent, false); | |
| 503 context.arc(0, 0, gRadius, angleEndCurrent, angleStartCurrent, true); | |
| 504 context.closePath(); | |
| 505 | |
| 506 if ( context.isPointInPath(mouseX - centerX, mouseY - centerY) ) | |
| 507 { | |
| 508 highlighted = true; | |
| 509 } | |
| 510 | |
| 511 if | |
| 512 ( | |
| 513 ! highlighted && | |
| 514 (angleEndCurrent - angleStartCurrent) * | |
| 515 (radiusInner + gRadius) < | |
| 516 minWidth() && | |
| 517 this.getDepth() == selectedNode.getDepth() + 1 | |
| 518 ) | |
| 519 { | |
| 520 if ( showKeys && this.checkHighlightKey() ) | |
| 521 { | |
| 522 highlighted = true; | |
| 523 } | |
| 524 } | |
| 525 } | |
| 526 | |
| 527 if ( highlighted ) | |
| 528 { | |
| 529 if ( this != highlightedNode ) | |
| 530 { | |
| 531 // document.body.style.cursor='pointer'; | |
| 532 } | |
| 533 | |
| 534 highlightedNode = this; | |
| 535 } | |
| 536 | |
| 537 return highlighted; | |
| 538 } | |
| 539 | |
| 540 this.checkHighlightCenter = function() | |
| 541 { | |
| 542 if ( ! this.canDisplayHistory() ) | |
| 543 { | |
| 544 return; | |
| 545 } | |
| 546 | |
| 547 var cx = centerX; | |
| 548 var cy = centerY - this.labelRadius.end * gRadius; | |
| 549 //var dim = context.measureText(this.name); | |
| 550 | |
| 551 var width = this.nameWidth; | |
| 552 | |
| 553 if ( this.searchResultChildren() ) | |
| 554 { | |
| 555 var results = searchResultString(this.searchResultChildren()); | |
| 556 var dim = context.measureText(results); | |
| 557 width += dim.width; | |
| 558 } | |
| 559 | |
| 560 if | |
| 561 ( | |
| 562 mouseX > cx - width / 2 && | |
| 563 mouseX < cx + width / 2 && | |
| 564 mouseY > cy - historySpacingFactor * fontSize / 2 && | |
| 565 mouseY < cy + historySpacingFactor * fontSize / 2 | |
| 566 ) | |
| 567 { | |
| 568 highlightedNode = this; | |
| 569 return; | |
| 570 } | |
| 571 | |
| 572 if ( this.getParent() ) | |
| 573 { | |
| 574 this.getParent().checkHighlightCenter(); | |
| 575 } | |
| 576 } | |
| 577 | |
| 578 this.checkHighlightKey = function() | |
| 579 { | |
| 580 var offset = keyOffset(); | |
| 581 | |
| 582 var xMin = imageWidth - keySize - margin - this.keyNameWidth - keyBuffer; | |
| 583 var xMax = imageWidth - margin; | |
| 584 var yMin = offset; | |
| 585 var yMax = offset + keySize; | |
| 586 | |
| 587 currentKey++; | |
| 588 | |
| 589 return ( | |
| 590 mouseX > xMin && | |
| 591 mouseX < xMax && | |
| 592 mouseY > yMin && | |
| 593 mouseY < yMax); | |
| 594 } | |
| 595 | |
| 596 this.checkHighlightMap = function() | |
| 597 { | |
| 598 if ( this.parent ) | |
| 599 { | |
| 600 this.parent.checkHighlightMap(); | |
| 601 } | |
| 602 | |
| 603 if ( this.getCollapse() || this == focusNode ) | |
| 604 { | |
| 605 return; | |
| 606 } | |
| 607 | |
| 608 var box = this.getMapPosition(); | |
| 609 | |
| 610 if | |
| 611 ( | |
| 612 mouseX > box.x - mapRadius && | |
| 613 mouseX < box.x + mapRadius && | |
| 614 mouseY > box.y - mapRadius && | |
| 615 mouseY < box.y + mapRadius | |
| 616 ) | |
| 617 { | |
| 618 highlightedNode = this; | |
| 619 } | |
| 620 } | |
| 621 | |
| 622 /* this.collapse = function() | |
| 623 { | |
| 624 for (var i = 0; i < this.children.length; i++ ) | |
| 625 { | |
| 626 this.children[i] = this.children[i].collapse(); | |
| 627 } | |
| 628 | |
| 629 if | |
| 630 ( | |
| 631 this.children.length == 1 && | |
| 632 this.children[0].magnitude == this.magnitude | |
| 633 ) | |
| 634 { | |
| 635 this.children[0].parent = this.parent; | |
| 636 this.children[0].getDepth() = this.parent.getDepth() + 1; | |
| 637 return this.children[0]; | |
| 638 } | |
| 639 else | |
| 640 { | |
| 641 return this; | |
| 642 } | |
| 643 } | |
| 644 */ | |
| 645 this.draw = function(labelMode, selected, searchHighlighted) | |
| 646 { | |
| 647 var depth = this.getDepth() - selectedNode.getDepth() + 1; | |
| 648 // var hidden = false; | |
| 649 | |
| 650 if ( selectedNode == this ) | |
| 651 { | |
| 652 selected = true; | |
| 653 } | |
| 654 | |
| 655 var angleStartCurrent = this.angleStart.current() + rotationOffset; | |
| 656 var angleEndCurrent = this.angleEnd.current() + rotationOffset; | |
| 657 var radiusInner = this.radiusInner.current() * gRadius; | |
| 658 var canDisplayLabelCurrent = this.canDisplayLabelCurrent(); | |
| 659 var hiddenSearchResults = false; | |
| 660 | |
| 661 /* if ( ! this.hide ) | |
| 662 { | |
| 663 for ( var i = 0; i < this.children.length; i++ ) | |
| 664 { | |
| 665 if ( this.children[i].hide && this.children[i].searchResults ) | |
| 666 { | |
| 667 hiddenSearchResults = true; | |
| 668 } | |
| 669 } | |
| 670 } | |
| 671 */ | |
| 672 var drawChildren = | |
| 673 ( ! this.hide || ! this.hidePrev && progress < 1 ) && | |
| 674 ( ! this.hideAlone || ! this.hideAlonePrev && progress < 1 ); | |
| 675 | |
| 676 // if ( this.alphaWedge.current() > 0 || this.alphaLabel.current() > 0 ) | |
| 677 { | |
| 678 var lastChildAngleEnd; | |
| 679 | |
| 680 if ( this.hasChildren() )//canDisplayChildren ) | |
| 681 { | |
| 682 lastChildAngleEnd = | |
| 683 this.children[this.children.length - 1].angleEnd.current() | |
| 684 + rotationOffset; | |
| 685 } | |
| 686 | |
| 687 if ( labelMode ) | |
| 688 { | |
| 689 var drawRadial = | |
| 690 !( | |
| 691 this.parent && | |
| 692 this.parent != selectedNode && | |
| 693 angleEndCurrent == this.parent.angleEnd.current() + rotationOffset | |
| 694 ); | |
| 695 | |
| 696 if ( angleStartCurrent != angleEndCurrent ) | |
| 697 { | |
| 698 this.drawLines(angleStartCurrent, angleEndCurrent, radiusInner, drawRadial, selected); | |
| 699 } | |
| 700 | |
| 701 var alphaOtherCurrent = this.alphaOther.current(); | |
| 702 var childRadiusInner; | |
| 703 | |
| 704 if ( this == selectedNode || alphaOtherCurrent ) | |
| 705 { | |
| 706 childRadiusInner = | |
| 707 this.children[this.children.length - 1].radiusInner.current() * gRadius; | |
| 708 } | |
| 709 | |
| 710 if ( this == selectedNode ) | |
| 711 { | |
| 712 this.drawReferenceRings(childRadiusInner); | |
| 713 } | |
| 714 | |
| 715 if | |
| 716 ( | |
| 717 selected && | |
| 718 ! searchHighlighted && | |
| 719 this != selectedNode && | |
| 720 ( | |
| 721 this.isSearchResult || | |
| 722 this.hideAlone && this.searchResultChildren() || | |
| 723 false | |
| 724 // this.hide && | |
| 725 // this.containsSearchResult | |
| 726 ) | |
| 727 ) | |
| 728 { | |
| 729 context.globalAlpha = this.alphaWedge.current(); | |
| 730 | |
| 731 drawWedge | |
| 732 ( | |
| 733 angleStartCurrent, | |
| 734 angleEndCurrent, | |
| 735 radiusInner, | |
| 736 gRadius, | |
| 737 highlightFill, | |
| 738 0, | |
| 739 true | |
| 740 ); | |
| 741 | |
| 742 if | |
| 743 ( | |
| 744 this.keyed && | |
| 745 ! showKeys && | |
| 746 this.searchResults && | |
| 747 ! searchHighlighted && | |
| 748 this != highlightedNode && | |
| 749 this != focusNode | |
| 750 ) | |
| 751 { | |
| 752 var angle = (angleEndCurrent + angleStartCurrent) / 2; | |
| 753 this.drawLabel(angle, true, false, true, true); | |
| 754 } | |
| 755 | |
| 756 //this.drawHighlight(false); | |
| 757 searchHighlighted = true; | |
| 758 } | |
| 759 | |
| 760 if | |
| 761 ( | |
| 762 this == selectedNode || | |
| 763 // true | |
| 764 //(canDisplayLabelCurrent) && | |
| 765 this != highlightedNode && | |
| 766 this != focusNode | |
| 767 ) | |
| 768 { | |
| 769 if ( this.radial != this.radialPrev && this.alphaLabel.end == 1 ) | |
| 770 { | |
| 771 context.globalAlpha = tweenFactor; | |
| 772 } | |
| 773 else | |
| 774 { | |
| 775 context.globalAlpha = this.alphaLabel.current(); | |
| 776 } | |
| 777 | |
| 778 this.drawLabel | |
| 779 ( | |
| 780 (angleStartCurrent + angleEndCurrent) / 2, | |
| 781 this.hideAlone && this.searchResultChildren() || | |
| 782 (this.isSearchResult || hiddenSearchResults) && selected, | |
| 783 this == selectedNode && ! this.radial, | |
| 784 selected, | |
| 785 this.radial | |
| 786 ); | |
| 787 | |
| 788 if ( this.radial != this.radialPrev && this.alphaLabel.start == 1 && progress < 1 ) | |
| 789 { | |
| 790 context.globalAlpha = 1 - tweenFactor; | |
| 791 | |
| 792 this.drawLabel | |
| 793 ( | |
| 794 (angleStartCurrent + angleEndCurrent) / 2, | |
| 795 (this.isSearchResult || hiddenSearchResults) && selected, | |
| 796 this == selectedNodeLast && ! this.radialPrev, | |
| 797 selected, | |
| 798 this.radialPrev | |
| 799 ); | |
| 800 } | |
| 801 } | |
| 802 | |
| 803 if | |
| 804 ( | |
| 805 alphaOtherCurrent && | |
| 806 lastChildAngleEnd != null | |
| 807 ) | |
| 808 { | |
| 809 if | |
| 810 ( | |
| 811 (angleEndCurrent - lastChildAngleEnd) * | |
| 812 (childRadiusInner + gRadius) >= | |
| 813 minWidth() | |
| 814 ) | |
| 815 { | |
| 816 //context.font = fontNormal; | |
| 817 context.globalAlpha = this.alphaOther.current(); | |
| 818 | |
| 819 drawTextPolar | |
| 820 ( | |
| 821 this.getUnclassifiedText(), | |
| 822 this.getUnclassifiedPercentage(), | |
| 823 (lastChildAngleEnd + angleEndCurrent) / 2, | |
| 824 (childRadiusInner + gRadius) / 2, | |
| 825 true, | |
| 826 false, | |
| 827 false, | |
| 828 0, | |
| 829 0 | |
| 830 ); | |
| 831 } | |
| 832 } | |
| 833 | |
| 834 if ( this == selectedNode && this.keyUnclassified && showKeys ) | |
| 835 { | |
| 836 this.drawKey | |
| 837 ( | |
| 838 (lastChildAngleEnd + angleEndCurrent) / 2, | |
| 839 false, | |
| 840 false | |
| 841 ); | |
| 842 } | |
| 843 } | |
| 844 else | |
| 845 { | |
| 846 var alphaWedgeCurrent = this.alphaWedge.current(); | |
| 847 | |
| 848 if ( alphaWedgeCurrent || this.alphaOther.current() ) | |
| 849 { | |
| 850 var currentR = this.r.current(); | |
| 851 var currentG = this.g.current(); | |
| 852 var currentB = this.b.current(); | |
| 853 | |
| 854 var fill = rgbText(currentR, currentG, currentB); | |
| 855 | |
| 856 var radiusOuter; | |
| 857 var lastChildAngle; | |
| 858 var truncateWedge = | |
| 859 ( | |
| 860 this.hasChildren() && | |
| 861 ! this.keyed && | |
| 862 (compress || depth < maxDisplayDepth) && | |
| 863 drawChildren | |
| 864 ); | |
| 865 | |
| 866 if ( truncateWedge ) | |
| 867 { | |
| 868 radiusOuter = this.children[0].radiusInner.current() * gRadius; | |
| 869 } | |
| 870 else | |
| 871 { | |
| 872 radiusOuter = gRadius; | |
| 873 } | |
| 874 /* | |
| 875 if ( this.hasChildren() ) | |
| 876 { | |
| 877 radiusOuter = this.children[0].getUncollapsed().radiusInner.current() * gRadius + 1; | |
| 878 } | |
| 879 else | |
| 880 { // TEMP | |
| 881 radiusOuter = radiusInner + nodeRadius * gRadius; | |
| 882 | |
| 883 if ( radiusOuter > gRadius ) | |
| 884 { | |
| 885 radiusOuter = gRadius; | |
| 886 } | |
| 887 } | |
| 888 */ | |
| 889 context.globalAlpha = alphaWedgeCurrent; | |
| 890 | |
| 891 if ( radiusInner != radiusOuter ) | |
| 892 { | |
| 893 drawWedge | |
| 894 ( | |
| 895 angleStartCurrent, | |
| 896 angleEndCurrent, | |
| 897 radiusInner, | |
| 898 radiusOuter,//this.radiusOuter.current() * gRadius, | |
| 899 //'rgba(0, 200, 0, .1)', | |
| 900 fill, | |
| 901 this.alphaPattern.current() | |
| 902 ); | |
| 903 | |
| 904 if ( truncateWedge ) | |
| 905 { | |
| 906 // fill in the extra space if the sum of our childrens' | |
| 907 // magnitudes is less than ours | |
| 908 | |
| 909 if ( lastChildAngleEnd < angleEndCurrent )//&& false) // TEMP | |
| 910 { | |
| 911 if ( radiusOuter > 1 ) | |
| 912 { | |
| 913 // overlap slightly to hide the seam | |
| 914 | |
| 915 // radiusOuter -= 1; | |
| 916 } | |
| 917 | |
| 918 if ( alphaWedgeCurrent < 1 ) | |
| 919 { | |
| 920 context.globalAlpha = this.alphaOther.current(); | |
| 921 drawWedge | |
| 922 ( | |
| 923 lastChildAngleEnd, | |
| 924 angleEndCurrent, | |
| 925 radiusOuter, | |
| 926 gRadius, | |
| 927 colorUnclassified, | |
| 928 0 | |
| 929 ); | |
| 930 context.globalAlpha = alphaWedgeCurrent; | |
| 931 } | |
| 932 | |
| 933 drawWedge | |
| 934 ( | |
| 935 lastChildAngleEnd, | |
| 936 angleEndCurrent, | |
| 937 radiusOuter, | |
| 938 gRadius,//this.radiusOuter.current() * gRadius, | |
| 939 //'rgba(200, 0, 0, .1)', | |
| 940 fill, | |
| 941 this.alphaPattern.current() | |
| 942 ); | |
| 943 } | |
| 944 } | |
| 945 | |
| 946 if ( radiusOuter < gRadius ) | |
| 947 { | |
| 948 // patch up the seam | |
| 949 // | |
| 950 context.beginPath(); | |
| 951 context.arc(0, 0, radiusOuter, angleStartCurrent/*lastChildAngleEnd*/, angleEndCurrent, false); | |
| 952 context.strokeStyle = fill; | |
| 953 context.lineWidth = 1; | |
| 954 context.stroke(); | |
| 955 } | |
| 956 } | |
| 957 | |
| 958 if ( this.keyed && selected && showKeys )//&& progress == 1 ) | |
| 959 { | |
| 960 this.drawKey | |
| 961 ( | |
| 962 (angleStartCurrent + angleEndCurrent) / 2, | |
| 963 ( | |
| 964 this == highlightedNode || | |
| 965 this == focusNode || | |
| 966 this.searchResults | |
| 967 ), | |
| 968 this == highlightedNode || this == focusNode | |
| 969 ); | |
| 970 } | |
| 971 } | |
| 972 } | |
| 973 } | |
| 974 | |
| 975 if ( drawChildren ) | |
| 976 { | |
| 977 // draw children | |
| 978 // | |
| 979 for ( var i = 0; i < this.children.length; i++ ) | |
| 980 { | |
| 981 if ( this.drawHiddenChildren(i, selected, labelMode, searchHighlighted) ) | |
| 982 { | |
| 983 i = this.children[i].hiddenEnd; | |
| 984 } | |
| 985 else | |
| 986 { | |
| 987 this.children[i].draw(labelMode, selected, searchHighlighted); | |
| 988 } | |
| 989 } | |
| 990 } | |
| 991 }; | |
| 992 | |
| 993 this.drawHiddenChildren = function | |
| 994 ( | |
| 995 firstHiddenChild, | |
| 996 selected, | |
| 997 labelMode, | |
| 998 searchHighlighted | |
| 999 ) | |
| 1000 { | |
| 1001 var firstChild = this.children[firstHiddenChild]; | |
| 1002 | |
| 1003 if ( firstChild.hiddenEnd == null || firstChild.radiusInner.current() == 1 ) | |
| 1004 { | |
| 1005 return false; | |
| 1006 } | |
| 1007 | |
| 1008 for ( var i = firstHiddenChild; i < firstChild.hiddenEnd; i++ ) | |
| 1009 { | |
| 1010 if ( ! this.children[i].hide || ! this.children[i].hidePrev && progress < 1 ) | |
| 1011 { | |
| 1012 return false; | |
| 1013 } | |
| 1014 } | |
| 1015 | |
| 1016 var angleStart = firstChild.angleStart.current() + rotationOffset; | |
| 1017 var lastChild = this.children[firstChild.hiddenEnd]; | |
| 1018 var angleEnd = lastChild.angleEnd.current() + rotationOffset; | |
| 1019 var radiusInner = gRadius * firstChild.radiusInner.current(); | |
| 1020 var hiddenChildren = firstChild.hiddenEnd - firstHiddenChild + 1; | |
| 1021 | |
| 1022 if ( labelMode ) | |
| 1023 { | |
| 1024 var hiddenSearchResults = 0; | |
| 1025 | |
| 1026 for ( var i = firstHiddenChild; i <= firstChild.hiddenEnd; i++ ) | |
| 1027 { | |
| 1028 hiddenSearchResults += this.children[i].searchResults; | |
| 1029 | |
| 1030 if ( this.children[i].magnitude == 0 ) | |
| 1031 { | |
| 1032 hiddenChildren--; | |
| 1033 } | |
| 1034 } | |
| 1035 | |
| 1036 if | |
| 1037 ( | |
| 1038 selected && | |
| 1039 (angleEnd - angleStart) * | |
| 1040 (gRadius + gRadius) >= | |
| 1041 minWidth() || | |
| 1042 this == highlightedNode && | |
| 1043 hiddenChildren || | |
| 1044 hiddenSearchResults | |
| 1045 ) | |
| 1046 { | |
| 1047 context.globalAlpha = this.alphaWedge.current(); | |
| 1048 | |
| 1049 this.drawHiddenLabel | |
| 1050 ( | |
| 1051 angleStart, | |
| 1052 angleEnd, | |
| 1053 hiddenChildren, | |
| 1054 hiddenSearchResults | |
| 1055 ); | |
| 1056 } | |
| 1057 } | |
| 1058 | |
| 1059 var drawWedges = true; | |
| 1060 | |
| 1061 for ( var i = firstHiddenChild; i <= firstChild.hiddenEnd; i++ ) | |
| 1062 { | |
| 1063 // all hidden children must be completely hidden to draw together | |
| 1064 | |
| 1065 if ( this.children[i].alphaPattern.current() != this.children[i].alphaWedge.current() ) | |
| 1066 { | |
| 1067 drawWedges = false; | |
| 1068 break; | |
| 1069 } | |
| 1070 } | |
| 1071 | |
| 1072 if ( labelMode ) | |
| 1073 { | |
| 1074 if ( drawWedges ) | |
| 1075 { | |
| 1076 var drawRadial = (angleEnd < this.angleEnd.current() + rotationOffset); | |
| 1077 this.drawLines(angleStart, angleEnd, radiusInner, drawRadial); | |
| 1078 } | |
| 1079 | |
| 1080 if ( hiddenSearchResults && ! searchHighlighted ) | |
| 1081 { | |
| 1082 drawWedge | |
| 1083 ( | |
| 1084 angleStart, | |
| 1085 angleEnd, | |
| 1086 radiusInner, | |
| 1087 gRadius,//this.radiusOuter.current() * gRadius, | |
| 1088 highlightFill, | |
| 1089 0, | |
| 1090 true | |
| 1091 ); | |
| 1092 } | |
| 1093 } | |
| 1094 else if ( drawWedges ) | |
| 1095 { | |
| 1096 context.globalAlpha = this.alphaWedge.current(); | |
| 1097 | |
| 1098 var fill = rgbText | |
| 1099 ( | |
| 1100 firstChild.r.current(), | |
| 1101 firstChild.g.current(), | |
| 1102 firstChild.b.current() | |
| 1103 ); | |
| 1104 | |
| 1105 drawWedge | |
| 1106 ( | |
| 1107 angleStart, | |
| 1108 angleEnd, | |
| 1109 radiusInner, | |
| 1110 gRadius,//this.radiusOuter.current() * gRadius, | |
| 1111 fill, | |
| 1112 context.globalAlpha, | |
| 1113 false | |
| 1114 ); | |
| 1115 } | |
| 1116 | |
| 1117 return drawWedges; | |
| 1118 } | |
| 1119 | |
| 1120 this.drawHiddenLabel = function(angleStart, angleEnd, value, hiddenSearchResults) | |
| 1121 { | |
| 1122 var textAngle = (angleStart + angleEnd) / 2; | |
| 1123 var labelRadius = gRadius + fontSize;//(radiusInner + radius) / 2; | |
| 1124 | |
| 1125 drawTick(gRadius - fontSize * .75, fontSize * 1.5, textAngle); | |
| 1126 drawTextPolar | |
| 1127 ( | |
| 1128 value.toString() + ' more', | |
| 1129 0, // inner text | |
| 1130 textAngle, | |
| 1131 labelRadius, | |
| 1132 true, // radial | |
| 1133 hiddenSearchResults, // bubble | |
| 1134 this == highlightedNode || this == focusNode, // bold | |
| 1135 false, | |
| 1136 hiddenSearchResults | |
| 1137 ); | |
| 1138 } | |
| 1139 | |
| 1140 this.drawHighlight = function(bold) | |
| 1141 { | |
| 1142 var angleStartCurrent = this.angleStart.current() + rotationOffset; | |
| 1143 var angleEndCurrent = this.angleEnd.current() + rotationOffset; | |
| 1144 var radiusInner = this.radiusInner.current() * gRadius; | |
| 1145 | |
| 1146 //this.setHighlightStyle(); | |
| 1147 | |
| 1148 if ( this == focusNode && this == highlightedNode && this.hasChildren() ) | |
| 1149 { | |
| 1150 // context.fillStyle = "rgba(255, 255, 255, .3)"; | |
| 1151 arrow | |
| 1152 ( | |
| 1153 angleStartCurrent, | |
| 1154 angleEndCurrent, | |
| 1155 radiusInner | |
| 1156 ); | |
| 1157 } | |
| 1158 else | |
| 1159 { | |
| 1160 drawWedge | |
| 1161 ( | |
| 1162 angleStartCurrent, | |
| 1163 angleEndCurrent, | |
| 1164 radiusInner, | |
| 1165 gRadius, | |
| 1166 highlightFill, | |
| 1167 0, | |
| 1168 true | |
| 1169 ); | |
| 1170 } | |
| 1171 | |
| 1172 // check if hidden children should be highlighted | |
| 1173 // | |
| 1174 for ( var i = 0; i < this.children.length; i++ ) | |
| 1175 { | |
| 1176 if | |
| 1177 ( | |
| 1178 this.children[i].getDepth() - selectedNode.getDepth() + 1 <= | |
| 1179 maxDisplayDepth && | |
| 1180 this.children[i].hiddenEnd != null | |
| 1181 ) | |
| 1182 { | |
| 1183 var firstChild = this.children[i]; | |
| 1184 var lastChild = this.children[firstChild.hiddenEnd]; | |
| 1185 var hiddenAngleStart = firstChild.angleStart.current() + rotationOffset; | |
| 1186 var hiddenAngleEnd = lastChild.angleEnd.current() + rotationOffset; | |
| 1187 var hiddenRadiusInner = gRadius * firstChild.radiusInner.current(); | |
| 1188 | |
| 1189 drawWedge | |
| 1190 ( | |
| 1191 hiddenAngleStart, | |
| 1192 hiddenAngleEnd, | |
| 1193 hiddenRadiusInner, | |
| 1194 gRadius, | |
| 1195 'rgba(255, 255, 255, .3)', | |
| 1196 0, | |
| 1197 true | |
| 1198 ); | |
| 1199 | |
| 1200 if ( false && ! this.searchResults ) | |
| 1201 { | |
| 1202 this.drawHiddenLabel | |
| 1203 ( | |
| 1204 hiddenAngleStart, | |
| 1205 hiddenAngleEnd, | |
| 1206 firstChild.hiddenEnd - i + 1 | |
| 1207 ); | |
| 1208 } | |
| 1209 | |
| 1210 i = firstChild.hiddenEnd; | |
| 1211 } | |
| 1212 } | |
| 1213 | |
| 1214 // context.strokeStyle = 'black'; | |
| 1215 context.fillStyle = 'black'; | |
| 1216 | |
| 1217 var highlight = ! ( progress < 1 && zoomOut && this == selectedNodeLast ); | |
| 1218 | |
| 1219 var angle = (angleEndCurrent + angleStartCurrent) / 2; | |
| 1220 | |
| 1221 if ( ! (this.keyed && showKeys) ) | |
| 1222 { | |
| 1223 this.drawLabel(angle, true, bold, true, this.radial); | |
| 1224 } | |
| 1225 } | |
| 1226 | |
| 1227 this.drawHighlightCenter = function() | |
| 1228 { | |
| 1229 if ( ! this.canDisplayHistory() ) | |
| 1230 { | |
| 1231 return; | |
| 1232 } | |
| 1233 | |
| 1234 context.lineWidth = highlightLineWidth; | |
| 1235 context.strokeStyle = 'black'; | |
| 1236 context.fillStyle = "rgba(255, 255, 255, .6)"; | |
| 1237 | |
| 1238 context.fillStyle = 'black'; | |
| 1239 this.drawLabel(3 * Math.PI / 2, true, true, false); | |
| 1240 context.font = fontNormal; | |
| 1241 } | |
| 1242 | |
| 1243 this.drawKey = function(angle, highlight, bold) | |
| 1244 { | |
| 1245 var offset = keyOffset(); | |
| 1246 var color; | |
| 1247 var colorText = this.magnitude == 0 ? 'gray' : 'black'; | |
| 1248 var patternAlpha = this.alphaPattern.end; | |
| 1249 var boxLeft = imageWidth - keySize - margin; | |
| 1250 var textY = offset + keySize / 2; | |
| 1251 | |
| 1252 var label; | |
| 1253 var keyNameWidth; | |
| 1254 | |
| 1255 if ( this == selectedNode ) | |
| 1256 { | |
| 1257 color = colorUnclassified; | |
| 1258 label = | |
| 1259 this.getUnclassifiedText() + | |
| 1260 ' ' + | |
| 1261 this.getUnclassifiedPercentage(); | |
| 1262 keyNameWidth = measureText(label, false); | |
| 1263 } | |
| 1264 else | |
| 1265 { | |
| 1266 label = this.keyLabel; | |
| 1267 color = rgbText(this.r.end, this.g.end, this.b.end); | |
| 1268 | |
| 1269 if ( highlight ) | |
| 1270 { | |
| 1271 if ( this.searchResultChildren() ) | |
| 1272 { | |
| 1273 label = label + searchResultString(this.searchResultChildren()); | |
| 1274 } | |
| 1275 | |
| 1276 keyNameWidth = measureText(label, bold); | |
| 1277 } | |
| 1278 else | |
| 1279 { | |
| 1280 keyNameWidth = this.keyNameWidth; | |
| 1281 } | |
| 1282 } | |
| 1283 | |
| 1284 var textLeft = boxLeft - keyBuffer - keyNameWidth - fontSize / 2; | |
| 1285 var labelLeft = textLeft; | |
| 1286 | |
| 1287 if ( labelLeft > keyMinTextLeft - fontSize / 2 ) | |
| 1288 { | |
| 1289 keyMinTextLeft -= fontSize / 2; | |
| 1290 | |
| 1291 if ( keyMinTextLeft < centerX - gRadius + fontSize / 2 ) | |
| 1292 { | |
| 1293 keyMinTextLeft = centerX - gRadius + fontSize / 2; | |
| 1294 } | |
| 1295 | |
| 1296 labelLeft = keyMinTextLeft; | |
| 1297 } | |
| 1298 | |
| 1299 var lineX = new Array(); | |
| 1300 var lineY = new Array(); | |
| 1301 | |
| 1302 var bendRadius; | |
| 1303 var keyAngle = Math.atan((textY - centerY) / (labelLeft - centerX)); | |
| 1304 var arcAngle; | |
| 1305 | |
| 1306 if ( keyAngle < 0 ) | |
| 1307 { | |
| 1308 keyAngle += Math.PI; | |
| 1309 } | |
| 1310 | |
| 1311 if ( keyMinAngle == 0 || angle < keyMinAngle ) | |
| 1312 { | |
| 1313 keyMinAngle = angle; | |
| 1314 } | |
| 1315 | |
| 1316 if ( angle > Math.PI && keyMinAngle > Math.PI ) | |
| 1317 { | |
| 1318 // allow lines to come underneath the chart | |
| 1319 | |
| 1320 angle -= Math.PI * 2; | |
| 1321 } | |
| 1322 | |
| 1323 lineX.push(Math.cos(angle) * gRadius); | |
| 1324 lineY.push(Math.sin(angle) * gRadius); | |
| 1325 | |
| 1326 if ( angle < keyAngle && textY > centerY + Math.sin(angle) * (gRadius + buffer * (currentKey - 1) / (keys + 1) / 2 + buffer / 2) ) | |
| 1327 { | |
| 1328 bendRadius = gRadius + buffer - buffer * currentKey / (keys + 1) / 2; | |
| 1329 } | |
| 1330 else | |
| 1331 { | |
| 1332 bendRadius = gRadius + buffer * currentKey / (keys + 1) / 2 + buffer / 2; | |
| 1333 } | |
| 1334 | |
| 1335 var outside = | |
| 1336 Math.sqrt | |
| 1337 ( | |
| 1338 Math.pow(labelLeft - centerX, 2) + | |
| 1339 Math.pow(textY - centerY, 2) | |
| 1340 ) > bendRadius; | |
| 1341 | |
| 1342 if ( ! outside ) | |
| 1343 { | |
| 1344 arcAngle = Math.asin((textY - centerY) / bendRadius); | |
| 1345 | |
| 1346 keyMinTextLeft = min(keyMinTextLeft, centerX + bendRadius * Math.cos(arcAngle) - fontSize / 2); | |
| 1347 | |
| 1348 if ( labelLeft < textLeft && textLeft > centerX + bendRadius * Math.cos(arcAngle) ) | |
| 1349 { | |
| 1350 lineX.push(textLeft - centerX); | |
| 1351 lineY.push(textY - centerY); | |
| 1352 } | |
| 1353 } | |
| 1354 else | |
| 1355 { | |
| 1356 keyMinTextLeft = min(keyMinTextLeft, labelLeft - fontSize / 2); | |
| 1357 | |
| 1358 if ( angle < keyAngle ) | |
| 1359 { | |
| 1360 // flip everything over y = x | |
| 1361 // | |
| 1362 arcAngle = Math.PI / 2 - keyLineAngle | |
| 1363 ( | |
| 1364 Math.PI / 2 - angle, | |
| 1365 Math.PI / 2 - keyAngle, | |
| 1366 bendRadius, | |
| 1367 textY - centerY, | |
| 1368 labelLeft - centerX, | |
| 1369 lineY, | |
| 1370 lineX | |
| 1371 ); | |
| 1372 | |
| 1373 } | |
| 1374 else | |
| 1375 { | |
| 1376 arcAngle = keyLineAngle | |
| 1377 ( | |
| 1378 angle, | |
| 1379 keyAngle, | |
| 1380 bendRadius, | |
| 1381 labelLeft - centerX, | |
| 1382 textY - centerY, | |
| 1383 lineX, | |
| 1384 lineY | |
| 1385 ); | |
| 1386 } | |
| 1387 } | |
| 1388 | |
| 1389 if ( labelLeft > centerX + bendRadius * Math.cos(arcAngle) || | |
| 1390 textY > centerY + bendRadius * Math.sin(arcAngle) + .01) | |
| 1391 // if ( outside || ) | |
| 1392 { | |
| 1393 lineX.push(labelLeft - centerX); | |
| 1394 lineY.push(textY - centerY); | |
| 1395 | |
| 1396 if ( textLeft != labelLeft ) | |
| 1397 { | |
| 1398 lineX.push(textLeft - centerX); | |
| 1399 lineY.push(textY - centerY); | |
| 1400 } | |
| 1401 } | |
| 1402 | |
| 1403 context.globalAlpha = this.alphaWedge.current(); | |
| 1404 | |
| 1405 if ( snapshotMode ) | |
| 1406 { | |
| 1407 var labelSVG; | |
| 1408 | |
| 1409 if ( this == selectedNode ) | |
| 1410 { | |
| 1411 labelSVG = | |
| 1412 this.getUnclassifiedText() + | |
| 1413 spacer() + | |
| 1414 this.getUnclassifiedPercentage(); | |
| 1415 } | |
| 1416 else | |
| 1417 { | |
| 1418 labelSVG = this.name + spacer() + this.getPercentage() + '%'; | |
| 1419 } | |
| 1420 | |
| 1421 svg += | |
| 1422 '<rect fill="' + color + '" ' + | |
| 1423 'x="' + boxLeft + '" y="' + offset + | |
| 1424 '" width="' + keySize + '" height="' + keySize + '"/>'; | |
| 1425 | |
| 1426 if ( patternAlpha ) | |
| 1427 { | |
| 1428 svg += | |
| 1429 '<rect fill="url(#hiddenPattern)" style="stroke:none" ' + | |
| 1430 'x="' + boxLeft + '" y="' + offset + | |
| 1431 '" width="' + keySize + '" height="' + keySize + '"/>'; | |
| 1432 } | |
| 1433 | |
| 1434 svg += | |
| 1435 '<path class="line' + | |
| 1436 (highlight ? ' highlight' : '') + | |
| 1437 '" d="M ' + (lineX[0] + centerX) + ',' + | |
| 1438 (lineY[0] + centerY); | |
| 1439 | |
| 1440 if ( angle != arcAngle ) | |
| 1441 { | |
| 1442 svg += | |
| 1443 ' L ' + (centerX + bendRadius * Math.cos(angle)) + ',' + | |
| 1444 (centerY + bendRadius * Math.sin(angle)) + | |
| 1445 ' A ' + bendRadius + ',' + bendRadius + ' 0 ' + | |
| 1446 '0,' + (angle > arcAngle ? '0' : '1') + ' ' + | |
| 1447 (centerX + bendRadius * Math.cos(arcAngle)) + ',' + | |
| 1448 (centerY + bendRadius * Math.sin(arcAngle)); | |
| 1449 } | |
| 1450 | |
| 1451 for ( var i = 1; i < lineX.length; i++ ) | |
| 1452 { | |
| 1453 svg += | |
| 1454 ' L ' + (centerX + lineX[i]) + ',' + | |
| 1455 (centerY + lineY[i]); | |
| 1456 } | |
| 1457 | |
| 1458 svg += '"/>'; | |
| 1459 | |
| 1460 if ( highlight ) | |
| 1461 { | |
| 1462 if ( this.searchResultChildren() ) | |
| 1463 { | |
| 1464 labelSVG = labelSVG + searchResultString(this.searchResultChildren()); | |
| 1465 } | |
| 1466 | |
| 1467 drawBubbleSVG | |
| 1468 ( | |
| 1469 boxLeft - keyBuffer - keyNameWidth - fontSize / 2, | |
| 1470 textY - fontSize, | |
| 1471 keyNameWidth + fontSize, | |
| 1472 fontSize * 2, | |
| 1473 fontSize, | |
| 1474 0 | |
| 1475 ); | |
| 1476 | |
| 1477 if ( this.isSearchResult ) | |
| 1478 { | |
| 1479 drawSearchHighlights | |
| 1480 ( | |
| 1481 label, | |
| 1482 boxLeft - keyBuffer - keyNameWidth, | |
| 1483 textY, | |
| 1484 0 | |
| 1485 ) | |
| 1486 } | |
| 1487 } | |
| 1488 | |
| 1489 svg += svgText(labelSVG, boxLeft - keyBuffer, textY, 'end', bold, colorText); | |
| 1490 } | |
| 1491 else | |
| 1492 { | |
| 1493 context.fillStyle = color; | |
| 1494 context.translate(-centerX, -centerY); | |
| 1495 context.strokeStyle = 'black'; | |
| 1496 context.globalAlpha = 1;//this.alphaWedge.current(); | |
| 1497 | |
| 1498 context.fillRect(boxLeft, offset, keySize, keySize); | |
| 1499 | |
| 1500 if ( patternAlpha ) | |
| 1501 { | |
| 1502 context.globalAlpha = patternAlpha; | |
| 1503 context.fillStyle = hiddenPattern; | |
| 1504 | |
| 1505 // make clipping box for Firefox performance | |
| 1506 context.beginPath(); | |
| 1507 context.moveTo(boxLeft, offset); | |
| 1508 context.lineTo(boxLeft + keySize, offset); | |
| 1509 context.lineTo(boxLeft + keySize, offset + keySize); | |
| 1510 context.lineTo(boxLeft, offset + keySize); | |
| 1511 context.closePath(); | |
| 1512 context.save(); | |
| 1513 context.clip(); | |
| 1514 | |
| 1515 context.fillRect(boxLeft, offset, keySize, keySize); | |
| 1516 context.fillRect(boxLeft, offset, keySize, keySize); | |
| 1517 | |
| 1518 context.restore(); // remove clipping region | |
| 1519 } | |
| 1520 | |
| 1521 if ( highlight ) | |
| 1522 { | |
| 1523 this.setHighlightStyle(); | |
| 1524 context.fillRect(boxLeft, offset, keySize, keySize); | |
| 1525 } | |
| 1526 else | |
| 1527 { | |
| 1528 context.lineWidth = thinLineWidth; | |
| 1529 } | |
| 1530 | |
| 1531 context.strokeRect(boxLeft, offset, keySize, keySize); | |
| 1532 | |
| 1533 if ( lineX.length ) | |
| 1534 { | |
| 1535 context.beginPath(); | |
| 1536 context.moveTo(lineX[0] + centerX, lineY[0] + centerY); | |
| 1537 | |
| 1538 context.arc(centerX, centerY, bendRadius, angle, arcAngle, angle > arcAngle); | |
| 1539 | |
| 1540 for ( var i = 1; i < lineX.length; i++ ) | |
| 1541 { | |
| 1542 context.lineTo(lineX[i] + centerX, lineY[i] + centerY); | |
| 1543 } | |
| 1544 | |
| 1545 context.globalAlpha = this == selectedNode ? | |
| 1546 this.children[0].alphaWedge.current() : | |
| 1547 this.alphaWedge.current(); | |
| 1548 context.lineWidth = highlight ? highlightLineWidth : thinLineWidth; | |
| 1549 context.stroke(); | |
| 1550 context.globalAlpha = 1; | |
| 1551 } | |
| 1552 | |
| 1553 if ( highlight ) | |
| 1554 { | |
| 1555 drawBubbleCanvas | |
| 1556 ( | |
| 1557 boxLeft - keyBuffer - keyNameWidth - fontSize / 2, | |
| 1558 textY - fontSize, | |
| 1559 keyNameWidth + fontSize, | |
| 1560 fontSize * 2, | |
| 1561 fontSize, | |
| 1562 0 | |
| 1563 ); | |
| 1564 | |
| 1565 if ( this.isSearchResult ) | |
| 1566 { | |
| 1567 drawSearchHighlights | |
| 1568 ( | |
| 1569 label, | |
| 1570 boxLeft - keyBuffer - keyNameWidth, | |
| 1571 textY, | |
| 1572 0 | |
| 1573 ) | |
| 1574 } | |
| 1575 } | |
| 1576 | |
| 1577 drawText(label, boxLeft - keyBuffer, offset + keySize / 2, 0, 'end', bold, colorText); | |
| 1578 | |
| 1579 context.translate(centerX, centerY); | |
| 1580 } | |
| 1581 | |
| 1582 currentKey++; | |
| 1583 } | |
| 1584 | |
| 1585 this.drawLabel = function(angle, bubble, bold, selected, radial) | |
| 1586 { | |
| 1587 if ( context.globalAlpha == 0 ) | |
| 1588 { | |
| 1589 return; | |
| 1590 } | |
| 1591 | |
| 1592 var innerText; | |
| 1593 var label; | |
| 1594 var radius; | |
| 1595 | |
| 1596 if ( radial ) | |
| 1597 { | |
| 1598 radius = (this.radiusInner.current() + 1) * gRadius / 2; | |
| 1599 } | |
| 1600 else | |
| 1601 { | |
| 1602 radius = this.labelRadius.current() * gRadius; | |
| 1603 } | |
| 1604 | |
| 1605 if ( radial && (selected || bubble ) ) | |
| 1606 { | |
| 1607 var percentage = this.getPercentage(); | |
| 1608 innerText = percentage + '%'; | |
| 1609 } | |
| 1610 | |
| 1611 if | |
| 1612 ( | |
| 1613 ! radial && | |
| 1614 this != selectedNode && | |
| 1615 ! bubble && | |
| 1616 ( !zoomOut || this != selectedNodeLast) | |
| 1617 ) | |
| 1618 { | |
| 1619 label = this.shortenLabel(); | |
| 1620 } | |
| 1621 else | |
| 1622 { | |
| 1623 label = this.name; | |
| 1624 } | |
| 1625 | |
| 1626 var flipped = drawTextPolar | |
| 1627 ( | |
| 1628 label, | |
| 1629 innerText, | |
| 1630 angle, | |
| 1631 radius, | |
| 1632 radial, | |
| 1633 bubble, | |
| 1634 bold, | |
| 1635 // this.isSearchResult && this.shouldAddSearchResultsString() && (!selected || this == selectedNode || highlight), | |
| 1636 this.isSearchResult && (!selected || this == selectedNode || bubble), | |
| 1637 (this.hideAlone || !selected || this == selectedNode ) ? this.searchResultChildren() : 0 | |
| 1638 ); | |
| 1639 | |
| 1640 var depth = this.getDepth() - selectedNode.getDepth() + 1; | |
| 1641 | |
| 1642 if | |
| 1643 ( | |
| 1644 ! radial && | |
| 1645 ! bubble && | |
| 1646 this != selectedNode && | |
| 1647 this.angleEnd.end != this.angleStart.end && | |
| 1648 nLabelOffsets[depth - 2] > 2 && | |
| 1649 this.labelWidth.current() > (this.angleEnd.end - this.angleStart.end) * Math.abs(radius) && | |
| 1650 ! ( zoomOut && this == selectedNodeLast ) && | |
| 1651 this.labelRadius.end > 0 | |
| 1652 ) | |
| 1653 { | |
| 1654 // name extends beyond wedge; draw tick mark towards the central | |
| 1655 // radius for easier identification | |
| 1656 | |
| 1657 var radiusCenter = compress ? | |
| 1658 (compressedRadii[depth - 1] + compressedRadii[depth - 2]) / 2 : | |
| 1659 (depth - .5) * nodeRadius; | |
| 1660 | |
| 1661 if ( this.labelRadius.end > radiusCenter ) | |
| 1662 { | |
| 1663 if ( flipped ) | |
| 1664 { | |
| 1665 drawTick(radius - tickLength * 1.4 , tickLength, angle); | |
| 1666 } | |
| 1667 else | |
| 1668 { | |
| 1669 drawTick(radius - tickLength * 1.7, tickLength, angle); | |
| 1670 } | |
| 1671 } | |
| 1672 else | |
| 1673 { | |
| 1674 if ( flipped ) | |
| 1675 { | |
| 1676 drawTick(radius + tickLength * .7, tickLength, angle); | |
| 1677 } | |
| 1678 else | |
| 1679 { | |
| 1680 drawTick(radius + tickLength * .4, tickLength, angle); | |
| 1681 } | |
| 1682 } | |
| 1683 } | |
| 1684 } | |
| 1685 | |
| 1686 this.drawLines = function(angleStart, angleEnd, radiusInner, drawRadial, selected) | |
| 1687 { | |
| 1688 if ( snapshotMode ) | |
| 1689 { | |
| 1690 if ( this != selectedNode) | |
| 1691 { | |
| 1692 if ( angleEnd == angleStart + Math.PI * 2 ) | |
| 1693 { | |
| 1694 // fudge to prevent overlap, which causes arc ambiguity | |
| 1695 // | |
| 1696 angleEnd -= .1 / gRadius; | |
| 1697 } | |
| 1698 | |
| 1699 var longArc = angleEnd - angleStart > Math.PI ? 1 : 0; | |
| 1700 | |
| 1701 var x1 = centerX + radiusInner * Math.cos(angleStart); | |
| 1702 var y1 = centerY + radiusInner * Math.sin(angleStart); | |
| 1703 | |
| 1704 var x2 = centerX + gRadius * Math.cos(angleStart); | |
| 1705 var y2 = centerY + gRadius * Math.sin(angleStart); | |
| 1706 | |
| 1707 var x3 = centerX + gRadius * Math.cos(angleEnd); | |
| 1708 var y3 = centerY + gRadius * Math.sin(angleEnd); | |
| 1709 | |
| 1710 var x4 = centerX + radiusInner * Math.cos(angleEnd); | |
| 1711 var y4 = centerY + radiusInner * Math.sin(angleEnd); | |
| 1712 | |
| 1713 if ( this.alphaArc.end ) | |
| 1714 { | |
| 1715 var dArray = | |
| 1716 [ | |
| 1717 " M ", x4, ",", y4, | |
| 1718 " A ", radiusInner, ",", radiusInner, " 0 ", longArc, | |
| 1719 " 0 ", x1, ",", y1 | |
| 1720 ]; | |
| 1721 | |
| 1722 svg += '<path class="line" d="' + dArray.join('') + '"/>'; | |
| 1723 } | |
| 1724 | |
| 1725 if ( drawRadial && this.alphaLine.end ) | |
| 1726 { | |
| 1727 svg += '<line x1="' + x3 + '" y1="' + y3 + '" x2="' + x4 + '" y2="' + y4 + '"/>'; | |
| 1728 } | |
| 1729 } | |
| 1730 } | |
| 1731 else | |
| 1732 { | |
| 1733 context.lineWidth = thinLineWidth; | |
| 1734 context.strokeStyle = 'black'; | |
| 1735 context.beginPath(); | |
| 1736 context.arc(0, 0, radiusInner, angleStart, angleEnd, false); | |
| 1737 context.globalAlpha = this.alphaArc.current(); | |
| 1738 context.stroke(); | |
| 1739 | |
| 1740 if ( drawRadial ) | |
| 1741 { | |
| 1742 var x1 = radiusInner * Math.cos(angleEnd); | |
| 1743 var y1 = radiusInner * Math.sin(angleEnd); | |
| 1744 var x2 = gRadius * Math.cos(angleEnd); | |
| 1745 var y2 = gRadius * Math.sin(angleEnd); | |
| 1746 | |
| 1747 context.beginPath(); | |
| 1748 context.moveTo(x1, y1); | |
| 1749 context.lineTo(x2, y2); | |
| 1750 | |
| 1751 // if ( this.getCollapse() )//( selected && this != selectedNode ) | |
| 1752 { | |
| 1753 context.globalAlpha = this.alphaLine.current(); | |
| 1754 } | |
| 1755 | |
| 1756 context.stroke(); | |
| 1757 } | |
| 1758 } | |
| 1759 } | |
| 1760 | |
| 1761 this.drawMap = function(child) | |
| 1762 { | |
| 1763 if ( this.parent ) | |
| 1764 { | |
| 1765 this.parent.drawMap(child); | |
| 1766 } | |
| 1767 | |
| 1768 if ( this.getCollapse() && this != child || this == focusNode ) | |
| 1769 { | |
| 1770 return; | |
| 1771 } | |
| 1772 | |
| 1773 var angleStart = | |
| 1774 (child.baseMagnitude - this.baseMagnitude) / this.magnitude * Math.PI * 2 + | |
| 1775 rotationOffset; | |
| 1776 var angleEnd = | |
| 1777 (child.baseMagnitude - this.baseMagnitude + child.magnitude) / | |
| 1778 this.magnitude * Math.PI * 2 + | |
| 1779 rotationOffset; | |
| 1780 | |
| 1781 var box = this.getMapPosition(); | |
| 1782 | |
| 1783 context.save(); | |
| 1784 context.fillStyle = 'black'; | |
| 1785 context.textAlign = 'end'; | |
| 1786 context.textBaseline = 'middle'; | |
| 1787 | |
| 1788 var textX = box.x - mapRadius - mapBuffer; | |
| 1789 var percentage = getPercentage(child.magnitude / this.magnitude); | |
| 1790 | |
| 1791 var highlight = this == selectedNode || this == highlightedNode; | |
| 1792 | |
| 1793 if ( highlight ) | |
| 1794 { | |
| 1795 context.font = fontBold; | |
| 1796 } | |
| 1797 else | |
| 1798 { | |
| 1799 context.font = fontNormal; | |
| 1800 } | |
| 1801 | |
| 1802 context.fillText(percentage + '% of', textX, box.y - mapRadius / 3); | |
| 1803 context.fillText(this.name, textX, box.y + mapRadius / 3); | |
| 1804 | |
| 1805 if ( highlight ) | |
| 1806 { | |
| 1807 context.font = fontNormal; | |
| 1808 } | |
| 1809 | |
| 1810 if ( this == highlightedNode && this != selectedNode ) | |
| 1811 { | |
| 1812 context.fillStyle = 'rgb(245, 245, 245)'; | |
| 1813 // context.fillStyle = 'rgb(200, 200, 200)'; | |
| 1814 } | |
| 1815 else | |
| 1816 { | |
| 1817 context.fillStyle = 'rgb(255, 255, 255)'; | |
| 1818 } | |
| 1819 | |
| 1820 context.beginPath(); | |
| 1821 context.arc(box.x, box.y, mapRadius, 0, Math.PI * 2, true); | |
| 1822 context.closePath(); | |
| 1823 context.fill(); | |
| 1824 | |
| 1825 if ( this == selectedNode ) | |
| 1826 { | |
| 1827 context.lineWidth = 1; | |
| 1828 context.fillStyle = 'rgb(100, 100, 100)'; | |
| 1829 } | |
| 1830 else | |
| 1831 { | |
| 1832 if ( this == highlightedNode ) | |
| 1833 { | |
| 1834 context.lineWidth = .2; | |
| 1835 context.fillStyle = 'rgb(190, 190, 190)'; | |
| 1836 } | |
| 1837 else | |
| 1838 { | |
| 1839 context.lineWidth = .2; | |
| 1840 context.fillStyle = 'rgb(200, 200, 200)'; | |
| 1841 } | |
| 1842 } | |
| 1843 | |
| 1844 var maxDepth = this.getMaxDepth(); | |
| 1845 | |
| 1846 if ( ! compress && maxDepth > maxPossibleDepth + this.getDepth() - 1 ) | |
| 1847 { | |
| 1848 maxDepth = maxPossibleDepth + this.getDepth() - 1; | |
| 1849 } | |
| 1850 | |
| 1851 if ( this.getDepth() < selectedNode.getDepth() ) | |
| 1852 { | |
| 1853 if ( child.getDepth() - 1 >= maxDepth ) | |
| 1854 { | |
| 1855 maxDepth = child.getDepth(); | |
| 1856 } | |
| 1857 } | |
| 1858 | |
| 1859 var radiusInner; | |
| 1860 | |
| 1861 if ( compress ) | |
| 1862 { | |
| 1863 radiusInner = 0; | |
| 1864 // Math.atan(child.getDepth() - this.getDepth()) / | |
| 1865 // Math.PI * 2 * .9; | |
| 1866 } | |
| 1867 else | |
| 1868 { | |
| 1869 radiusInner = | |
| 1870 (child.getDepth() - this.getDepth()) / | |
| 1871 (maxDepth - this.getDepth() + 1); | |
| 1872 } | |
| 1873 | |
| 1874 context.stroke(); | |
| 1875 context.beginPath(); | |
| 1876 | |
| 1877 if ( radiusInner == 0 ) | |
| 1878 { | |
| 1879 context.moveTo(box.x, box.y); | |
| 1880 } | |
| 1881 else | |
| 1882 { | |
| 1883 context.arc(box.x, box.y, mapRadius * radiusInner, angleEnd, angleStart, true); | |
| 1884 } | |
| 1885 | |
| 1886 context.arc(box.x, box.y, mapRadius, angleStart, angleEnd, false); | |
| 1887 context.closePath(); | |
| 1888 context.fill(); | |
| 1889 | |
| 1890 if ( this == highlightedNode && this != selectedNode ) | |
| 1891 { | |
| 1892 context.lineWidth = 1; | |
| 1893 context.stroke(); | |
| 1894 } | |
| 1895 | |
| 1896 context.restore(); | |
| 1897 } | |
| 1898 | |
| 1899 this.drawReferenceRings = function(childRadiusInner) | |
| 1900 { | |
| 1901 if ( snapshotMode ) | |
| 1902 { | |
| 1903 svg += | |
| 1904 '<circle cx="' + centerX + '" cy="' + centerY + | |
| 1905 '" r="' + childRadiusInner + '"/>'; | |
| 1906 svg += | |
| 1907 '<circle cx="' + centerX + '" cy="' + centerY + | |
| 1908 '" r="' + gRadius + '"/>'; | |
| 1909 } | |
| 1910 else | |
| 1911 { | |
| 1912 context.globalAlpha = 1 - this.alphaLine.current();//this.getUncollapsed().alphaLine.current(); | |
| 1913 context.beginPath(); | |
| 1914 context.arc(0, 0, childRadiusInner, 0, Math.PI * 2, false); | |
| 1915 context.stroke(); | |
| 1916 context.beginPath(); | |
| 1917 context.arc(0, 0, gRadius, 0, Math.PI * 2, false); | |
| 1918 context.stroke(); | |
| 1919 } | |
| 1920 } | |
| 1921 | |
| 1922 this.getCollapse = function() | |
| 1923 { | |
| 1924 return ( | |
| 1925 collapse && | |
| 1926 this.collapse && | |
| 1927 this.depth != maxAbsoluteDepth | |
| 1928 ); | |
| 1929 } | |
| 1930 | |
| 1931 this.getDepth = function() | |
| 1932 { | |
| 1933 if ( collapse ) | |
| 1934 { | |
| 1935 return this.depthCollapsed; | |
| 1936 } | |
| 1937 else | |
| 1938 { | |
| 1939 return this.depth; | |
| 1940 } | |
| 1941 } | |
| 1942 | |
| 1943 this.getMagnitude = function() | |
| 1944 { | |
| 1945 return this.attributes[magnitudeIndex][currentDataset]; | |
| 1946 } | |
| 1947 | |
| 1948 this.getMapPosition = function() | |
| 1949 { | |
| 1950 return { | |
| 1951 x : (details.offsetLeft + details.clientWidth - mapRadius), | |
| 1952 y : ((focusNode.getDepth() - this.getDepth()) * | |
| 1953 (mapBuffer + mapRadius * 2) - mapRadius) + | |
| 1954 details.clientHeight + details.offsetTop | |
| 1955 }; | |
| 1956 } | |
| 1957 | |
| 1958 this.getMaxDepth = function(limit) | |
| 1959 { | |
| 1960 var max; | |
| 1961 | |
| 1962 if ( collapse ) | |
| 1963 { | |
| 1964 return this.maxDepthCollapsed; | |
| 1965 } | |
| 1966 else | |
| 1967 { | |
| 1968 if ( this.maxDepth > maxAbsoluteDepth ) | |
| 1969 { | |
| 1970 return maxAbsoluteDepth; | |
| 1971 } | |
| 1972 else | |
| 1973 { | |
| 1974 return this.maxDepth; | |
| 1975 } | |
| 1976 } | |
| 1977 } | |
| 1978 | |
| 1979 this.getData = function(index, summary) | |
| 1980 { | |
| 1981 var files = new Array(); | |
| 1982 | |
| 1983 if | |
| 1984 ( | |
| 1985 this.attributes[index] != null && | |
| 1986 this.attributes[index][currentDataset] != null && | |
| 1987 this.attributes[index][currentDataset] != '' | |
| 1988 ) | |
| 1989 { | |
| 1990 files.push | |
| 1991 ( | |
| 1992 document.location + | |
| 1993 '.files/' + | |
| 1994 this.attributes[index][currentDataset] | |
| 1995 ); | |
| 1996 } | |
| 1997 | |
| 1998 if ( summary ) | |
| 1999 { | |
| 2000 for ( var i = 0; i < this.children.length; i++ ) | |
| 2001 { | |
| 2002 files = files.concat(this.children[i].getData(index, true)); | |
| 2003 } | |
| 2004 } | |
| 2005 | |
| 2006 return files; | |
| 2007 } | |
| 2008 | |
| 2009 this.getList = function(index, summary) | |
| 2010 { | |
| 2011 var list; | |
| 2012 | |
| 2013 if | |
| 2014 ( | |
| 2015 this.attributes[index] != null && | |
| 2016 this.attributes[index][currentDataset] != null | |
| 2017 ) | |
| 2018 { | |
| 2019 list = this.attributes[index][currentDataset]; | |
| 2020 } | |
| 2021 else | |
| 2022 { | |
| 2023 list = new Array(); | |
| 2024 } | |
| 2025 | |
| 2026 if ( summary ) | |
| 2027 { | |
| 2028 for ( var i = 0; i < this.children.length; i++ ) | |
| 2029 { | |
| 2030 list = list.concat(this.children[i].getList(index, true)); | |
| 2031 } | |
| 2032 } | |
| 2033 | |
| 2034 return list; | |
| 2035 } | |
| 2036 | |
| 2037 this.getParent = function() | |
| 2038 { | |
| 2039 // returns parent, accounting for collapsing or 0 if doesn't exist | |
| 2040 | |
| 2041 var parent = this.parent; | |
| 2042 | |
| 2043 while ( parent != 0 && parent.getCollapse() ) | |
| 2044 { | |
| 2045 parent = parent.parent; | |
| 2046 } | |
| 2047 | |
| 2048 return parent; | |
| 2049 } | |
| 2050 | |
| 2051 this.getPercentage = function() | |
| 2052 { | |
| 2053 return getPercentage(this.magnitude / selectedNode.magnitude); | |
| 2054 } | |
| 2055 | |
| 2056 this.getUnclassifiedPercentage = function() | |
| 2057 { | |
| 2058 var lastChild = this.children[this.children.length - 1]; | |
| 2059 | |
| 2060 return getPercentage | |
| 2061 ( | |
| 2062 ( | |
| 2063 this.baseMagnitude + | |
| 2064 this.magnitude - | |
| 2065 lastChild.magnitude - | |
| 2066 lastChild.baseMagnitude | |
| 2067 ) / this.magnitude | |
| 2068 ) + '%'; | |
| 2069 } | |
| 2070 | |
| 2071 this.getUnclassifiedText = function() | |
| 2072 { | |
| 2073 return '[unassigned '+ this.name + ']'; | |
| 2074 } | |
| 2075 | |
| 2076 this.getUncollapsed = function() | |
| 2077 { | |
| 2078 // recurse through collapsed children until uncollapsed node is found | |
| 2079 | |
| 2080 if ( this.getCollapse() ) | |
| 2081 { | |
| 2082 return this.children[0].getUncollapsed(); | |
| 2083 } | |
| 2084 else | |
| 2085 { | |
| 2086 return this; | |
| 2087 } | |
| 2088 } | |
| 2089 | |
| 2090 this.hasChildren = function() | |
| 2091 { | |
| 2092 return this.children.length && this.depth < maxAbsoluteDepth && this.magnitude; | |
| 2093 } | |
| 2094 | |
| 2095 this.hasParent = function(parent) | |
| 2096 { | |
| 2097 if ( this.parent ) | |
| 2098 { | |
| 2099 if ( this.parent == parent ) | |
| 2100 { | |
| 2101 return true; | |
| 2102 } | |
| 2103 else | |
| 2104 { | |
| 2105 return this.parent.hasParent(parent); | |
| 2106 } | |
| 2107 } | |
| 2108 else | |
| 2109 { | |
| 2110 return false; | |
| 2111 } | |
| 2112 } | |
| 2113 | |
| 2114 this.maxVisibleDepth = function(maxDepth) | |
| 2115 { | |
| 2116 var childInnerRadius; | |
| 2117 var depth = this.getDepth() - selectedNode.getDepth() + 1; | |
| 2118 var currentMaxDepth = depth; | |
| 2119 | |
| 2120 if ( this.hasChildren() && depth < maxDepth) | |
| 2121 { | |
| 2122 var lastChild = this.children[this.children.length - 1]; | |
| 2123 | |
| 2124 if ( this.name == 'Pseudomonadaceae' ) | |
| 2125 { | |
| 2126 var x = 3; | |
| 2127 } | |
| 2128 | |
| 2129 if | |
| 2130 ( | |
| 2131 lastChild.baseMagnitude + lastChild.magnitude < | |
| 2132 this.baseMagnitude + this.magnitude | |
| 2133 ) | |
| 2134 { | |
| 2135 currentMaxDepth++; | |
| 2136 } | |
| 2137 | |
| 2138 if ( compress ) | |
| 2139 { | |
| 2140 childInnerRadius = compressedRadii[depth - 1]; | |
| 2141 } | |
| 2142 else | |
| 2143 { | |
| 2144 childInnerRadius = (depth) / maxDepth; | |
| 2145 } | |
| 2146 | |
| 2147 for ( var i = 0; i < this.children.length; i++ ) | |
| 2148 { | |
| 2149 if | |
| 2150 (//true || | |
| 2151 this.children[i].magnitude * | |
| 2152 angleFactor * | |
| 2153 (childInnerRadius + 1) * | |
| 2154 gRadius >= | |
| 2155 minWidth() | |
| 2156 ) | |
| 2157 { | |
| 2158 var childMaxDepth = this.children[i].maxVisibleDepth(maxDepth); | |
| 2159 | |
| 2160 if ( childMaxDepth > currentMaxDepth ) | |
| 2161 { | |
| 2162 currentMaxDepth = childMaxDepth; | |
| 2163 } | |
| 2164 } | |
| 2165 } | |
| 2166 } | |
| 2167 | |
| 2168 return currentMaxDepth; | |
| 2169 } | |
| 2170 | |
| 2171 this.resetLabelWidth = function() | |
| 2172 { | |
| 2173 var nameWidthOld = this.nameWidth; | |
| 2174 | |
| 2175 if ( ! this.radial )//&& fontSize != fontSizeLast ) | |
| 2176 { | |
| 2177 var dim = context.measureText(this.name); | |
| 2178 this.nameWidth = dim.width; | |
| 2179 } | |
| 2180 | |
| 2181 if ( fontSize != fontSizeLast && this.labelWidth.end == nameWidthOld * labelWidthFudge ) | |
| 2182 { | |
| 2183 // font size changed; adjust start of tween to match | |
| 2184 | |
| 2185 this.labelWidth.start = this.nameWidth * labelWidthFudge; | |
| 2186 } | |
| 2187 else | |
| 2188 { | |
| 2189 this.labelWidth.start = this.labelWidth.current(); | |
| 2190 } | |
| 2191 | |
| 2192 this.labelWidth.end = this.nameWidth * labelWidthFudge; | |
| 2193 } | |
| 2194 | |
| 2195 this.restrictLabelWidth = function(width) | |
| 2196 { | |
| 2197 if ( width < this.labelWidth.end ) | |
| 2198 { | |
| 2199 this.labelWidth.end = width; | |
| 2200 } | |
| 2201 } | |
| 2202 | |
| 2203 this.search = function() | |
| 2204 { | |
| 2205 this.isSearchResult = false; | |
| 2206 this.searchResults = 0; | |
| 2207 | |
| 2208 if | |
| 2209 ( | |
| 2210 ! this.getCollapse() && | |
| 2211 search.value != '' && | |
| 2212 this.name.toLowerCase().indexOf(search.value.toLowerCase()) != -1 | |
| 2213 ) | |
| 2214 { | |
| 2215 this.isSearchResult = true; | |
| 2216 this.searchResults = 1; | |
| 2217 nSearchResults++; | |
| 2218 } | |
| 2219 | |
| 2220 for ( var i = 0; i < this.children.length; i++ ) | |
| 2221 { | |
| 2222 this.searchResults += this.children[i].search(); | |
| 2223 } | |
| 2224 | |
| 2225 return this.searchResults; | |
| 2226 } | |
| 2227 | |
| 2228 this.searchResultChildren = function() | |
| 2229 { | |
| 2230 if ( this.isSearchResult ) | |
| 2231 { | |
| 2232 return this.searchResults - 1; | |
| 2233 } | |
| 2234 else | |
| 2235 { | |
| 2236 return this.searchResults; | |
| 2237 } | |
| 2238 } | |
| 2239 | |
| 2240 this.setDepth = function(depth, depthCollapsed) | |
| 2241 { | |
| 2242 this.depth = depth; | |
| 2243 this.depthCollapsed = depthCollapsed; | |
| 2244 | |
| 2245 if | |
| 2246 ( | |
| 2247 this.children.length == 1 && | |
| 2248 // this.magnitude > 0 && | |
| 2249 this.children[0].magnitude == this.magnitude && | |
| 2250 ( head.children.length > 1 || this.children[0].children.length ) | |
| 2251 ) | |
| 2252 { | |
| 2253 this.collapse = true; | |
| 2254 } | |
| 2255 else | |
| 2256 { | |
| 2257 this.collapse = false; | |
| 2258 depthCollapsed++; | |
| 2259 } | |
| 2260 | |
| 2261 for ( var i = 0; i < this.children.length; i++ ) | |
| 2262 { | |
| 2263 this.children[i].setDepth(depth + 1, depthCollapsed); | |
| 2264 } | |
| 2265 } | |
| 2266 | |
| 2267 this.setHighlightStyle = function() | |
| 2268 { | |
| 2269 context.lineWidth = highlightLineWidth; | |
| 2270 | |
| 2271 if ( this.hasChildren() || this != focusNode || this != highlightedNode ) | |
| 2272 { | |
| 2273 context.strokeStyle = 'black'; | |
| 2274 context.fillStyle = "rgba(255, 255, 255, .3)"; | |
| 2275 } | |
| 2276 else | |
| 2277 { | |
| 2278 context.strokeStyle = 'rgb(90,90,90)'; | |
| 2279 context.fillStyle = "rgba(155, 155, 155, .3)"; | |
| 2280 } | |
| 2281 } | |
| 2282 | |
| 2283 this.setLabelWidth = function(node) | |
| 2284 { | |
| 2285 if ( ! shorten || this.radial ) | |
| 2286 { | |
| 2287 return; // don't need to set width | |
| 2288 } | |
| 2289 | |
| 2290 if ( node.hide ) | |
| 2291 { | |
| 2292 alert('wtf'); | |
| 2293 return; | |
| 2294 } | |
| 2295 | |
| 2296 var angle = (this.angleStart.end + this.angleEnd.end) / 2; | |
| 2297 var a; // angle difference | |
| 2298 | |
| 2299 if ( node == selectedNode ) | |
| 2300 { | |
| 2301 a = Math.abs(angle - node.angleOther); | |
| 2302 } | |
| 2303 else | |
| 2304 { | |
| 2305 a = Math.abs(angle - (node.angleStart.end + node.angleEnd.end) / 2); | |
| 2306 } | |
| 2307 | |
| 2308 if ( a == 0 ) | |
| 2309 { | |
| 2310 return; | |
| 2311 } | |
| 2312 | |
| 2313 if ( a > Math.PI ) | |
| 2314 { | |
| 2315 a = 2 * Math.PI - a; | |
| 2316 } | |
| 2317 | |
| 2318 if ( node.radial || node == selectedNode ) | |
| 2319 { | |
| 2320 var nodeLabelRadius; | |
| 2321 | |
| 2322 if ( node == selectedNode ) | |
| 2323 { | |
| 2324 // radial 'other' label | |
| 2325 | |
| 2326 nodeLabelRadius = (node.children[0].radiusInner.end + 1) / 2; | |
| 2327 } | |
| 2328 else | |
| 2329 { | |
| 2330 nodeLabelRadius = (node.radiusInner.end + 1) / 2; | |
| 2331 } | |
| 2332 | |
| 2333 if ( a < Math.PI / 2 ) | |
| 2334 { | |
| 2335 var r = this.labelRadius.end * gRadius + .5 * fontSize | |
| 2336 var hypotenuse = r / Math.cos(a); | |
| 2337 var opposite = r * Math.tan(a); | |
| 2338 var fontRadius = .8 * fontSize; | |
| 2339 | |
| 2340 if | |
| 2341 ( | |
| 2342 nodeLabelRadius * gRadius < hypotenuse && | |
| 2343 this.labelWidth.end / 2 + fontRadius > opposite | |
| 2344 ) | |
| 2345 { | |
| 2346 this.labelWidth.end = 2 * (opposite - fontRadius); | |
| 2347 } | |
| 2348 } | |
| 2349 } | |
| 2350 else if | |
| 2351 ( | |
| 2352 this.labelRadius.end == node.labelRadius.end && | |
| 2353 a < Math.PI / 4 | |
| 2354 ) | |
| 2355 { | |
| 2356 // same radius with small angle; use circumferential approximation | |
| 2357 | |
| 2358 var dist = a * this.labelRadius.end * gRadius - fontSize * (1 - a * 4 / Math.PI) * 1.3; | |
| 2359 | |
| 2360 if ( this.labelWidth.end < dist ) | |
| 2361 { | |
| 2362 node.restrictLabelWidth((dist - this.labelWidth.end / 2) * 2); | |
| 2363 } | |
| 2364 else if ( node.labelWidth.end < dist ) | |
| 2365 { | |
| 2366 this.restrictLabelWidth((dist - node.labelWidth.end / 2) * 2); | |
| 2367 } | |
| 2368 else | |
| 2369 { | |
| 2370 // both labels reach halfway point; restrict both | |
| 2371 | |
| 2372 this.labelWidth.end = dist; | |
| 2373 node.labelWidth.end = dist | |
| 2374 } | |
| 2375 } | |
| 2376 else | |
| 2377 { | |
| 2378 var r1 = this.labelRadius.end * gRadius; | |
| 2379 var r2 = node.labelRadius.end * gRadius; | |
| 2380 | |
| 2381 // first adjust the radii to account for the height of the font by shifting them | |
| 2382 // toward each other | |
| 2383 // | |
| 2384 var fontFudge = .35 * fontSize; | |
| 2385 // | |
| 2386 if ( this.labelRadius.end < node.labelRadius.end ) | |
| 2387 { | |
| 2388 r1 += fontFudge; | |
| 2389 r2 -= fontFudge; | |
| 2390 } | |
| 2391 else if ( this.labelRadius.end > node.labelRadius.end ) | |
| 2392 { | |
| 2393 r1 -= fontFudge; | |
| 2394 r2 += fontFudge; | |
| 2395 } | |
| 2396 else | |
| 2397 { | |
| 2398 r1 -= fontFudge; | |
| 2399 r2 -= fontFudge; | |
| 2400 } | |
| 2401 | |
| 2402 var r1s = r1 * r1; | |
| 2403 var r2s = r2 * r2; | |
| 2404 | |
| 2405 // distance between the centers of the two labels | |
| 2406 // | |
| 2407 var dist = Math.sqrt(r1s + r2s - 2 * r1 * r2 * Math.cos(a)); | |
| 2408 | |
| 2409 // angle at our label center between our radius and the line to the other label center | |
| 2410 // | |
| 2411 var b = Math.acos((r1s + dist * dist - r2s) / (2 * r1 * dist)); | |
| 2412 | |
| 2413 // distance from our label center to the intersection of the two tangents | |
| 2414 // | |
| 2415 var l1 = Math.sin(a + b - Math.PI / 2) * dist / Math.sin(Math.PI - a); | |
| 2416 | |
| 2417 // distance from other label center the the intersection of the two tangents | |
| 2418 // | |
| 2419 var l2 = Math.sin(Math.PI / 2 - b) * dist / Math.sin(Math.PI - a); | |
| 2420 | |
| 2421 l1 = Math.abs(l1) - .4 * fontSize; | |
| 2422 l2 = Math.abs(l2) - .4 * fontSize; | |
| 2423 /* | |
| 2424 // amount to shorten the distances because of the height of the font | |
| 2425 // | |
| 2426 var l3 = 0; | |
| 2427 var fontRadius = fontSize * .55; | |
| 2428 // | |
| 2429 if ( l1 < 0 || l2 < 0 ) | |
| 2430 { | |
| 2431 var l4 = fontRadius / Math.tan(a); | |
| 2432 l1 = Math.abs(l1); | |
| 2433 l2 = Math.abs(l2); | |
| 2434 | |
| 2435 l1 -= l4; | |
| 2436 l2 -= l4; | |
| 2437 } | |
| 2438 else | |
| 2439 { | |
| 2440 var c = Math.PI - a; | |
| 2441 | |
| 2442 l3 = fontRadius * Math.tan(c / 2); | |
| 2443 } | |
| 2444 */ | |
| 2445 if ( this.labelWidth.end / 2 > l1 && node.labelWidth.end / 2 > l2 ) | |
| 2446 { | |
| 2447 // shorten the farthest one from the intersection | |
| 2448 | |
| 2449 if ( l1 > l2 ) | |
| 2450 { | |
| 2451 this.restrictLabelWidth(2 * (l1));// - l3 - fontRadius)); | |
| 2452 } | |
| 2453 else | |
| 2454 { | |
| 2455 node.restrictLabelWidth(2 * (l2));// - l3 - fontRadius)); | |
| 2456 } | |
| 2457 }/* | |
| 2458 else if ( this.labelWidth.end / 2 > l1 + l3 && node.labelWidth.end / 2 > l2 - l3 ) | |
| 2459 { | |
| 2460 node.restrictLabelWidth(2 * (l2 - l3)); | |
| 2461 } | |
| 2462 else if ( this.labelWidth.end / 2 > l1 - l3 && node.labelWidth.end / 2 > l2 + l3 ) | |
| 2463 { | |
| 2464 this.restrictLabelWidth(2 * (l1 - l3)); | |
| 2465 }*/ | |
| 2466 } | |
| 2467 } | |
| 2468 | |
| 2469 this.setMagnitudes = function(baseMagnitude) | |
| 2470 { | |
| 2471 this.magnitude = this.getMagnitude(); | |
| 2472 this.baseMagnitude = baseMagnitude; | |
| 2473 | |
| 2474 for ( var i = 0; i < this.children.length; i++ ) | |
| 2475 { | |
| 2476 this.children[i].setMagnitudes(baseMagnitude); | |
| 2477 baseMagnitude += this.children[i].magnitude; | |
| 2478 } | |
| 2479 | |
| 2480 this.maxChildMagnitude = baseMagnitude; | |
| 2481 } | |
| 2482 | |
| 2483 this.setMaxDepths = function() | |
| 2484 { | |
| 2485 this.maxDepth = this.depth; | |
| 2486 this.maxDepthCollapsed = this.depthCollapsed; | |
| 2487 | |
| 2488 for ( i in this.children ) | |
| 2489 { | |
| 2490 var child = this.children[i]; | |
| 2491 | |
| 2492 child.setMaxDepths(); | |
| 2493 | |
| 2494 if ( child.maxDepth > this.maxDepth ) | |
| 2495 { | |
| 2496 this.maxDepth = child.maxDepth; | |
| 2497 } | |
| 2498 | |
| 2499 if | |
| 2500 ( | |
| 2501 child.maxDepthCollapsed > this.maxDepthCollapsed && | |
| 2502 (child.depth <= maxAbsoluteDepth || maxAbsoluteDepth == 0) | |
| 2503 ) | |
| 2504 { | |
| 2505 this.maxDepthCollapsed = child.maxDepthCollapsed; | |
| 2506 } | |
| 2507 } | |
| 2508 } | |
| 2509 | |
| 2510 this.setTargetLabelRadius = function() | |
| 2511 { | |
| 2512 var depth = this.getDepth() - selectedNode.getDepth() + 1; | |
| 2513 var index = depth - 2; | |
| 2514 var labelOffset = labelOffsets[index]; | |
| 2515 | |
| 2516 if ( this.radial ) | |
| 2517 { | |
| 2518 //this.labelRadius.setTarget((this.radiusInner.end + 1) / 2); | |
| 2519 var max = | |
| 2520 depth == maxDisplayDepth ? | |
| 2521 1 : | |
| 2522 compressedRadii[index + 1]; | |
| 2523 | |
| 2524 this.labelRadius.setTarget((compressedRadii[index] + max) / 2); | |
| 2525 } | |
| 2526 else | |
| 2527 { | |
| 2528 var radiusCenter; | |
| 2529 var width; | |
| 2530 | |
| 2531 if ( compress ) | |
| 2532 { | |
| 2533 if ( nLabelOffsets[index] > 1 ) | |
| 2534 { | |
| 2535 this.labelRadius.setTarget | |
| 2536 ( | |
| 2537 lerp | |
| 2538 ( | |
| 2539 labelOffset + .75, | |
| 2540 0, | |
| 2541 nLabelOffsets[index] + .5, | |
| 2542 compressedRadii[index], | |
| 2543 compressedRadii[index + 1] | |
| 2544 ) | |
| 2545 ); | |
| 2546 } | |
| 2547 else | |
| 2548 { | |
| 2549 this.labelRadius.setTarget((compressedRadii[index] + compressedRadii[index + 1]) / 2); | |
| 2550 } | |
| 2551 } | |
| 2552 else | |
| 2553 { | |
| 2554 radiusCenter = | |
| 2555 nodeRadius * (depth - 1) + | |
| 2556 nodeRadius / 2; | |
| 2557 width = nodeRadius; | |
| 2558 | |
| 2559 this.labelRadius.setTarget | |
| 2560 ( | |
| 2561 radiusCenter + width * ((labelOffset + 1) / (nLabelOffsets[index] + 1) - .5) | |
| 2562 ); | |
| 2563 } | |
| 2564 } | |
| 2565 | |
| 2566 if ( ! this.hide && ! this.keyed && nLabelOffsets[index] ) | |
| 2567 { | |
| 2568 // check last and first labels in each track for overlap | |
| 2569 | |
| 2570 for ( var i = 0; i < maxDisplayDepth - 1; i++ ) | |
| 2571 { | |
| 2572 for ( var j = 0; j <= nLabelOffsets[i]; j++ ) | |
| 2573 { | |
| 2574 var last = labelLastNodes[i][j]; | |
| 2575 var first = labelFirstNodes[i][j]; | |
| 2576 | |
| 2577 if ( last ) | |
| 2578 { | |
| 2579 if ( j == nLabelOffsets[i] ) | |
| 2580 { | |
| 2581 // last is radial | |
| 2582 this.setLabelWidth(last); | |
| 2583 } | |
| 2584 else | |
| 2585 { | |
| 2586 last.setLabelWidth(this); | |
| 2587 } | |
| 2588 } | |
| 2589 | |
| 2590 if ( first ) | |
| 2591 { | |
| 2592 if ( j == nLabelOffsets[i] ) | |
| 2593 { | |
| 2594 this.setLabelWidth(first); | |
| 2595 } | |
| 2596 else | |
| 2597 { | |
| 2598 first.setLabelWidth(this); | |
| 2599 } | |
| 2600 } | |
| 2601 } | |
| 2602 } | |
| 2603 | |
| 2604 if ( selectedNode.canDisplayLabelOther ) | |
| 2605 { | |
| 2606 this.setLabelWidth(selectedNode); // in case there is an 'other' label | |
| 2607 } | |
| 2608 | |
| 2609 if ( this.radial ) | |
| 2610 { | |
| 2611 // use the last 'track' of this depth for radial | |
| 2612 | |
| 2613 labelLastNodes[index][nLabelOffsets[index]] = this; | |
| 2614 | |
| 2615 if ( labelFirstNodes[index][nLabelOffsets[index]] == 0 ) | |
| 2616 { | |
| 2617 labelFirstNodes[index][nLabelOffsets[index]] = this; | |
| 2618 } | |
| 2619 } | |
| 2620 else | |
| 2621 { | |
| 2622 labelLastNodes[index][labelOffset] = this; | |
| 2623 | |
| 2624 // update offset | |
| 2625 | |
| 2626 labelOffsets[index] += 1; | |
| 2627 | |
| 2628 if ( labelOffsets[index] > nLabelOffsets[index] ) | |
| 2629 { | |
| 2630 labelOffsets[index] -= nLabelOffsets[index]; | |
| 2631 | |
| 2632 if ( !(nLabelOffsets[index] & 1) ) | |
| 2633 { | |
| 2634 labelOffsets[index]--; | |
| 2635 } | |
| 2636 } | |
| 2637 else if ( labelOffsets[index] == nLabelOffsets[index] ) | |
| 2638 { | |
| 2639 labelOffsets[index] -= nLabelOffsets[index]; | |
| 2640 | |
| 2641 if ( false && !(nLabelOffsets[index] & 1) ) | |
| 2642 { | |
| 2643 labelOffsets[index]++; | |
| 2644 } | |
| 2645 } | |
| 2646 | |
| 2647 if ( labelFirstNodes[index][labelOffset] == 0 ) | |
| 2648 { | |
| 2649 labelFirstNodes[index][labelOffset] = this; | |
| 2650 } | |
| 2651 } | |
| 2652 } | |
| 2653 else if ( this.hide ) | |
| 2654 { | |
| 2655 this.labelWidth.end = 0; | |
| 2656 } | |
| 2657 } | |
| 2658 | |
| 2659 this.setTargets = function() | |
| 2660 { | |
| 2661 if ( this == selectedNode ) | |
| 2662 { | |
| 2663 this.setTargetsSelected | |
| 2664 ( | |
| 2665 0, | |
| 2666 1, | |
| 2667 lightnessBase, | |
| 2668 false, | |
| 2669 false | |
| 2670 ); | |
| 2671 return; | |
| 2672 } | |
| 2673 | |
| 2674 var depthRelative = this.getDepth() - selectedNode.getDepth(); | |
| 2675 | |
| 2676 var parentOfSelected = selectedNode.hasParent(this); | |
| 2677 /* ( | |
| 2678 // ! this.getCollapse() && | |
| 2679 this.baseMagnitude <= selectedNode.baseMagnitude && | |
| 2680 this.baseMagnitude + this.magnitude >= | |
| 2681 selectedNode.baseMagnitude + selectedNode.magnitude | |
| 2682 ); | |
| 2683 */ | |
| 2684 if ( parentOfSelected ) | |
| 2685 { | |
| 2686 this.resetLabelWidth(); | |
| 2687 } | |
| 2688 else | |
| 2689 { | |
| 2690 //context.font = fontNormal; | |
| 2691 var dim = context.measureText(this.name); | |
| 2692 this.nameWidth = dim.width; | |
| 2693 //this.labelWidth.setTarget(this.labelWidth.end); | |
| 2694 this.labelWidth.setTarget(0); | |
| 2695 } | |
| 2696 | |
| 2697 // set angles | |
| 2698 // | |
| 2699 if ( this.baseMagnitude <= selectedNode.baseMagnitude ) | |
| 2700 { | |
| 2701 this.angleStart.setTarget(0); | |
| 2702 } | |
| 2703 else | |
| 2704 { | |
| 2705 this.angleStart.setTarget(Math.PI * 2); | |
| 2706 } | |
| 2707 // | |
| 2708 if | |
| 2709 ( | |
| 2710 parentOfSelected || | |
| 2711 this.baseMagnitude + this.magnitude >= | |
| 2712 selectedNode.baseMagnitude + selectedNode.magnitude | |
| 2713 ) | |
| 2714 { | |
| 2715 this.angleEnd.setTarget(Math.PI * 2); | |
| 2716 } | |
| 2717 else | |
| 2718 { | |
| 2719 this.angleEnd.setTarget(0); | |
| 2720 } | |
| 2721 | |
| 2722 // children | |
| 2723 // | |
| 2724 for ( var i = 0; i < this.children.length; i++ ) | |
| 2725 { | |
| 2726 this.children[i].setTargets(); | |
| 2727 } | |
| 2728 | |
| 2729 if ( this.getDepth() <= selectedNode.getDepth() ) | |
| 2730 { | |
| 2731 // collapse in | |
| 2732 | |
| 2733 this.radiusInner.setTarget(0); | |
| 2734 | |
| 2735 if ( parentOfSelected ) | |
| 2736 { | |
| 2737 this.labelRadius.setTarget | |
| 2738 ( | |
| 2739 (depthRelative) * | |
| 2740 historySpacingFactor * fontSize / gRadius | |
| 2741 ); | |
| 2742 //this.scale.setTarget(1 - (selectedNode.getDepth() - this.getDepth()) / 18); // TEMP | |
| 2743 } | |
| 2744 else | |
| 2745 { | |
| 2746 this.labelRadius.setTarget(0); | |
| 2747 //this.scale.setTarget(1); // TEMP | |
| 2748 } | |
| 2749 } | |
| 2750 else if ( depthRelative + 1 > maxDisplayDepth ) | |
| 2751 { | |
| 2752 // collapse out | |
| 2753 | |
| 2754 this.radiusInner.setTarget(1); | |
| 2755 this.labelRadius.setTarget(1); | |
| 2756 //this.scale.setTarget(1); // TEMP | |
| 2757 } | |
| 2758 else | |
| 2759 { | |
| 2760 // don't collapse | |
| 2761 | |
| 2762 if ( compress ) | |
| 2763 { | |
| 2764 this.radiusInner.setTarget(compressedRadii[depthRelative - 1]); | |
| 2765 } | |
| 2766 else | |
| 2767 { | |
| 2768 this.radiusInner.setTarget(nodeRadius * (depthRelative)); | |
| 2769 } | |
| 2770 | |
| 2771 //this.scale.setTarget(1); // TEMP | |
| 2772 | |
| 2773 if ( this == selectedNode ) | |
| 2774 { | |
| 2775 this.labelRadius.setTarget(0); | |
| 2776 } | |
| 2777 else | |
| 2778 { | |
| 2779 if ( compress ) | |
| 2780 { | |
| 2781 this.labelRadius.setTarget | |
| 2782 ( | |
| 2783 (compressedRadii[depthRelative - 1] + compressedRadii[depthRelative]) / 2 | |
| 2784 ); | |
| 2785 } | |
| 2786 else | |
| 2787 { | |
| 2788 this.labelRadius.setTarget(nodeRadius * (depthRelative) + nodeRadius / 2); | |
| 2789 } | |
| 2790 } | |
| 2791 } | |
| 2792 | |
| 2793 // this.r.start = this.r.end; | |
| 2794 // this.g.start = this.g.end; | |
| 2795 // this.b.start = this.b.end; | |
| 2796 | |
| 2797 this.r.setTarget(255); | |
| 2798 this.g.setTarget(255); | |
| 2799 this.b.setTarget(255); | |
| 2800 | |
| 2801 this.alphaLine.setTarget(0); | |
| 2802 this.alphaArc.setTarget(0); | |
| 2803 this.alphaWedge.setTarget(0); | |
| 2804 this.alphaPattern.setTarget(0); | |
| 2805 this.alphaOther.setTarget(0); | |
| 2806 | |
| 2807 if ( parentOfSelected && ! this.getCollapse() ) | |
| 2808 { | |
| 2809 var alpha = | |
| 2810 ( | |
| 2811 1 - | |
| 2812 (selectedNode.getDepth() - this.getDepth()) / | |
| 2813 (Math.floor((compress ? compressedRadii[0] : nodeRadius) * gRadius / (historySpacingFactor * fontSize) - .5) + 1) | |
| 2814 ); | |
| 2815 | |
| 2816 if ( alpha < 0 ) | |
| 2817 { | |
| 2818 alpha = 0; | |
| 2819 } | |
| 2820 | |
| 2821 this.alphaLabel.setTarget(alpha); | |
| 2822 this.radial = false; | |
| 2823 } | |
| 2824 else | |
| 2825 { | |
| 2826 this.alphaLabel.setTarget(0); | |
| 2827 } | |
| 2828 | |
| 2829 this.hideAlonePrev = this.hideAlone; | |
| 2830 this.hidePrev = this.hide; | |
| 2831 | |
| 2832 if ( parentOfSelected ) | |
| 2833 { | |
| 2834 this.hideAlone = false; | |
| 2835 this.hide = false; | |
| 2836 } | |
| 2837 | |
| 2838 if ( this.getParent() == selectedNode.getParent() ) | |
| 2839 { | |
| 2840 this.hiddenEnd = null; | |
| 2841 } | |
| 2842 | |
| 2843 this.radialPrev = this.radial; | |
| 2844 } | |
| 2845 | |
| 2846 this.setTargetsSelected = function(hueMin, hueMax, lightness, hide, nextSiblingHidden) | |
| 2847 { | |
| 2848 var collapse = this.getCollapse(); | |
| 2849 var depth = this.getDepth() - selectedNode.getDepth() + 1; | |
| 2850 var canDisplayChildLabels = false; | |
| 2851 var lastChild; | |
| 2852 | |
| 2853 if ( this.hasChildren() )//&& ! hide ) | |
| 2854 { | |
| 2855 lastChild = this.children[this.children.length - 1]; | |
| 2856 this.hideAlone = true; | |
| 2857 } | |
| 2858 else | |
| 2859 { | |
| 2860 this.hideAlone = false; | |
| 2861 } | |
| 2862 | |
| 2863 // set child wedges | |
| 2864 // | |
| 2865 for ( var i = 0; i < this.children.length; i++ ) | |
| 2866 { | |
| 2867 this.children[i].setTargetWedge(); | |
| 2868 | |
| 2869 if | |
| 2870 ( | |
| 2871 ! this.children[i].hide && | |
| 2872 ( collapse || depth < maxDisplayDepth ) && | |
| 2873 this.depth < maxAbsoluteDepth | |
| 2874 ) | |
| 2875 { | |
| 2876 canDisplayChildLabels = true; | |
| 2877 this.hideAlone = false; | |
| 2878 } | |
| 2879 } | |
| 2880 | |
| 2881 if ( this == selectedNode || lastChild && lastChild.angleEnd.end < this.angleEnd.end - .01) | |
| 2882 { | |
| 2883 this.hideAlone = false; | |
| 2884 } | |
| 2885 | |
| 2886 if ( this.hideAlonePrev == undefined ) | |
| 2887 { | |
| 2888 this.hideAlonePrev = this.hideAlone; | |
| 2889 } | |
| 2890 | |
| 2891 if ( this == selectedNode ) | |
| 2892 { | |
| 2893 var otherArc = | |
| 2894 angleFactor * | |
| 2895 ( | |
| 2896 this.baseMagnitude + this.magnitude - | |
| 2897 lastChild.baseMagnitude - lastChild.magnitude | |
| 2898 ); | |
| 2899 this.canDisplayLabelOther = | |
| 2900 otherArc * | |
| 2901 (this.children[0].radiusInner.end + 1) * gRadius >= | |
| 2902 minWidth(); | |
| 2903 | |
| 2904 this.keyUnclassified = false; | |
| 2905 | |
| 2906 if ( this.canDisplayLabelOther ) | |
| 2907 { | |
| 2908 this.angleOther = Math.PI * 2 - otherArc / 2; | |
| 2909 } | |
| 2910 else if ( otherArc > 0.0000000001 ) | |
| 2911 { | |
| 2912 this.keyUnclassified = true; | |
| 2913 keys++; | |
| 2914 } | |
| 2915 | |
| 2916 this.angleStart.setTarget(0); | |
| 2917 this.angleEnd.setTarget(Math.PI * 2); | |
| 2918 this.radiusInner.setTarget(0); | |
| 2919 this.hidePrev = this.hide; | |
| 2920 this.hide = false; | |
| 2921 this.hideAlonePrev = this.hideAlone; | |
| 2922 this.hideAlone = false; | |
| 2923 this.keyed = false; | |
| 2924 } | |
| 2925 | |
| 2926 if ( hueMax - hueMin > 1 / 12 ) | |
| 2927 { | |
| 2928 hueMax = hueMin + 1 / 12; | |
| 2929 } | |
| 2930 | |
| 2931 // set lightness | |
| 2932 // | |
| 2933 if ( ! ( hide || this.hideAlone ) ) | |
| 2934 { | |
| 2935 if ( useHue() ) | |
| 2936 { | |
| 2937 lightness = (lightnessBase + lightnessMax) / 2; | |
| 2938 } | |
| 2939 else | |
| 2940 { | |
| 2941 lightness = lightnessBase + (depth - 1) * lightnessFactor; | |
| 2942 | |
| 2943 if ( lightness > lightnessMax ) | |
| 2944 { | |
| 2945 lightness = lightnessMax; | |
| 2946 } | |
| 2947 } | |
| 2948 } | |
| 2949 | |
| 2950 if ( hide ) | |
| 2951 { | |
| 2952 this.hide = true; | |
| 2953 } | |
| 2954 | |
| 2955 if ( this.hidePrev == undefined ) | |
| 2956 { | |
| 2957 this.hidePrev = this.hide; | |
| 2958 } | |
| 2959 | |
| 2960 var hiddenStart = -1; | |
| 2961 var hiddenHueNumer = 0; | |
| 2962 var hiddenHueDenom = 0; | |
| 2963 var i = 0; | |
| 2964 | |
| 2965 if ( ! this.hide ) | |
| 2966 { | |
| 2967 this.hiddenEnd = null; | |
| 2968 } | |
| 2969 | |
| 2970 while ( true ) | |
| 2971 { | |
| 2972 if ( ! this.hideAlone && ! hide && ( i == this.children.length || ! this.children[i].hide ) ) | |
| 2973 { | |
| 2974 // reached a non-hidden child or the end; set targets for | |
| 2975 // previous group of hidden children (if any) using their | |
| 2976 // average hue | |
| 2977 | |
| 2978 if ( hiddenStart != -1 ) | |
| 2979 { | |
| 2980 var hiddenHue = hiddenHueDenom ? hiddenHueNumer / hiddenHueDenom : hueMin; | |
| 2981 | |
| 2982 for ( var j = hiddenStart; j < i; j++ ) | |
| 2983 { | |
| 2984 this.children[j].setTargetsSelected | |
| 2985 ( | |
| 2986 hiddenHue, | |
| 2987 null, | |
| 2988 lightness, | |
| 2989 false, | |
| 2990 j < i - 1 | |
| 2991 ); | |
| 2992 | |
| 2993 this.children[j].hiddenEnd = null; | |
| 2994 } | |
| 2995 | |
| 2996 this.children[hiddenStart].hiddenEnd = i - 1; | |
| 2997 } | |
| 2998 } | |
| 2999 | |
| 3000 if ( i == this.children.length ) | |
| 3001 { | |
| 3002 break; | |
| 3003 } | |
| 3004 | |
| 3005 var child = this.children[i]; | |
| 3006 var childHueMin; | |
| 3007 var childHueMax; | |
| 3008 | |
| 3009 if ( this.magnitude > 0 && ! this.hide && ! this.hideAlone ) | |
| 3010 { | |
| 3011 if ( useHue() ) | |
| 3012 { | |
| 3013 childHueMin = child.hues[currentDataset]; | |
| 3014 } | |
| 3015 else if ( this == selectedNode ) | |
| 3016 { | |
| 3017 var min = 0.0; | |
| 3018 var max = 1.0; | |
| 3019 | |
| 3020 if ( this.children.length > 6 ) | |
| 3021 { | |
| 3022 childHueMin = lerp((1 - Math.pow(1 - i / this.children.length, 1.4)) * .95, 0, 1, min, max); | |
| 3023 childHueMax = lerp((1 - Math.pow(1 - (i + .55) / this.children.length, 1.4)) * .95, 0, 1, min, max); | |
| 3024 } | |
| 3025 else | |
| 3026 { | |
| 3027 childHueMin = lerp(i / this.children.length, 0, 1, min, max); | |
| 3028 childHueMax = lerp((i + .55) / this.children.length, 0, 1, min, max); | |
| 3029 } | |
| 3030 } | |
| 3031 else | |
| 3032 { | |
| 3033 childHueMin = lerp | |
| 3034 ( | |
| 3035 child.baseMagnitude, | |
| 3036 this.baseMagnitude, | |
| 3037 this.baseMagnitude + this.magnitude, | |
| 3038 hueMin, | |
| 3039 hueMax | |
| 3040 ); | |
| 3041 childHueMax = lerp | |
| 3042 ( | |
| 3043 child.baseMagnitude + child.magnitude * .99, | |
| 3044 this.baseMagnitude, | |
| 3045 this.baseMagnitude + this.magnitude, | |
| 3046 hueMin, | |
| 3047 hueMax | |
| 3048 ); | |
| 3049 } | |
| 3050 } | |
| 3051 else | |
| 3052 { | |
| 3053 childHueMin = hueMin; | |
| 3054 childHueMax = hueMax; | |
| 3055 } | |
| 3056 | |
| 3057 if ( ! this.hideAlone && ! hide && ! this.hide && child.hide ) | |
| 3058 { | |
| 3059 if ( hiddenStart == -1 ) | |
| 3060 { | |
| 3061 hiddenStart = i; | |
| 3062 } | |
| 3063 | |
| 3064 if ( useHue() ) | |
| 3065 { | |
| 3066 hiddenHueNumer += childHueMin * child.magnitude; | |
| 3067 hiddenHueDenom += child.magnitude; | |
| 3068 } | |
| 3069 else | |
| 3070 { | |
| 3071 hiddenHueNumer += childHueMin; | |
| 3072 hiddenHueDenom++; | |
| 3073 } | |
| 3074 } | |
| 3075 else | |
| 3076 { | |
| 3077 hiddenStart = -1; | |
| 3078 | |
| 3079 this.children[i].setTargetsSelected | |
| 3080 ( | |
| 3081 childHueMin, | |
| 3082 childHueMax, | |
| 3083 lightness, | |
| 3084 hide || this.keyed || this.hideAlone || this.hide && ! collapse, | |
| 3085 false | |
| 3086 ); | |
| 3087 } | |
| 3088 | |
| 3089 i++; | |
| 3090 } | |
| 3091 | |
| 3092 if ( this.hue && this.magnitude ) | |
| 3093 { | |
| 3094 this.hue.setTarget(this.hues[currentDataset]); | |
| 3095 | |
| 3096 if ( this.attributes[magnitudeIndex][lastDataset] == 0 ) | |
| 3097 { | |
| 3098 this.hue.start = this.hue.end; | |
| 3099 } | |
| 3100 } | |
| 3101 | |
| 3102 this.radialPrev = this.radial; | |
| 3103 | |
| 3104 if ( this == selectedNode ) | |
| 3105 { | |
| 3106 this.resetLabelWidth(); | |
| 3107 this.labelWidth.setTarget(this.nameWidth * labelWidthFudge); | |
| 3108 this.alphaWedge.setTarget(0); | |
| 3109 this.alphaLabel.setTarget(1); | |
| 3110 this.alphaOther.setTarget(1); | |
| 3111 this.alphaArc.setTarget(0); | |
| 3112 this.alphaLine.setTarget(0); | |
| 3113 this.alphaPattern.setTarget(0); | |
| 3114 this.r.setTarget(255); | |
| 3115 this.g.setTarget(255); | |
| 3116 this.b.setTarget(255); | |
| 3117 this.radial = false; | |
| 3118 this.labelRadius.setTarget(0); | |
| 3119 } | |
| 3120 else | |
| 3121 { | |
| 3122 var rgb = hslToRgb | |
| 3123 ( | |
| 3124 hueMin, | |
| 3125 saturation, | |
| 3126 lightness | |
| 3127 ); | |
| 3128 | |
| 3129 this.r.setTarget(rgb.r); | |
| 3130 this.g.setTarget(rgb.g); | |
| 3131 this.b.setTarget(rgb.b); | |
| 3132 this.alphaOther.setTarget(0); | |
| 3133 | |
| 3134 this.alphaWedge.setTarget(1); | |
| 3135 | |
| 3136 if ( this.hide || this.hideAlone ) | |
| 3137 { | |
| 3138 this.alphaPattern.setTarget(1); | |
| 3139 } | |
| 3140 else | |
| 3141 { | |
| 3142 this.alphaPattern.setTarget(0); | |
| 3143 } | |
| 3144 | |
| 3145 // set radial | |
| 3146 // | |
| 3147 if ( ! ( hide || this.hide ) )//&& ! this.keyed ) | |
| 3148 { | |
| 3149 if ( this.hideAlone ) | |
| 3150 { | |
| 3151 this.radial = true; | |
| 3152 } | |
| 3153 else if ( false && canDisplayChildLabels ) | |
| 3154 { | |
| 3155 this.radial = false; | |
| 3156 } | |
| 3157 else | |
| 3158 { | |
| 3159 this.radial = true; | |
| 3160 | |
| 3161 if ( this.hasChildren() && depth < maxDisplayDepth ) | |
| 3162 { | |
| 3163 var lastChild = this.children[this.children.length - 1]; | |
| 3164 | |
| 3165 if | |
| 3166 ( | |
| 3167 lastChild.angleEnd.end == this.angleEnd.end || | |
| 3168 ( | |
| 3169 (this.angleStart.end + this.angleEnd.end) / 2 - | |
| 3170 lastChild.angleEnd.end | |
| 3171 ) * (this.radiusInner.end + 1) * gRadius * 2 < | |
| 3172 minWidth() | |
| 3173 ) | |
| 3174 { | |
| 3175 this.radial = false; | |
| 3176 } | |
| 3177 } | |
| 3178 } | |
| 3179 } | |
| 3180 | |
| 3181 // set alphaLabel | |
| 3182 // | |
| 3183 if | |
| 3184 ( | |
| 3185 collapse || | |
| 3186 hide || | |
| 3187 this.hide || | |
| 3188 this.keyed || | |
| 3189 depth > maxDisplayDepth || | |
| 3190 ! this.canDisplayDepth() | |
| 3191 ) | |
| 3192 { | |
| 3193 this.alphaLabel.setTarget(0); | |
| 3194 } | |
| 3195 else | |
| 3196 { | |
| 3197 if | |
| 3198 ( | |
| 3199 (this.radial || nLabelOffsets[depth - 2]) | |
| 3200 ) | |
| 3201 { | |
| 3202 this.alphaLabel.setTarget(1); | |
| 3203 } | |
| 3204 else | |
| 3205 { | |
| 3206 this.alphaLabel.setTarget(0); | |
| 3207 | |
| 3208 if ( this.radialPrev ) | |
| 3209 { | |
| 3210 this.alphaLabel.start = 0; | |
| 3211 } | |
| 3212 } | |
| 3213 } | |
| 3214 | |
| 3215 // set alphaArc | |
| 3216 // | |
| 3217 if | |
| 3218 ( | |
| 3219 collapse || | |
| 3220 hide || | |
| 3221 depth > maxDisplayDepth || | |
| 3222 ! this.canDisplayDepth() | |
| 3223 ) | |
| 3224 { | |
| 3225 this.alphaArc.setTarget(0); | |
| 3226 } | |
| 3227 else | |
| 3228 { | |
| 3229 this.alphaArc.setTarget(1); | |
| 3230 } | |
| 3231 | |
| 3232 // set alphaLine | |
| 3233 // | |
| 3234 if | |
| 3235 ( | |
| 3236 hide || | |
| 3237 this.hide && nextSiblingHidden || | |
| 3238 depth > maxDisplayDepth || | |
| 3239 ! this.canDisplayDepth() | |
| 3240 ) | |
| 3241 { | |
| 3242 this.alphaLine.setTarget(0); | |
| 3243 } | |
| 3244 else | |
| 3245 { | |
| 3246 this.alphaLine.setTarget(1); | |
| 3247 } | |
| 3248 | |
| 3249 //if ( ! this.radial ) | |
| 3250 { | |
| 3251 this.resetLabelWidth(); | |
| 3252 } | |
| 3253 | |
| 3254 // set labelRadius target | |
| 3255 // | |
| 3256 if ( collapse ) | |
| 3257 { | |
| 3258 this.labelRadius.setTarget(this.radiusInner.end); | |
| 3259 } | |
| 3260 else | |
| 3261 { | |
| 3262 if ( depth > maxDisplayDepth || ! this.canDisplayDepth() ) | |
| 3263 { | |
| 3264 this.labelRadius.setTarget(1); | |
| 3265 } | |
| 3266 else | |
| 3267 { | |
| 3268 this.setTargetLabelRadius(); | |
| 3269 } | |
| 3270 } | |
| 3271 } | |
| 3272 } | |
| 3273 | |
| 3274 this.setTargetWedge = function() | |
| 3275 { | |
| 3276 var depth = this.getDepth() - selectedNode.getDepth() + 1; | |
| 3277 | |
| 3278 // set angles | |
| 3279 // | |
| 3280 var baseMagnitudeRelative = this.baseMagnitude - selectedNode.baseMagnitude; | |
| 3281 // | |
| 3282 this.angleStart.setTarget(baseMagnitudeRelative * angleFactor); | |
| 3283 this.angleEnd.setTarget((baseMagnitudeRelative + this.magnitude) * angleFactor); | |
| 3284 | |
| 3285 // set radiusInner | |
| 3286 // | |
| 3287 if ( depth > maxDisplayDepth || ! this.canDisplayDepth() ) | |
| 3288 { | |
| 3289 this.radiusInner.setTarget(1); | |
| 3290 } | |
| 3291 else | |
| 3292 { | |
| 3293 if ( compress ) | |
| 3294 { | |
| 3295 this.radiusInner.setTarget(compressedRadii[depth - 2]); | |
| 3296 } | |
| 3297 else | |
| 3298 { | |
| 3299 this.radiusInner.setTarget(nodeRadius * (depth - 1)); | |
| 3300 } | |
| 3301 } | |
| 3302 | |
| 3303 if ( this.hide != undefined ) | |
| 3304 { | |
| 3305 this.hidePrev = this.hide; | |
| 3306 } | |
| 3307 | |
| 3308 if ( this.hideAlone != undefined ) | |
| 3309 { | |
| 3310 this.hideAlonePrev = this.hideAlone; | |
| 3311 } | |
| 3312 | |
| 3313 // set hide | |
| 3314 // | |
| 3315 if | |
| 3316 ( | |
| 3317 (this.angleEnd.end - this.angleStart.end) * | |
| 3318 (this.radiusInner.end * gRadius + gRadius) < | |
| 3319 minWidth() | |
| 3320 ) | |
| 3321 { | |
| 3322 if ( depth == 2 && ! this.getCollapse() && this.depth <= maxAbsoluteDepth ) | |
| 3323 { | |
| 3324 this.keyed = true; | |
| 3325 keys++; | |
| 3326 this.hide = false; | |
| 3327 | |
| 3328 var percentage = this.getPercentage(); | |
| 3329 this.keyLabel = this.name + ' ' + percentage + '%'; | |
| 3330 var dim = context.measureText(this.keyLabel); | |
| 3331 this.keyNameWidth = dim.width; | |
| 3332 } | |
| 3333 else | |
| 3334 { | |
| 3335 this.keyed = false; | |
| 3336 this.hide = depth > 2; | |
| 3337 } | |
| 3338 } | |
| 3339 else | |
| 3340 { | |
| 3341 this.keyed = false; | |
| 3342 this.hide = false; | |
| 3343 } | |
| 3344 } | |
| 3345 | |
| 3346 this.shortenLabel = function() | |
| 3347 { | |
| 3348 var label = this.name; | |
| 3349 | |
| 3350 var labelWidth = this.nameWidth; | |
| 3351 var maxWidth = this.labelWidth.current(); | |
| 3352 var minEndLength = 0; | |
| 3353 | |
| 3354 if ( labelWidth > maxWidth && label.length > minEndLength * 2 ) | |
| 3355 { | |
| 3356 var endLength = | |
| 3357 Math.floor((label.length - 1) * maxWidth / labelWidth / 2); | |
| 3358 | |
| 3359 if ( endLength < minEndLength ) | |
| 3360 { | |
| 3361 endLength = minEndLength; | |
| 3362 } | |
| 3363 | |
| 3364 return ( | |
| 3365 label.substring(0, endLength) + | |
| 3366 '...' + | |
| 3367 label.substring(label.length - endLength)); | |
| 3368 } | |
| 3369 else | |
| 3370 { | |
| 3371 return label; | |
| 3372 } | |
| 3373 } | |
| 3374 | |
| 3375 /* this.shouldAddSearchResultsString = function() | |
| 3376 { | |
| 3377 if ( this.isSearchResult ) | |
| 3378 { | |
| 3379 return this.searchResults > 1; | |
| 3380 } | |
| 3381 else | |
| 3382 { | |
| 3383 return this.searchResults > 0; | |
| 3384 } | |
| 3385 } | |
| 3386 */ | |
| 3387 this.sort = function() | |
| 3388 { | |
| 3389 this.children.sort(function(a, b){return b.getMagnitude() - a.getMagnitude()}); | |
| 3390 | |
| 3391 for (var i = 0; i < this.children.length; i++) | |
| 3392 { | |
| 3393 this.children[i].sort(); | |
| 3394 } | |
| 3395 } | |
| 3396 } | |
| 3397 | |
| 3398 var options; | |
| 3399 | |
| 3400 function addOptionElement(position, innerHTML, title) | |
| 3401 { | |
| 3402 var div = document.createElement("div"); | |
| 3403 // div.style.position = 'absolute'; | |
| 3404 // div.style.top = position + 'px'; | |
| 3405 div.innerHTML = innerHTML; | |
| 3406 // div.style.display = 'block'; | |
| 3407 div.style.padding = '2px'; | |
| 3408 | |
| 3409 if ( title ) | |
| 3410 { | |
| 3411 div.title = title; | |
| 3412 } | |
| 3413 | |
| 3414 options.appendChild(div); | |
| 3415 var height = 0;//div.clientHeight; | |
| 3416 return position + height; | |
| 3417 } | |
| 3418 | |
| 3419 function addOptionElements(hueName, hueDefault) | |
| 3420 { | |
| 3421 options = document.createElement('div'); | |
| 3422 options.style.position = 'absolute'; | |
| 3423 options.style.top = '0px'; | |
| 3424 options.addEventListener('mousedown', function(e) {mouseClick(e)}, false); | |
| 3425 // options.onmouseup = function(e) {mouseUp(e)} | |
| 3426 document.body.appendChild(options); | |
| 3427 | |
| 3428 document.body.style.font = '11px sans-serif'; | |
| 3429 var position = 5; | |
| 3430 | |
| 3431 details = document.createElement('div'); | |
| 3432 details.style.position = 'absolute'; | |
| 3433 details.style.top = '1%'; | |
| 3434 details.style.right = '2%'; | |
| 3435 details.style.textAlign = 'right'; | |
| 3436 document.body.insertBefore(details, canvas); | |
| 3437 // <div id="details" style="position:absolute;top:1%;right:2%;text-align:right;"> | |
| 3438 | |
| 3439 details.innerHTML = '\ | |
| 3440 <span id="detailsName" style="font-weight:bold"></span> \ | |
| 3441 <input type="button" id="detailsExpand" onclick="expand(focusNode);"\ | |
| 3442 value="↔" title="Expand this wedge to become the new focus of the chart"/><br/>\ | |
| 3443 <div id="detailsInfo" style="float:right"></div>'; | |
| 3444 | |
| 3445 keyControl = document.createElement('input'); | |
| 3446 keyControl.type = 'button'; | |
| 3447 keyControl.value = showKeys ? 'x' : '…'; | |
| 3448 keyControl.style.position = ''; | |
| 3449 keyControl.style.position = 'fixed'; | |
| 3450 keyControl.style.visibility = 'hidden'; | |
| 3451 | |
| 3452 document.body.insertBefore(keyControl, canvas); | |
| 3453 | |
| 3454 var logoElement = document.getElementById('logo'); | |
| 3455 | |
| 3456 if ( logoElement ) | |
| 3457 { | |
| 3458 logoImage = logoElement.src; | |
| 3459 } | |
| 3460 else | |
| 3461 { | |
| 3462 logoImage = 'http://krona.sourceforge.net/img/logo.png'; | |
| 3463 } | |
| 3464 | |
| 3465 // document.getElementById('options').style.fontSize = '9pt'; | |
| 3466 position = addOptionElement | |
| 3467 ( | |
| 3468 position, | |
| 3469 '<a style="margin:2px" target="_blank" href="http://krona.sourceforge.net"><div style="display:inline-block;vertical-align:middle;background-color:#EEEEEE;border:1px solid gray;padding:2px;font-size:18px"><img style="vertical-align:middle;" src="' + logoImage + '"/><span style="vertical-align:middle;color:#555555">Krona</span></div></a><input type="button" id="back" value="←" title="Go back (Shortcut: ←)"/>\ | |
| 3470 <input type="button" id="forward" value="→" title="Go forward (Shortcut: →)"/> \ | |
| 3471 Search: <input type="text" id="search"/>\ | |
| 3472 <input id="searchClear" type="button" value="x" onclick="clearSearch()"/> \ | |
| 3473 <span id="searchResults"></span>' | |
| 3474 ); | |
| 3475 | |
| 3476 if ( datasets > 1 ) | |
| 3477 { | |
| 3478 var size = datasets < datasetSelectSize ? datasets : datasetSelectSize; | |
| 3479 | |
| 3480 var select = | |
| 3481 '<table style="border-collapse:collapse;padding:0px"><tr><td style="padding:0px">' + | |
| 3482 '<select id="datasets" style="min-width:100px" size="' + size + '" onchange="onDatasetChange()">'; | |
| 3483 | |
| 3484 for ( var i = 0; i < datasetNames.length; i++ ) | |
| 3485 { | |
| 3486 select += '<option>' + datasetNames[i] + '</option>'; | |
| 3487 } | |
| 3488 | |
| 3489 select += | |
| 3490 '</select></td><td style="vertical-align:top;padding:1px;">' + | |
| 3491 '<input style="display:block" title="Previous dataset (Shortcut: ↑)" id="prevDataset" type="button" value="↑" onclick="prevDataset()" disabled="true"/>' + | |
| 3492 '<input title="Next dataset (Shortcut: ↓)" id="nextDataset" type="button" value="↓" onclick="nextDataset()"/><br/></td>' + | |
| 3493 '<td style="padding-top:1px;vertical-align:top"><input title="Switch to the last dataset that was viewed (Shortcut: TAB)" id="lastDataset" type="button" style="font:11px Times new roman" value="last" onclick="selectLastDataset()"/></td></tr></table>'; | |
| 3494 | |
| 3495 position = addOptionElement(position + 5, select); | |
| 3496 | |
| 3497 datasetDropDown = document.getElementById('datasets'); | |
| 3498 datasetButtonLast = document.getElementById('lastDataset'); | |
| 3499 datasetButtonPrev = document.getElementById('prevDataset'); | |
| 3500 datasetButtonNext = document.getElementById('nextDataset'); | |
| 3501 | |
| 3502 position += datasetDropDown.clientHeight; | |
| 3503 } | |
| 3504 | |
| 3505 position = addOptionElement | |
| 3506 ( | |
| 3507 position + 5, | |
| 3508 '<input type="button" id="maxAbsoluteDepthDecrease" value="-"/>\ | |
| 3509 <span id="maxAbsoluteDepth"></span>\ | |
| 3510 <input type="button" id="maxAbsoluteDepthIncrease" value="+"/> Max depth', | |
| 3511 'Maximum depth to display, counted from the top level \ | |
| 3512 and including collapsed wedges.' | |
| 3513 ); | |
| 3514 | |
| 3515 position = addOptionElement | |
| 3516 ( | |
| 3517 position, | |
| 3518 '<input type="button" id="fontSizeDecrease" value="-"/>\ | |
| 3519 <span id="fontSize"></span>\ | |
| 3520 <input type="button" id="fontSizeIncrease" value="+"/> Font size' | |
| 3521 ); | |
| 3522 | |
| 3523 position = addOptionElement | |
| 3524 ( | |
| 3525 position, | |
| 3526 '<input type="button" id="radiusDecrease" value="-"/>\ | |
| 3527 <input type="button" id="radiusIncrease" value="+"/> Chart size' | |
| 3528 ); | |
| 3529 | |
| 3530 if ( hueName ) | |
| 3531 { | |
| 3532 hueDisplayName = attributes[attributeIndex(hueName)].displayName; | |
| 3533 | |
| 3534 position = addOptionElement | |
| 3535 ( | |
| 3536 position + 5, | |
| 3537 '<input type="checkbox" id="useHue" style="float:left" ' + | |
| 3538 '/><div>Color by<br/>' + hueDisplayName + | |
| 3539 '</div>' | |
| 3540 ); | |
| 3541 | |
| 3542 useHueCheckBox = document.getElementById('useHue'); | |
| 3543 useHueCheckBox.checked = hueDefault; | |
| 3544 useHueCheckBox.onclick = handleResize; | |
| 3545 useHueCheckBox.onmousedown = suppressEvent; | |
| 3546 } | |
| 3547 /* | |
| 3548 position = addOptionElement | |
| 3549 ( | |
| 3550 position + 5, | |
| 3551 ' <input type="checkbox" id="shorten" checked="checked" />Shorten labels</div>', | |
| 3552 'Prevent labels from overlapping by shortening them' | |
| 3553 ); | |
| 3554 | |
| 3555 position = addOptionElement | |
| 3556 ( | |
| 3557 position, | |
| 3558 ' <input type="checkbox" id="compress" checked="checked" />Compress', | |
| 3559 'Compress wedges if needed to show the entire depth' | |
| 3560 ); | |
| 3561 */ | |
| 3562 position = addOptionElement | |
| 3563 ( | |
| 3564 position, | |
| 3565 '<input type="checkbox" id="collapse" checked="checked" />Collapse', | |
| 3566 'Collapse wedges that are redundant (entirely composed of another wedge)' | |
| 3567 ); | |
| 3568 | |
| 3569 position = addOptionElement | |
| 3570 ( | |
| 3571 position + 5, | |
| 3572 '<input type="button" id="snapshot" value="Snapshot"/>', | |
| 3573 'Render the current view as SVG (Scalable Vector Graphics), a publication-\ | |
| 3574 quality format that can be printed and saved (see Help for browser compatibility)' | |
| 3575 ); | |
| 3576 | |
| 3577 position = addOptionElement | |
| 3578 ( | |
| 3579 position + 5, | |
| 3580 '<input type="button" id="linkButton" value="Link"/>\ | |
| 3581 <input type="text" size="30" id="linkText"/>', | |
| 3582 'Show a link to this view that can be copied for bookmarking or sharing' | |
| 3583 ); | |
| 3584 | |
| 3585 position = addOptionElement | |
| 3586 ( | |
| 3587 position + 5, | |
| 3588 '<input type="button" id="help" value="?"\ | |
| 3589 onclick="window.open(\'https://sourceforge.net/p/krona/wiki/Browsing%20Krona%20charts/\', \'help\')"/>', | |
| 3590 'Help' | |
| 3591 ); | |
| 3592 } | |
| 3593 | |
| 3594 function arrow(angleStart, angleEnd, radiusInner) | |
| 3595 { | |
| 3596 if ( context.globalAlpha == 0 ) | |
| 3597 { | |
| 3598 return; | |
| 3599 } | |
| 3600 | |
| 3601 var angleCenter = (angleStart + angleEnd) / 2; | |
| 3602 var radiusArrowInner = radiusInner - gRadius / 10;//nodeRadius * gRadius; | |
| 3603 var radiusArrowOuter = gRadius * 1.1;//(1 + nodeRadius); | |
| 3604 var radiusArrowCenter = (radiusArrowInner + radiusArrowOuter) / 2; | |
| 3605 var pointLength = (radiusArrowOuter - radiusArrowInner) / 5; | |
| 3606 | |
| 3607 context.fillStyle = highlightFill; | |
| 3608 context.lineWidth = highlightLineWidth; | |
| 3609 | |
| 3610 // First, mask out the first half of the arrow. This will prevent the tips | |
| 3611 // from superimposing if the arrow goes most of the way around the circle. | |
| 3612 // Masking is done by setting the clipping region to the inverse of the | |
| 3613 // half-arrow, which is defined by cutting the half-arrow out of a large | |
| 3614 // rectangle | |
| 3615 // | |
| 3616 context.beginPath(); | |
| 3617 context.arc(0, 0, radiusInner, angleCenter, angleEnd, false); | |
| 3618 context.lineTo | |
| 3619 ( | |
| 3620 radiusArrowInner * Math.cos(angleEnd), | |
| 3621 radiusArrowInner * Math.sin(angleEnd) | |
| 3622 ); | |
| 3623 context.lineTo | |
| 3624 ( | |
| 3625 radiusArrowCenter * Math.cos(angleEnd) - pointLength * Math.sin(angleEnd), | |
| 3626 radiusArrowCenter * Math.sin(angleEnd) + pointLength * Math.cos(angleEnd) | |
| 3627 ); | |
| 3628 context.lineTo | |
| 3629 ( | |
| 3630 radiusArrowOuter * Math.cos(angleEnd), | |
| 3631 radiusArrowOuter * Math.sin(angleEnd) | |
| 3632 ); | |
| 3633 context.arc(0, 0, gRadius, angleEnd, angleCenter, true); | |
| 3634 context.closePath(); | |
| 3635 context.moveTo(-imageWidth, -imageHeight); | |
| 3636 context.lineTo(imageWidth, -imageHeight); | |
| 3637 context.lineTo(imageWidth, imageHeight); | |
| 3638 context.lineTo(-imageWidth, imageHeight); | |
| 3639 context.closePath(); | |
| 3640 context.save(); | |
| 3641 context.clip(); | |
| 3642 | |
| 3643 // Next, draw the other half-arrow with the first half masked out | |
| 3644 // | |
| 3645 context.beginPath(); | |
| 3646 context.arc(0, 0, radiusInner, angleCenter, angleStart, true); | |
| 3647 context.lineTo | |
| 3648 ( | |
| 3649 radiusArrowInner * Math.cos(angleStart), | |
| 3650 radiusArrowInner * Math.sin(angleStart) | |
| 3651 ); | |
| 3652 context.lineTo | |
| 3653 ( | |
| 3654 radiusArrowCenter * Math.cos(angleStart) + pointLength * Math.sin(angleStart), | |
| 3655 radiusArrowCenter * Math.sin(angleStart) - pointLength * Math.cos(angleStart) | |
| 3656 ); | |
| 3657 context.lineTo | |
| 3658 ( | |
| 3659 radiusArrowOuter * Math.cos(angleStart), | |
| 3660 radiusArrowOuter * Math.sin(angleStart) | |
| 3661 ); | |
| 3662 context.arc(0, 0, gRadius, angleStart, angleCenter, false); | |
| 3663 context.fill(); | |
| 3664 context.stroke(); | |
| 3665 | |
| 3666 // Finally, remove the clipping region and draw the first half-arrow. This | |
| 3667 // half is extended slightly to fill the seam. | |
| 3668 // | |
| 3669 context.restore(); | |
| 3670 context.beginPath(); | |
| 3671 context.arc(0, 0, radiusInner, angleCenter - 2 / (2 * Math.PI * radiusInner), angleEnd, false); | |
| 3672 context.lineTo | |
| 3673 ( | |
| 3674 radiusArrowInner * Math.cos(angleEnd), | |
| 3675 radiusArrowInner * Math.sin(angleEnd) | |
| 3676 ); | |
| 3677 context.lineTo | |
| 3678 ( | |
| 3679 radiusArrowCenter * Math.cos(angleEnd) - pointLength * Math.sin(angleEnd), | |
| 3680 radiusArrowCenter * Math.sin(angleEnd) + pointLength * Math.cos(angleEnd) | |
| 3681 ); | |
| 3682 context.lineTo | |
| 3683 ( | |
| 3684 radiusArrowOuter * Math.cos(angleEnd), | |
| 3685 radiusArrowOuter * Math.sin(angleEnd) | |
| 3686 ); | |
| 3687 context.arc(0, 0, gRadius, angleEnd, angleCenter - 2 / (2 * Math.PI * gRadius), true); | |
| 3688 context.fill(); | |
| 3689 context.stroke(); | |
| 3690 } | |
| 3691 | |
| 3692 function attributeIndex(aname) | |
| 3693 { | |
| 3694 for ( var i = 0 ; i < attributes.length; i++ ) | |
| 3695 { | |
| 3696 if ( aname == attributes[i].name ) | |
| 3697 { | |
| 3698 return i; | |
| 3699 } | |
| 3700 } | |
| 3701 | |
| 3702 return null; | |
| 3703 } | |
| 3704 | |
| 3705 function checkHighlight() | |
| 3706 { | |
| 3707 var lastHighlightedNode = highlightedNode; | |
| 3708 var lastHighlightingHidden = highlightingHidden; | |
| 3709 | |
| 3710 highlightedNode = selectedNode; | |
| 3711 resetKeyOffset(); | |
| 3712 | |
| 3713 if ( progress == 1 ) | |
| 3714 { | |
| 3715 selectedNode.checkHighlight(); | |
| 3716 if ( selectedNode.getParent() ) | |
| 3717 { | |
| 3718 selectedNode.getParent().checkHighlightCenter(); | |
| 3719 } | |
| 3720 | |
| 3721 focusNode.checkHighlightMap(); | |
| 3722 } | |
| 3723 | |
| 3724 if ( highlightedNode != selectedNode ) | |
| 3725 { | |
| 3726 if ( highlightedNode == focusNode ) | |
| 3727 { | |
| 3728 // canvas.style.display='none'; | |
| 3729 // window.resizeBy(1,0); | |
| 3730 // canvas.style.cursor='ew-resize'; | |
| 3731 // window.resizeBy(-1,0); | |
| 3732 // canvas.style.display='inline'; | |
| 3733 } | |
| 3734 else | |
| 3735 { | |
| 3736 // canvas.style.cursor='pointer'; | |
| 3737 } | |
| 3738 } | |
| 3739 else | |
| 3740 { | |
| 3741 // canvas.style.cursor='auto'; | |
| 3742 } | |
| 3743 | |
| 3744 if | |
| 3745 ( | |
| 3746 ( | |
| 3747 true || | |
| 3748 highlightedNode != lastHighlightedNode || | |
| 3749 highlightingHidden != highlightingHiddenLast | |
| 3750 ) && | |
| 3751 progress == 1 | |
| 3752 ) | |
| 3753 { | |
| 3754 draw(); // TODO: handle in update() | |
| 3755 } | |
| 3756 } | |
| 3757 | |
| 3758 function checkSelectedCollapse() | |
| 3759 { | |
| 3760 var newNode = selectedNode; | |
| 3761 | |
| 3762 while ( newNode.getCollapse() ) | |
| 3763 { | |
| 3764 newNode = newNode.children[0]; | |
| 3765 } | |
| 3766 | |
| 3767 if ( newNode.children.length == 0 ) | |
| 3768 { | |
| 3769 newNode = newNode.getParent(); | |
| 3770 } | |
| 3771 | |
| 3772 if ( newNode != selectedNode ) | |
| 3773 { | |
| 3774 selectNode(newNode); | |
| 3775 } | |
| 3776 } | |
| 3777 | |
| 3778 function clearSearch() | |
| 3779 { | |
| 3780 if ( search.value != '' ) | |
| 3781 { | |
| 3782 search.value = ''; | |
| 3783 onSearchChange(); | |
| 3784 } | |
| 3785 } | |
| 3786 | |
| 3787 function createSVG() | |
| 3788 { | |
| 3789 svgNS = "http://www.w3.org/2000/svg"; | |
| 3790 var SVG = {}; | |
| 3791 SVG.xlinkns = "http://www.w3.org/1999/xlink"; | |
| 3792 | |
| 3793 var newSVG = document.createElementNS(svgNS, "svg:svg"); | |
| 3794 | |
| 3795 newSVG.setAttribute("id", "canvas"); | |
| 3796 // How big is the canvas in pixels | |
| 3797 newSVG.setAttribute("width", '100%'); | |
| 3798 newSVG.setAttribute("height", '100%'); | |
| 3799 // Set the coordinates used by drawings in the canvas | |
| 3800 // newSVG.setAttribute("viewBox", "0 0 " + imageWidth + " " + imageHeight); | |
| 3801 // Define the XLink namespace that SVG uses | |
| 3802 newSVG.setAttributeNS | |
| 3803 ( | |
| 3804 "http://www.w3.org/2000/xmlns/", | |
| 3805 "xmlns:xlink", | |
| 3806 SVG.xlinkns | |
| 3807 ); | |
| 3808 | |
| 3809 return newSVG; | |
| 3810 } | |
| 3811 | |
| 3812 function degrees(radians) | |
| 3813 { | |
| 3814 return radians * 180 / Math.PI; | |
| 3815 } | |
| 3816 | |
| 3817 function draw() | |
| 3818 { | |
| 3819 tweenFrames++; | |
| 3820 //resize(); | |
| 3821 // context.fillRect(0, 0, imageWidth, imageHeight); | |
| 3822 context.clearRect(0, 0, imageWidth, imageHeight); | |
| 3823 | |
| 3824 context.font = fontNormal; | |
| 3825 context.textBaseline = 'middle'; | |
| 3826 | |
| 3827 //context.strokeStyle = 'rgba(0, 0, 0, 0.3)'; | |
| 3828 context.translate(centerX, centerY); | |
| 3829 | |
| 3830 resetKeyOffset(); | |
| 3831 | |
| 3832 head.draw(false, false); // draw pie slices | |
| 3833 head.draw(true, false); // draw labels | |
| 3834 | |
| 3835 var pathRoot = selectedNode; | |
| 3836 | |
| 3837 if ( focusNode != 0 && focusNode != selectedNode ) | |
| 3838 { | |
| 3839 context.globalAlpha = 1; | |
| 3840 focusNode.drawHighlight(true); | |
| 3841 pathRoot = focusNode; | |
| 3842 } | |
| 3843 | |
| 3844 if | |
| 3845 ( | |
| 3846 highlightedNode && | |
| 3847 highlightedNode.getDepth() >= selectedNode.getDepth() && | |
| 3848 highlightedNode != focusNode | |
| 3849 ) | |
| 3850 { | |
| 3851 if | |
| 3852 ( | |
| 3853 progress == 1 && | |
| 3854 highlightedNode != selectedNode && | |
| 3855 ( | |
| 3856 highlightedNode != focusNode || | |
| 3857 focusNode.children.length > 0 | |
| 3858 ) | |
| 3859 ) | |
| 3860 { | |
| 3861 context.globalAlpha = 1; | |
| 3862 highlightedNode.drawHighlight(true); | |
| 3863 } | |
| 3864 | |
| 3865 //pathRoot = highlightedNode; | |
| 3866 } | |
| 3867 else if | |
| 3868 ( | |
| 3869 progress == 1 && | |
| 3870 highlightedNode.getDepth() < selectedNode.getDepth() | |
| 3871 ) | |
| 3872 { | |
| 3873 context.globalAlpha = 1; | |
| 3874 highlightedNode.drawHighlightCenter(); | |
| 3875 } | |
| 3876 | |
| 3877 if ( quickLook && false) // TEMP | |
| 3878 { | |
| 3879 context.globalAlpha = 1 - progress / 2; | |
| 3880 selectedNode.drawHighlight(true); | |
| 3881 } | |
| 3882 else if ( progress < 1 )//&& zoomOut() ) | |
| 3883 { | |
| 3884 if ( !zoomOut)//() ) | |
| 3885 { | |
| 3886 context.globalAlpha = selectedNode.alphaLine.current(); | |
| 3887 selectedNode.drawHighlight(true); | |
| 3888 } | |
| 3889 else if ( selectedNodeLast ) | |
| 3890 { | |
| 3891 context.globalAlpha = 1 - 4 * Math.pow(progress - .5, 2); | |
| 3892 selectedNodeLast.drawHighlight(false); | |
| 3893 } | |
| 3894 } | |
| 3895 | |
| 3896 drawDatasetName(); | |
| 3897 | |
| 3898 //drawHistory(); | |
| 3899 | |
| 3900 context.translate(-centerX, -centerY); | |
| 3901 context.globalAlpha = 1; | |
| 3902 | |
| 3903 mapRadius = | |
| 3904 (imageHeight / 2 - details.clientHeight - details.offsetTop) / | |
| 3905 (pathRoot.getDepth() - 1) * 3 / 4 / 2; | |
| 3906 | |
| 3907 if ( mapRadius > maxMapRadius ) | |
| 3908 { | |
| 3909 mapRadius = maxMapRadius; | |
| 3910 } | |
| 3911 | |
| 3912 mapBuffer = mapRadius / 2; | |
| 3913 | |
| 3914 //context.font = fontNormal; | |
| 3915 pathRoot.drawMap(pathRoot); | |
| 3916 | |
| 3917 if ( hueDisplayName && useHue() ) | |
| 3918 { | |
| 3919 drawLegend(); | |
| 3920 } | |
| 3921 } | |
| 3922 | |
| 3923 function drawBubble(angle, radius, width, radial, flip) | |
| 3924 { | |
| 3925 var height = fontSize * 2; | |
| 3926 var x; | |
| 3927 var y; | |
| 3928 | |
| 3929 width = width + fontSize; | |
| 3930 | |
| 3931 if ( radial ) | |
| 3932 { | |
| 3933 y = -fontSize; | |
| 3934 | |
| 3935 if ( flip ) | |
| 3936 { | |
| 3937 x = radius - width + fontSize / 2; | |
| 3938 } | |
| 3939 else | |
| 3940 { | |
| 3941 x = radius - fontSize / 2; | |
| 3942 } | |
| 3943 } | |
| 3944 else | |
| 3945 { | |
| 3946 x = -width / 2; | |
| 3947 y = -radius - fontSize; | |
| 3948 } | |
| 3949 | |
| 3950 if ( snapshotMode ) | |
| 3951 { | |
| 3952 drawBubbleSVG(x + centerX, y + centerY, width, height, fontSize, angle); | |
| 3953 } | |
| 3954 else | |
| 3955 { | |
| 3956 drawBubbleCanvas(x, y, width, height, fontSize, angle); | |
| 3957 } | |
| 3958 } | |
| 3959 | |
| 3960 function drawBubbleCanvas(x, y, width, height, radius, rotation) | |
| 3961 { | |
| 3962 context.strokeStyle = 'black'; | |
| 3963 context.lineWidth = highlightLineWidth; | |
| 3964 context.fillStyle = 'rgba(255, 255, 255, .75)'; | |
| 3965 context.rotate(rotation); | |
| 3966 roundedRectangle(x, y, width, fontSize * 2, fontSize); | |
| 3967 context.fill(); | |
| 3968 context.stroke(); | |
| 3969 context.rotate(-rotation); | |
| 3970 } | |
| 3971 | |
| 3972 function drawBubbleSVG(x, y, width, height, radius, rotation) | |
| 3973 { | |
| 3974 svg += | |
| 3975 '<rect x="' + x + '" y="' + y + | |
| 3976 '" width="' + width + | |
| 3977 '" height="' + height + | |
| 3978 '" rx="' + radius + | |
| 3979 '" ry="' + radius + | |
| 3980 '" fill="rgba(255, 255, 255, .75)' + | |
| 3981 '" class="highlight" ' + | |
| 3982 'transform="rotate(' + | |
| 3983 degrees(rotation) + ',' + centerX + ',' + centerY + | |
| 3984 ')"/>'; | |
| 3985 } | |
| 3986 | |
| 3987 function drawDatasetName() | |
| 3988 { | |
| 3989 var alpha = datasetAlpha.current(); | |
| 3990 | |
| 3991 if ( alpha > 0 ) | |
| 3992 { | |
| 3993 var radius = gRadius * compressedRadii[0] / -2; | |
| 3994 | |
| 3995 if ( alpha > 1 ) | |
| 3996 { | |
| 3997 alpha = 1; | |
| 3998 } | |
| 3999 | |
| 4000 context.globalAlpha = alpha; | |
| 4001 | |
| 4002 drawBubble(0, -radius, datasetWidths[currentDataset], false, false); | |
| 4003 drawText(datasetNames[currentDataset], 0, radius, 0, 'center', true); | |
| 4004 } | |
| 4005 } | |
| 4006 | |
| 4007 function drawHistory() | |
| 4008 { | |
| 4009 var alpha = 1; | |
| 4010 context.textAlign = 'center'; | |
| 4011 | |
| 4012 for ( var i = 0; i < nodeHistoryPosition && alpha > 0; i++ ) | |
| 4013 { | |
| 4014 | |
| 4015 context.globalAlpha = alpha - historyAlphaDelta * tweenFactor; | |
| 4016 context.fillText | |
| 4017 ( | |
| 4018 nodeHistory[nodeHistoryPosition - i - 1].name, | |
| 4019 0, | |
| 4020 (i + tweenFactor) * historySpacingFactor * fontSize - 1 | |
| 4021 ); | |
| 4022 | |
| 4023 if ( alpha > 0 ) | |
| 4024 { | |
| 4025 alpha -= historyAlphaDelta; | |
| 4026 } | |
| 4027 } | |
| 4028 | |
| 4029 context.globalAlpha = 1; | |
| 4030 } | |
| 4031 | |
| 4032 function drawLegend() | |
| 4033 { | |
| 4034 var left = imageWidth * .01; | |
| 4035 var width = imageHeight * .0265; | |
| 4036 var height = imageHeight * .15; | |
| 4037 var top = imageHeight - fontSize * 3.5 - height; | |
| 4038 var textLeft = left + width + fontSize / 2; | |
| 4039 | |
| 4040 context.fillStyle = 'black'; | |
| 4041 context.textAlign = 'start'; | |
| 4042 context.font = fontNormal; | |
| 4043 // context.fillText(valueStartText, textLeft, top + height); | |
| 4044 // context.fillText(valueEndText, textLeft, top); | |
| 4045 context.fillText(hueDisplayName, left, imageHeight - fontSize * 1.5); | |
| 4046 | |
| 4047 var gradient = context.createLinearGradient(0, top + height, 0, top); | |
| 4048 | |
| 4049 for ( var i = 0; i < hueStopPositions.length; i++ ) | |
| 4050 { | |
| 4051 gradient.addColorStop(hueStopPositions[i], hueStopHsl[i]); | |
| 4052 | |
| 4053 var textY = top + (1 - hueStopPositions[i]) * height; | |
| 4054 | |
| 4055 if | |
| 4056 ( | |
| 4057 i == 0 || | |
| 4058 i == hueStopPositions.length - 1 || | |
| 4059 textY > top + fontSize && textY < top + height - fontSize | |
| 4060 ) | |
| 4061 { | |
| 4062 context.fillText(hueStopText[i], textLeft, textY); | |
| 4063 } | |
| 4064 } | |
| 4065 | |
| 4066 context.fillStyle = gradient; | |
| 4067 context.fillRect(left, top, width, height); | |
| 4068 context.lineWidth = thinLineWidth; | |
| 4069 context.strokeRect(left, top, width, height); | |
| 4070 } | |
| 4071 | |
| 4072 function drawLegendSVG() | |
| 4073 { | |
| 4074 var left = imageWidth * .01; | |
| 4075 var width = imageHeight * .0265; | |
| 4076 var height = imageHeight * .15; | |
| 4077 var top = imageHeight - fontSize * 3.5 - height; | |
| 4078 var textLeft = left + width + fontSize / 2; | |
| 4079 | |
| 4080 var text = ''; | |
| 4081 | |
| 4082 text += svgText(hueDisplayName, left, imageHeight - fontSize * 1.5); | |
| 4083 | |
| 4084 var svgtest = '<linearGradient id="gradient" x1="0%" y1="100%" x2="0%" y2="0%">'; | |
| 4085 | |
| 4086 for ( var i = 0; i < hueStopPositions.length; i++ ) | |
| 4087 { | |
| 4088 svgtest += | |
| 4089 '<stop offset="' + round(hueStopPositions[i] * 100) + | |
| 4090 '%" style="stop-color:' + hueStopHsl[i] + '"/>'; | |
| 4091 | |
| 4092 var textY = top + (1 - hueStopPositions[i]) * height; | |
| 4093 | |
| 4094 if | |
| 4095 ( | |
| 4096 i == 0 || | |
| 4097 i == hueStopPositions.length - 1 || | |
| 4098 textY > top + fontSize && textY < top + height - fontSize | |
| 4099 ) | |
| 4100 { | |
| 4101 text += svgText(hueStopText[i], textLeft, textY); | |
| 4102 } | |
| 4103 } | |
| 4104 | |
| 4105 svgtest += '</linearGradient>'; | |
| 4106 //alert(svgtest); | |
| 4107 svg += svgtest; | |
| 4108 svg += | |
| 4109 '<rect style="fill:url(#gradient)" x="' + left + '" y="' + top + | |
| 4110 '" width="' + width + '" height="' + height + '"/>'; | |
| 4111 | |
| 4112 svg += text; | |
| 4113 } | |
| 4114 | |
| 4115 function drawSearchHighlights(label, bubbleX, bubbleY, rotation, center) | |
| 4116 { | |
| 4117 var index = -1; | |
| 4118 var labelLength = label.length; | |
| 4119 | |
| 4120 bubbleX -= fontSize / 4; | |
| 4121 | |
| 4122 do | |
| 4123 { | |
| 4124 index = label.toLowerCase().indexOf(search.value.toLowerCase(), index + 1); | |
| 4125 | |
| 4126 if ( index != -1 && index < labelLength ) | |
| 4127 { | |
| 4128 var dim = context.measureText(label.substr(0, index)); | |
| 4129 var x = bubbleX + dim.width; | |
| 4130 | |
| 4131 dim = context.measureText(label.substr(index, search.value.length)); | |
| 4132 | |
| 4133 var y = bubbleY - fontSize * 3 / 4; | |
| 4134 var width = dim.width + fontSize / 2; | |
| 4135 var height = fontSize * 3 / 2; | |
| 4136 var radius = fontSize / 2; | |
| 4137 | |
| 4138 if ( snapshotMode ) | |
| 4139 { | |
| 4140 if ( center ) | |
| 4141 { | |
| 4142 x += centerX; | |
| 4143 y += centerY; | |
| 4144 } | |
| 4145 | |
| 4146 svg += | |
| 4147 '<rect x="' + x + '" y="' + y + | |
| 4148 '" width="' + width + | |
| 4149 '" height="' + height + | |
| 4150 '" rx="' + radius + | |
| 4151 '" ry="' + radius + | |
| 4152 '" class="searchHighlight' + | |
| 4153 '" transform="rotate(' + | |
| 4154 degrees(rotation) + ',' + centerX + ',' + centerY + | |
| 4155 ')"/>'; | |
| 4156 } | |
| 4157 else | |
| 4158 { | |
| 4159 context.fillStyle = 'rgb(255, 255, 100)'; | |
| 4160 context.rotate(rotation); | |
| 4161 roundedRectangle(x, y, width, height, radius); | |
| 4162 context.fill(); | |
| 4163 context.rotate(-rotation); | |
| 4164 } | |
| 4165 } | |
| 4166 } | |
| 4167 while ( index != -1 && index < labelLength ); | |
| 4168 } | |
| 4169 | |
| 4170 function drawText(text, x, y, angle, anchor, bold, color) | |
| 4171 { | |
| 4172 if ( color == undefined ) | |
| 4173 { | |
| 4174 color = 'black'; | |
| 4175 } | |
| 4176 | |
| 4177 if ( snapshotMode ) | |
| 4178 { | |
| 4179 svg += | |
| 4180 '<text x="' + (centerX + x) + '" y="' + (centerY + y) + | |
| 4181 '" text-anchor="' + anchor + '" style="font-color:' + color + ';font-weight:' + (bold ? 'bold' : 'normal') + | |
| 4182 '" transform="rotate(' + degrees(angle) + ',' + centerX + ',' + centerY + ')">' + | |
| 4183 text + '</text>'; | |
| 4184 } | |
| 4185 else | |
| 4186 { | |
| 4187 context.fillStyle = color; | |
| 4188 context.textAlign = anchor; | |
| 4189 context.font = bold ? fontBold : fontNormal; | |
| 4190 context.rotate(angle); | |
| 4191 context.fillText(text, x, y); | |
| 4192 context.rotate(-angle); | |
| 4193 } | |
| 4194 } | |
| 4195 | |
| 4196 function drawTextPolar | |
| 4197 ( | |
| 4198 text, | |
| 4199 innerText, | |
| 4200 angle, | |
| 4201 radius, | |
| 4202 radial, | |
| 4203 bubble, | |
| 4204 bold, | |
| 4205 searchResult, | |
| 4206 searchResults | |
| 4207 ) | |
| 4208 { | |
| 4209 var anchor; | |
| 4210 var textX; | |
| 4211 var textY; | |
| 4212 var spacer; | |
| 4213 var totalText = text; | |
| 4214 var flip; | |
| 4215 | |
| 4216 if ( snapshotMode ) | |
| 4217 { | |
| 4218 spacer = '   '; | |
| 4219 } | |
| 4220 else | |
| 4221 { | |
| 4222 spacer = ' '; | |
| 4223 } | |
| 4224 | |
| 4225 if ( radial ) | |
| 4226 { | |
| 4227 flip = angle < 3 * Math.PI / 2; | |
| 4228 | |
| 4229 if ( flip ) | |
| 4230 { | |
| 4231 angle -= Math.PI; | |
| 4232 radius = -radius; | |
| 4233 anchor = 'end'; | |
| 4234 | |
| 4235 if ( innerText ) | |
| 4236 { | |
| 4237 totalText = text + spacer + innerText; | |
| 4238 } | |
| 4239 } | |
| 4240 else | |
| 4241 { | |
| 4242 anchor = 'start'; | |
| 4243 | |
| 4244 if ( innerText ) | |
| 4245 { | |
| 4246 totalText = innerText + spacer + text; | |
| 4247 } | |
| 4248 } | |
| 4249 | |
| 4250 textX = radius; | |
| 4251 textY = 0; | |
| 4252 } | |
| 4253 else | |
| 4254 { | |
| 4255 flip = angle < Math.PI || angle > 2 * Math.PI; | |
| 4256 var label; | |
| 4257 | |
| 4258 anchor = snapshotMode ? 'middle' : 'center'; | |
| 4259 | |
| 4260 if ( flip ) | |
| 4261 { | |
| 4262 angle -= Math.PI; | |
| 4263 radius = -radius; | |
| 4264 } | |
| 4265 | |
| 4266 angle += Math.PI / 2; | |
| 4267 textX = 0; | |
| 4268 textY = -radius; | |
| 4269 } | |
| 4270 | |
| 4271 if ( bubble ) | |
| 4272 { | |
| 4273 var textActual = totalText; | |
| 4274 | |
| 4275 if ( innerText && snapshotMode ) | |
| 4276 { | |
| 4277 if ( flip ) | |
| 4278 { | |
| 4279 textActual = text + ' ' + innerText; | |
| 4280 } | |
| 4281 else | |
| 4282 { | |
| 4283 textActual = innerText + ' ' + text; | |
| 4284 } | |
| 4285 } | |
| 4286 | |
| 4287 if ( searchResults ) | |
| 4288 { | |
| 4289 textActual = textActual + searchResultString(searchResults); | |
| 4290 } | |
| 4291 | |
| 4292 var textWidth = measureText(textActual, bold); | |
| 4293 | |
| 4294 var x = textX; | |
| 4295 | |
| 4296 if ( anchor == 'end' ) | |
| 4297 { | |
| 4298 x -= textWidth; | |
| 4299 } | |
| 4300 else if ( anchor != 'start' ) | |
| 4301 { | |
| 4302 // centered | |
| 4303 x -= textWidth / 2; | |
| 4304 } | |
| 4305 | |
| 4306 drawBubble(angle, radius, textWidth, radial, flip); | |
| 4307 | |
| 4308 if ( searchResult ) | |
| 4309 { | |
| 4310 drawSearchHighlights | |
| 4311 ( | |
| 4312 textActual, | |
| 4313 x, | |
| 4314 textY, | |
| 4315 angle, | |
| 4316 true | |
| 4317 ) | |
| 4318 } | |
| 4319 } | |
| 4320 | |
| 4321 if ( searchResults ) | |
| 4322 { | |
| 4323 totalText = totalText + searchResultString(searchResults); | |
| 4324 } | |
| 4325 | |
| 4326 drawText(totalText, textX, textY, angle, anchor, bold); | |
| 4327 | |
| 4328 return flip; | |
| 4329 } | |
| 4330 | |
| 4331 function drawTick(start, length, angle) | |
| 4332 { | |
| 4333 if ( snapshotMode ) | |
| 4334 { | |
| 4335 svg += | |
| 4336 '<line x1="' + (centerX + start) + | |
| 4337 '" y1="' + centerY + | |
| 4338 '" x2="' + (centerX + start + length) + | |
| 4339 '" y2="' + centerY + | |
| 4340 '" class="tick" transform="rotate(' + | |
| 4341 degrees(angle) + ',' + centerX + ',' + centerY + | |
| 4342 ')"/>'; | |
| 4343 } | |
| 4344 else | |
| 4345 { | |
| 4346 context.rotate(angle); | |
| 4347 context.beginPath(); | |
| 4348 context.moveTo(start, 0); | |
| 4349 context.lineTo(start + length, 0); | |
| 4350 context.lineWidth = thinLineWidth * 2; | |
| 4351 context.stroke(); | |
| 4352 context.rotate(-angle); | |
| 4353 } | |
| 4354 } | |
| 4355 | |
| 4356 function drawWedge | |
| 4357 ( | |
| 4358 angleStart, | |
| 4359 angleEnd, | |
| 4360 radiusInner, | |
| 4361 radiusOuter, | |
| 4362 color, | |
| 4363 patternAlpha, | |
| 4364 highlight | |
| 4365 ) | |
| 4366 { | |
| 4367 if ( context.globalAlpha == 0 ) | |
| 4368 { | |
| 4369 return; | |
| 4370 } | |
| 4371 | |
| 4372 if ( snapshotMode ) | |
| 4373 { | |
| 4374 if ( angleEnd == angleStart + Math.PI * 2 ) | |
| 4375 { | |
| 4376 // fudge to prevent overlap, which causes arc ambiguity | |
| 4377 // | |
| 4378 angleEnd -= .1 / gRadius; | |
| 4379 } | |
| 4380 | |
| 4381 var longArc = angleEnd - angleStart > Math.PI ? 1 : 0; | |
| 4382 | |
| 4383 var x1 = centerX + radiusInner * Math.cos(angleStart); | |
| 4384 var y1 = centerY + radiusInner * Math.sin(angleStart); | |
| 4385 | |
| 4386 var x2 = centerX + gRadius * Math.cos(angleStart); | |
| 4387 var y2 = centerY + gRadius * Math.sin(angleStart); | |
| 4388 | |
| 4389 var x3 = centerX + gRadius * Math.cos(angleEnd); | |
| 4390 var y3 = centerY + gRadius * Math.sin(angleEnd); | |
| 4391 | |
| 4392 var x4 = centerX + radiusInner * Math.cos(angleEnd); | |
| 4393 var y4 = centerY + radiusInner * Math.sin(angleEnd); | |
| 4394 | |
| 4395 var dArray = | |
| 4396 [ | |
| 4397 " M ", x1, ",", y1, | |
| 4398 " L ", x2, ",", y2, | |
| 4399 " A ", gRadius, ",", gRadius, " 0 ", longArc, ",1 ", x3, ",", y3, | |
| 4400 " L ", x4, ",", y4, | |
| 4401 " A ", radiusInner, ",", radiusInner, " 0 ", longArc, " 0 ", x1, ",", y1, | |
| 4402 " Z " | |
| 4403 ]; | |
| 4404 | |
| 4405 svg += | |
| 4406 '<path class="'+ (highlight ? 'highlight' : 'wedge') + '" fill="' + color + | |
| 4407 '" d="' + dArray.join('') + '"/>'; | |
| 4408 | |
| 4409 if ( patternAlpha > 0 ) | |
| 4410 { | |
| 4411 svg += | |
| 4412 '<path class="wedge" fill="url(#hiddenPattern)" d="' + | |
| 4413 dArray.join('') + '"/>'; | |
| 4414 } | |
| 4415 } | |
| 4416 else | |
| 4417 { | |
| 4418 // fudge to prevent seams during animation | |
| 4419 // | |
| 4420 angleEnd += 1 / gRadius; | |
| 4421 | |
| 4422 context.fillStyle = color; | |
| 4423 context.beginPath(); | |
| 4424 context.arc(0, 0, radiusInner, angleStart, angleEnd, false); | |
| 4425 context.arc(0, 0, radiusOuter, angleEnd, angleStart, true); | |
| 4426 context.closePath(); | |
| 4427 context.fill(); | |
| 4428 | |
| 4429 if ( patternAlpha > 0 ) | |
| 4430 { | |
| 4431 context.save(); | |
| 4432 context.clip(); | |
| 4433 context.globalAlpha = patternAlpha; | |
| 4434 context.fillStyle = hiddenPattern; | |
| 4435 context.fill(); | |
| 4436 context.restore(); | |
| 4437 } | |
| 4438 | |
| 4439 if ( highlight ) | |
| 4440 { | |
| 4441 context.lineWidth = highlight ? highlightLineWidth : thinLineWidth; | |
| 4442 context.strokeStyle = 'black'; | |
| 4443 context.stroke(); | |
| 4444 } | |
| 4445 } | |
| 4446 } | |
| 4447 | |
| 4448 function expand(node) | |
| 4449 { | |
| 4450 selectNode(node); | |
| 4451 updateView(); | |
| 4452 } | |
| 4453 | |
| 4454 function focusLost() | |
| 4455 { | |
| 4456 mouseX = -1; | |
| 4457 mouseY = -1; | |
| 4458 checkHighlight(); | |
| 4459 document.body.style.cursor = 'auto'; | |
| 4460 } | |
| 4461 | |
| 4462 function fontSizeDecrease() | |
| 4463 { | |
| 4464 if ( fontSize > 1 ) | |
| 4465 { | |
| 4466 fontSize--; | |
| 4467 updateViewNeeded = true; | |
| 4468 } | |
| 4469 } | |
| 4470 | |
| 4471 function fontSizeIncrease() | |
| 4472 { | |
| 4473 fontSize++; | |
| 4474 updateViewNeeded = true; | |
| 4475 } | |
| 4476 | |
| 4477 function getGetString(name, value, bool) | |
| 4478 { | |
| 4479 return name + '=' + (bool ? value ? 'true' : 'false' : value); | |
| 4480 } | |
| 4481 | |
| 4482 function hideLink() | |
| 4483 { | |
| 4484 hide(linkText); | |
| 4485 show(linkButton); | |
| 4486 } | |
| 4487 | |
| 4488 function show(object) | |
| 4489 { | |
| 4490 object.style.display = 'inline'; | |
| 4491 } | |
| 4492 | |
| 4493 function hide(object) | |
| 4494 { | |
| 4495 object.style.display = 'none'; | |
| 4496 } | |
| 4497 | |
| 4498 function showLink() | |
| 4499 { | |
| 4500 var urlHalves = String(document.location).split('?'); | |
| 4501 var newGetVariables = new Array(); | |
| 4502 | |
| 4503 newGetVariables.push | |
| 4504 ( | |
| 4505 getGetString('dataset', currentDataset, false), | |
| 4506 getGetString('node', selectedNode.id, false), | |
| 4507 getGetString('collapse', collapse, true), | |
| 4508 getGetString('color', useHue(), true), | |
| 4509 getGetString('depth', maxAbsoluteDepth - 1, false), | |
| 4510 getGetString('font', fontSize, false), | |
| 4511 getGetString('key', showKeys, true) | |
| 4512 ); | |
| 4513 | |
| 4514 hide(linkButton); | |
| 4515 show(linkText); | |
| 4516 linkText.value = urlHalves[0] + '?' + getVariables.concat(newGetVariables).join('&'); | |
| 4517 //linkText.disabled = false; | |
| 4518 linkText.focus(); | |
| 4519 linkText.select(); | |
| 4520 //linkText.disabled = true; | |
| 4521 // document.location = urlHalves[0] + '?' + getVariables.join('&'); | |
| 4522 } | |
| 4523 | |
| 4524 function getFirstChild(element) | |
| 4525 { | |
| 4526 element = element.firstChild; | |
| 4527 | |
| 4528 if ( element && element.nodeType != 1 ) | |
| 4529 { | |
| 4530 element = getNextSibling(element); | |
| 4531 } | |
| 4532 | |
| 4533 return element; | |
| 4534 } | |
| 4535 | |
| 4536 function getNextSibling(element) | |
| 4537 { | |
| 4538 do | |
| 4539 { | |
| 4540 element = element.nextSibling; | |
| 4541 } | |
| 4542 while ( element && element.nodeType != 1 ); | |
| 4543 | |
| 4544 return element; | |
| 4545 } | |
| 4546 | |
| 4547 function getPercentage(fraction) | |
| 4548 { | |
| 4549 return round(fraction * 100); | |
| 4550 } | |
| 4551 | |
| 4552 function hslText(hue) | |
| 4553 { | |
| 4554 if ( 1 || snapshotMode ) | |
| 4555 { | |
| 4556 // Safari doesn't seem to allow hsl() in SVG | |
| 4557 | |
| 4558 var rgb = hslToRgb(hue, saturation, (lightnessBase + lightnessMax) / 2); | |
| 4559 | |
| 4560 return rgbText(rgb.r, rgb.g, rgb.b); | |
| 4561 } | |
| 4562 else | |
| 4563 { | |
| 4564 var hslArray = | |
| 4565 [ | |
| 4566 'hsl(', | |
| 4567 Math.floor(hue * 360), | |
| 4568 ',', | |
| 4569 Math.floor(saturation * 100), | |
| 4570 '%,', | |
| 4571 Math.floor((lightnessBase + lightnessMax) * 50), | |
| 4572 '%)' | |
| 4573 ]; | |
| 4574 | |
| 4575 return hslArray.join(''); | |
| 4576 } | |
| 4577 } | |
| 4578 | |
| 4579 function hslToRgb(h, s, l) | |
| 4580 { | |
| 4581 var m1, m2; | |
| 4582 var r, g, b; | |
| 4583 | |
| 4584 if (s == 0) | |
| 4585 { | |
| 4586 r = g = b = Math.floor((l * 255)); | |
| 4587 } | |
| 4588 else | |
| 4589 { | |
| 4590 if (l <= 0.5) | |
| 4591 { | |
| 4592 m2 = l * (s + 1); | |
| 4593 } | |
| 4594 else | |
| 4595 { | |
| 4596 m2 = l + s - l * s; | |
| 4597 } | |
| 4598 | |
| 4599 m1 = l * 2 - m2; | |
| 4600 | |
| 4601 r = Math.floor(hueToRgb(m1, m2, h + 1 / 3)); | |
| 4602 g = Math.floor(hueToRgb(m1, m2, h)); | |
| 4603 b = Math.floor(hueToRgb(m1, m2, h - 1/3)); | |
| 4604 } | |
| 4605 | |
| 4606 return {r: r, g: g, b: b}; | |
| 4607 } | |
| 4608 | |
| 4609 function hueToRgb(m1, m2, hue) | |
| 4610 { | |
| 4611 var v; | |
| 4612 | |
| 4613 while (hue < 0) | |
| 4614 { | |
| 4615 hue += 1; | |
| 4616 } | |
| 4617 | |
| 4618 while (hue > 1) | |
| 4619 { | |
| 4620 hue -= 1; | |
| 4621 } | |
| 4622 | |
| 4623 if (6 * hue < 1) | |
| 4624 v = m1 + (m2 - m1) * hue * 6; | |
| 4625 else if (2 * hue < 1) | |
| 4626 v = m2; | |
| 4627 else if (3 * hue < 2) | |
| 4628 v = m1 + (m2 - m1) * (2/3 - hue) * 6; | |
| 4629 else | |
| 4630 v = m1; | |
| 4631 | |
| 4632 return 255 * v; | |
| 4633 } | |
| 4634 | |
| 4635 function interpolateHue(hueStart, hueEnd, valueStart, valueEnd) | |
| 4636 { | |
| 4637 // since the gradient will be RGB based, we need to add stops to hit all the | |
| 4638 // colors in the hue spectrum | |
| 4639 | |
| 4640 hueStopPositions = new Array(); | |
| 4641 hueStopHsl = new Array(); | |
| 4642 hueStopText = new Array(); | |
| 4643 | |
| 4644 hueStopPositions.push(0); | |
| 4645 hueStopHsl.push(hslText(hueStart)); | |
| 4646 hueStopText.push(round(valueStart)); | |
| 4647 | |
| 4648 for | |
| 4649 ( | |
| 4650 var i = (hueStart > hueEnd ? 5 / 6 : 1 / 6); | |
| 4651 (hueStart > hueEnd ? i > 0 : i < 1); | |
| 4652 i += (hueStart > hueEnd ? -1 : 1) / 6 | |
| 4653 ) | |
| 4654 { | |
| 4655 if | |
| 4656 ( | |
| 4657 hueStart > hueEnd ? | |
| 4658 i > hueEnd && i < hueStart : | |
| 4659 i > hueStart && i < hueEnd | |
| 4660 ) | |
| 4661 { | |
| 4662 hueStopPositions.push(lerp(i, hueStart, hueEnd, 0, 1)); | |
| 4663 hueStopHsl.push(hslText(i)); | |
| 4664 hueStopText.push(round(lerp | |
| 4665 ( | |
| 4666 i, | |
| 4667 hueStart, | |
| 4668 hueEnd, | |
| 4669 valueStart, | |
| 4670 valueEnd | |
| 4671 ))); | |
| 4672 } | |
| 4673 } | |
| 4674 | |
| 4675 hueStopPositions.push(1); | |
| 4676 hueStopHsl.push(hslText(hueEnd)); | |
| 4677 hueStopText.push(round(valueEnd)); | |
| 4678 } | |
| 4679 | |
| 4680 function keyLineAngle(angle, keyAngle, bendRadius, keyX, keyY, pointsX, pointsY) | |
| 4681 { | |
| 4682 if ( angle < Math.PI / 2 && keyY < bendRadius * Math.sin(angle) | |
| 4683 || angle > Math.PI / 2 && keyY < bendRadius) | |
| 4684 { | |
| 4685 return Math.asin(keyY / bendRadius); | |
| 4686 } | |
| 4687 else | |
| 4688 { | |
| 4689 // find the angle of the normal to a tangent line that goes to | |
| 4690 // the label | |
| 4691 | |
| 4692 var textDist = Math.sqrt | |
| 4693 ( | |
| 4694 Math.pow(keyX, 2) + | |
| 4695 Math.pow(keyY, 2) | |
| 4696 ); | |
| 4697 | |
| 4698 var tanAngle = Math.acos(bendRadius / textDist) + keyAngle; | |
| 4699 | |
| 4700 if ( angle < tanAngle || angle < Math.PI / 2 )//|| labelLeft < centerX ) | |
| 4701 { | |
| 4702 // angle doesn't reach far enough for tangent; collapse and | |
| 4703 // connect directly to label | |
| 4704 | |
| 4705 if ( keyY / Math.tan(angle) > 0 ) | |
| 4706 { | |
| 4707 pointsX.push(keyY / Math.tan(angle)); | |
| 4708 pointsY.push(keyY); | |
| 4709 } | |
| 4710 else | |
| 4711 { | |
| 4712 pointsX.push(bendRadius * Math.cos(angle)); | |
| 4713 pointsY.push(bendRadius * Math.sin(angle)); | |
| 4714 } | |
| 4715 | |
| 4716 return angle; | |
| 4717 } | |
| 4718 else | |
| 4719 { | |
| 4720 return tanAngle; | |
| 4721 } | |
| 4722 } | |
| 4723 } | |
| 4724 | |
| 4725 function keyOffset() | |
| 4726 { | |
| 4727 return imageHeight - (keys - currentKey + 1) * (keySize + keyBuffer) + keyBuffer - margin; | |
| 4728 } | |
| 4729 | |
| 4730 function lerp(value, fromStart, fromEnd, toStart, toEnd) | |
| 4731 { | |
| 4732 return (value - fromStart) * | |
| 4733 (toEnd - toStart) / | |
| 4734 (fromEnd - fromStart) + | |
| 4735 toStart; | |
| 4736 } | |
| 4737 | |
| 4738 function createCanvas() | |
| 4739 { | |
| 4740 canvas = document.createElement('canvas'); | |
| 4741 document.body.appendChild(canvas); | |
| 4742 context = canvas.getContext('2d'); | |
| 4743 } | |
| 4744 | |
| 4745 function load() | |
| 4746 { | |
| 4747 document.body.style.overflow = "hidden"; | |
| 4748 document.body.style.margin = 0; | |
| 4749 | |
| 4750 createCanvas(); | |
| 4751 | |
| 4752 if ( context == undefined ) | |
| 4753 { | |
| 4754 document.body.innerHTML = '\ | |
| 4755 <br/>This browser does not support HTML5 (see \ | |
| 4756 <a href="http://sourceforge.net/p/krona/wiki/Browser%20support/">Browser support</a>).\ | |
| 4757 '; | |
| 4758 return; | |
| 4759 } | |
| 4760 | |
| 4761 if ( typeof context.fillText != 'function' ) | |
| 4762 { | |
| 4763 document.body.innerHTML = '\ | |
| 4764 <br/>This browser does not support HTML5 canvas text (see \ | |
| 4765 <a href="http://sourceforge.net/p/krona/wiki/Browser%20support/">Browser support</a>).\ | |
| 4766 '; | |
| 4767 return; | |
| 4768 } | |
| 4769 | |
| 4770 resize(); | |
| 4771 | |
| 4772 var kronaElement = document.getElementsByTagName('krona')[0]; | |
| 4773 | |
| 4774 var magnitudeName; | |
| 4775 var hueName; | |
| 4776 var hueDefault; | |
| 4777 var hueStart; | |
| 4778 var hueEnd; | |
| 4779 var valueStart; | |
| 4780 var valueEnd; | |
| 4781 | |
| 4782 if ( kronaElement.getAttribute('collapse') != undefined ) | |
| 4783 { | |
| 4784 collapse = kronaElement.getAttribute('collapse') == 'true'; | |
| 4785 } | |
| 4786 | |
| 4787 if ( kronaElement.getAttribute('key') != undefined ) | |
| 4788 { | |
| 4789 showKeys = kronaElement.getAttribute('key') == 'true'; | |
| 4790 } | |
| 4791 | |
| 4792 for | |
| 4793 ( | |
| 4794 var element = getFirstChild(kronaElement); | |
| 4795 element; | |
| 4796 element = getNextSibling(element) | |
| 4797 ) | |
| 4798 { | |
| 4799 switch ( element.tagName.toLowerCase() ) | |
| 4800 { | |
| 4801 case 'attributes': | |
| 4802 magnitudeName = element.getAttribute('magnitude'); | |
| 4803 // | |
| 4804 for | |
| 4805 ( | |
| 4806 var attributeElement = getFirstChild(element); | |
| 4807 attributeElement; | |
| 4808 attributeElement = getNextSibling(attributeElement) | |
| 4809 ) | |
| 4810 { | |
| 4811 var tag = attributeElement.tagName.toLowerCase(); | |
| 4812 | |
| 4813 if ( tag == 'attribute' ) | |
| 4814 { | |
| 4815 var attribute = new Attribute(); | |
| 4816 attribute.name = attributeElement.firstChild.nodeValue.toLowerCase(); | |
| 4817 attribute.displayName = attributeElement.getAttribute('display'); | |
| 4818 | |
| 4819 if ( attributeElement.getAttribute('hrefBase') ) | |
| 4820 { | |
| 4821 attribute.hrefBase = attributeElement.getAttribute('hrefBase'); | |
| 4822 } | |
| 4823 | |
| 4824 if ( attributeElement.getAttribute('target') ) | |
| 4825 { | |
| 4826 attribute.target = attributeElement.getAttribute('target'); | |
| 4827 } | |
| 4828 | |
| 4829 if ( attribute.name == magnitudeName ) | |
| 4830 { | |
| 4831 magnitudeIndex = attributes.length; | |
| 4832 } | |
| 4833 | |
| 4834 if ( attributeElement.getAttribute('listAll') ) | |
| 4835 { | |
| 4836 attribute.listAll = attributeElement.getAttribute('listAll').toLowerCase(); | |
| 4837 } | |
| 4838 else if ( attributeElement.getAttribute('listNode') ) | |
| 4839 { | |
| 4840 attribute.listNode = attributeElement.getAttribute('listNode').toLowerCase(); | |
| 4841 } | |
| 4842 else if ( attributeElement.getAttribute('dataAll') ) | |
| 4843 { | |
| 4844 attribute.dataAll = attributeElement.getAttribute('dataAll').toLowerCase(); | |
| 4845 } | |
| 4846 else if ( attributeElement.getAttribute('dataNode') ) | |
| 4847 { | |
| 4848 attribute.dataNode = attributeElement.getAttribute('dataNode').toLowerCase(); | |
| 4849 } | |
| 4850 | |
| 4851 if ( attributeElement.getAttribute('postUrl') ) | |
| 4852 { | |
| 4853 attribute.postUrl = attributeElement.getAttribute('postUrl'); | |
| 4854 } | |
| 4855 | |
| 4856 if ( attributeElement.getAttribute('postVar') ) | |
| 4857 { | |
| 4858 attribute.postVar = attributeElement.getAttribute('postVar'); | |
| 4859 } | |
| 4860 | |
| 4861 if ( attributeElement.getAttribute('mono') ) | |
| 4862 { | |
| 4863 attribute.mono = true; | |
| 4864 } | |
| 4865 | |
| 4866 attributes.push(attribute); | |
| 4867 } | |
| 4868 else if ( tag == 'list' ) | |
| 4869 { | |
| 4870 var attribute = new Attribute(); | |
| 4871 | |
| 4872 attribute.name = attributeElement.firstChild.nodeValue; | |
| 4873 attribute.list = true; | |
| 4874 attributes.push(attribute); | |
| 4875 } | |
| 4876 else if ( tag == 'data' ) | |
| 4877 { | |
| 4878 var attribute = new Attribute(); | |
| 4879 | |
| 4880 attribute.name = attributeElement.firstChild.nodeValue; | |
| 4881 attribute.data = true; | |
| 4882 attributes.push(attribute); | |
| 4883 | |
| 4884 var enableScript = document.createElement('script'); | |
| 4885 var date = new Date(); | |
| 4886 enableScript.src = | |
| 4887 attributeElement.getAttribute('enable') + '?' + | |
| 4888 date.getTime(); | |
| 4889 document.body.appendChild(enableScript); | |
| 4890 } | |
| 4891 } | |
| 4892 break; | |
| 4893 | |
| 4894 case 'color': | |
| 4895 hueName = element.getAttribute('attribute'); | |
| 4896 hueStart = Number(element.getAttribute('hueStart')) / 360; | |
| 4897 hueEnd = Number(element.getAttribute('hueEnd')) / 360; | |
| 4898 valueStart = Number(element.getAttribute('valueStart')); | |
| 4899 valueEnd = Number(element.getAttribute('valueEnd')); | |
| 4900 // | |
| 4901 interpolateHue(hueStart, hueEnd, valueStart, valueEnd); | |
| 4902 // | |
| 4903 if ( element.getAttribute('default') == 'true' ) | |
| 4904 { | |
| 4905 hueDefault = true; | |
| 4906 } | |
| 4907 break; | |
| 4908 | |
| 4909 case 'datasets': | |
| 4910 datasetNames = new Array(); | |
| 4911 // | |
| 4912 for ( j = getFirstChild(element); j; j = getNextSibling(j) ) | |
| 4913 { | |
| 4914 datasetNames.push(j.firstChild.nodeValue); | |
| 4915 } | |
| 4916 datasets = datasetNames.length; | |
| 4917 break; | |
| 4918 | |
| 4919 case 'node': | |
| 4920 head = loadTreeDOM | |
| 4921 ( | |
| 4922 element, | |
| 4923 magnitudeName, | |
| 4924 hueName, | |
| 4925 hueStart, | |
| 4926 hueEnd, | |
| 4927 valueStart, | |
| 4928 valueEnd | |
| 4929 ); | |
| 4930 break; | |
| 4931 } | |
| 4932 } | |
| 4933 | |
| 4934 // get GET options | |
| 4935 // | |
| 4936 var urlHalves = String(document.location).split('?'); | |
| 4937 var datasetDefault = 0; | |
| 4938 var maxDepthDefault; | |
| 4939 var nodeDefault = 0; | |
| 4940 // | |
| 4941 if ( urlHalves[1] ) | |
| 4942 { | |
| 4943 var vars = urlHalves[1].split('&'); | |
| 4944 | |
| 4945 for ( i = 0; i < vars.length; i++ ) | |
| 4946 { | |
| 4947 var pair = vars[i].split('='); | |
| 4948 | |
| 4949 switch ( pair[0] ) | |
| 4950 { | |
| 4951 case 'collapse': | |
| 4952 collapse = pair[1] == 'true'; | |
| 4953 break; | |
| 4954 | |
| 4955 case 'color': | |
| 4956 hueDefault = pair[1] == 'true'; | |
| 4957 break; | |
| 4958 | |
| 4959 case 'dataset': | |
| 4960 datasetDefault = Number(pair[1]); | |
| 4961 break; | |
| 4962 | |
| 4963 case 'depth': | |
| 4964 maxDepthDefault = Number(pair[1]) + 1; | |
| 4965 break; | |
| 4966 | |
| 4967 case 'key': | |
| 4968 showKeys = pair[1] == 'true'; | |
| 4969 break; | |
| 4970 | |
| 4971 case 'font': | |
| 4972 fontSize = Number(pair[1]); | |
| 4973 break; | |
| 4974 | |
| 4975 case 'node': | |
| 4976 nodeDefault = Number(pair[1]); | |
| 4977 break; | |
| 4978 | |
| 4979 default: | |
| 4980 getVariables.push(pair[0] + '=' + pair[1]); | |
| 4981 break; | |
| 4982 } | |
| 4983 } | |
| 4984 } | |
| 4985 | |
| 4986 addOptionElements(hueName, hueDefault); | |
| 4987 setCallBacks(); | |
| 4988 | |
| 4989 head.sort(); | |
| 4990 maxAbsoluteDepth = 0; | |
| 4991 selectDataset(datasetDefault); | |
| 4992 | |
| 4993 if ( maxDepthDefault && maxDepthDefault < head.maxDepth ) | |
| 4994 { | |
| 4995 maxAbsoluteDepth = maxDepthDefault; | |
| 4996 } | |
| 4997 else | |
| 4998 { | |
| 4999 maxAbsoluteDepth = head.maxDepth; | |
| 5000 } | |
| 5001 | |
| 5002 selectNode(nodes[nodeDefault]); | |
| 5003 | |
| 5004 setInterval(update, 20); | |
| 5005 | |
| 5006 window.onresize = handleResize; | |
| 5007 updateMaxAbsoluteDepth(); | |
| 5008 updateViewNeeded = true; | |
| 5009 } | |
| 5010 | |
| 5011 function loadTreeDOM | |
| 5012 ( | |
| 5013 domNode, | |
| 5014 magnitudeName, | |
| 5015 hueName, | |
| 5016 hueStart, | |
| 5017 hueEnd, | |
| 5018 valueStart, | |
| 5019 valueEnd | |
| 5020 ) | |
| 5021 { | |
| 5022 var newNode = new Node(); | |
| 5023 | |
| 5024 newNode.name = domNode.getAttribute('name'); | |
| 5025 | |
| 5026 if ( domNode.getAttribute('href') ) | |
| 5027 { | |
| 5028 newNode.href = domNode.getAttribute('href'); | |
| 5029 } | |
| 5030 | |
| 5031 if ( hueName ) | |
| 5032 { | |
| 5033 newNode.hues = new Array(); | |
| 5034 } | |
| 5035 | |
| 5036 for ( var i = getFirstChild(domNode); i; i = getNextSibling(i) ) | |
| 5037 { | |
| 5038 switch ( i.tagName.toLowerCase() ) | |
| 5039 { | |
| 5040 case 'node': | |
| 5041 var newChild = loadTreeDOM | |
| 5042 ( | |
| 5043 i, | |
| 5044 magnitudeName, | |
| 5045 hueName, | |
| 5046 hueStart, | |
| 5047 hueEnd, | |
| 5048 valueStart, | |
| 5049 valueEnd | |
| 5050 ); | |
| 5051 newChild.parent = newNode; | |
| 5052 newNode.children.push(newChild); | |
| 5053 break; | |
| 5054 | |
| 5055 default: | |
| 5056 var attributeName = i.tagName.toLowerCase(); | |
| 5057 var index = attributeIndex(attributeName); | |
| 5058 // | |
| 5059 newNode.attributes[index] = new Array(); | |
| 5060 // | |
| 5061 for ( var j = getFirstChild(i); j; j = getNextSibling(j) ) | |
| 5062 { | |
| 5063 if ( attributes[index] == undefined ) | |
| 5064 { | |
| 5065 var x = 5; | |
| 5066 } | |
| 5067 if ( attributes[index].list ) | |
| 5068 { | |
| 5069 newNode.attributes[index].push(new Array()); | |
| 5070 | |
| 5071 for ( var k = getFirstChild(j); k; k = getNextSibling(k) ) | |
| 5072 { | |
| 5073 newNode.attributes[index][newNode.attributes[index].length - 1].push(k.firstChild.nodeValue); | |
| 5074 } | |
| 5075 } | |
| 5076 else | |
| 5077 { | |
| 5078 var value = j.firstChild ? j.firstChild.nodeValue : ''; | |
| 5079 | |
| 5080 if ( j.getAttribute('href') ) | |
| 5081 { | |
| 5082 var target; | |
| 5083 | |
| 5084 if ( attributes[index].target ) | |
| 5085 { | |
| 5086 target = ' target="' + attributes[index].target + '"'; | |
| 5087 } | |
| 5088 | |
| 5089 value = '<a href="' + attributes[index].hrefBase + j.getAttribute('href') + '"' + target + '>' + value + '</a>'; | |
| 5090 } | |
| 5091 | |
| 5092 newNode.attributes[index].push(value); | |
| 5093 } | |
| 5094 } | |
| 5095 // | |
| 5096 if ( attributeName == magnitudeName || attributeName == hueName ) | |
| 5097 { | |
| 5098 for ( j = 0; j < datasets; j++ ) | |
| 5099 { | |
| 5100 var value = newNode.attributes[index][j] == undefined ? 0 : Number(newNode.attributes[index][j]); | |
| 5101 | |
| 5102 newNode.attributes[index][j] = value; | |
| 5103 | |
| 5104 if ( attributeName == hueName ) | |
| 5105 { | |
| 5106 var hue = lerp | |
| 5107 ( | |
| 5108 value, | |
| 5109 valueStart, | |
| 5110 valueEnd, | |
| 5111 hueStart, | |
| 5112 hueEnd | |
| 5113 ); | |
| 5114 | |
| 5115 if ( hue < hueStart == hueStart < hueEnd ) | |
| 5116 { | |
| 5117 hue = hueStart; | |
| 5118 } | |
| 5119 else if ( hue > hueEnd == hueStart < hueEnd ) | |
| 5120 { | |
| 5121 hue = hueEnd; | |
| 5122 } | |
| 5123 | |
| 5124 newNode.hues[j] = hue; | |
| 5125 } | |
| 5126 } | |
| 5127 | |
| 5128 if ( attributeName == hueName ) | |
| 5129 { | |
| 5130 newNode.hue = new Tween(newNode.hues[0], newNode.hues[0]); | |
| 5131 } | |
| 5132 } | |
| 5133 break; | |
| 5134 } | |
| 5135 } | |
| 5136 | |
| 5137 return newNode; | |
| 5138 } | |
| 5139 | |
| 5140 function maxAbsoluteDepthDecrease() | |
| 5141 { | |
| 5142 if ( maxAbsoluteDepth > 2 ) | |
| 5143 { | |
| 5144 maxAbsoluteDepth--; | |
| 5145 head.setMaxDepths(); | |
| 5146 handleResize(); | |
| 5147 } | |
| 5148 } | |
| 5149 | |
| 5150 function maxAbsoluteDepthIncrease() | |
| 5151 { | |
| 5152 if ( maxAbsoluteDepth < head.maxDepth ) | |
| 5153 { | |
| 5154 maxAbsoluteDepth++; | |
| 5155 head.setMaxDepths(); | |
| 5156 handleResize(); | |
| 5157 } | |
| 5158 } | |
| 5159 | |
| 5160 function measureText(text, bold) | |
| 5161 { | |
| 5162 context.font = bold ? fontBold : fontNormal; | |
| 5163 var dim = context.measureText(text); | |
| 5164 return dim.width; | |
| 5165 } | |
| 5166 | |
| 5167 function min(a, b) | |
| 5168 { | |
| 5169 return a < b ? a : b; | |
| 5170 } | |
| 5171 | |
| 5172 function minWidth() | |
| 5173 { | |
| 5174 // Min wedge width (at center) for displaying a node (or for displaying a | |
| 5175 // label if it's at the highest level being viewed, multiplied by 2 to make | |
| 5176 // further calculations simpler | |
| 5177 | |
| 5178 return (fontSize * 2.3); | |
| 5179 } | |
| 5180 | |
| 5181 function mouseMove(e) | |
| 5182 { | |
| 5183 mouseX = e.pageX; | |
| 5184 mouseY = e.pageY - headerHeight; | |
| 5185 | |
| 5186 if ( head && ! quickLook ) | |
| 5187 { | |
| 5188 checkHighlight(); | |
| 5189 } | |
| 5190 } | |
| 5191 | |
| 5192 function mouseClick(e) | |
| 5193 { | |
| 5194 if ( highlightedNode == focusNode && focusNode != selectedNode || selectedNode.hasParent(highlightedNode) ) | |
| 5195 { | |
| 5196 if ( highlightedNode.hasChildren() ) | |
| 5197 { | |
| 5198 expand(highlightedNode); | |
| 5199 } | |
| 5200 } | |
| 5201 else if ( progress == 1 )//( highlightedNode != selectedNode ) | |
| 5202 { | |
| 5203 setFocus(highlightedNode); | |
| 5204 // document.body.style.cursor='ew-resize'; | |
| 5205 draw(); | |
| 5206 checkHighlight(); | |
| 5207 var date = new Date(); | |
| 5208 mouseDownTime = date.getTime(); | |
| 5209 mouseDown = true; | |
| 5210 } | |
| 5211 } | |
| 5212 | |
| 5213 function mouseUp(e) | |
| 5214 { | |
| 5215 if ( quickLook ) | |
| 5216 { | |
| 5217 navigateBack(); | |
| 5218 quickLook = false; | |
| 5219 } | |
| 5220 | |
| 5221 mouseDown = false; | |
| 5222 } | |
| 5223 | |
| 5224 function navigateBack() | |
| 5225 { | |
| 5226 if ( nodeHistoryPosition > 0 ) | |
| 5227 { | |
| 5228 nodeHistory[nodeHistoryPosition] = selectedNode; | |
| 5229 nodeHistoryPosition--; | |
| 5230 | |
| 5231 if ( nodeHistory[nodeHistoryPosition].collapse ) | |
| 5232 { | |
| 5233 collapseCheckBox.checked = collapse = false; | |
| 5234 } | |
| 5235 | |
| 5236 setSelectedNode(nodeHistory[nodeHistoryPosition]); | |
| 5237 updateDatasetButtons(); | |
| 5238 updateView(); | |
| 5239 } | |
| 5240 } | |
| 5241 | |
| 5242 function navigateUp() | |
| 5243 { | |
| 5244 if ( selectedNode.getParent() ) | |
| 5245 { | |
| 5246 selectNode(selectedNode.getParent()); | |
| 5247 updateView(); | |
| 5248 } | |
| 5249 } | |
| 5250 | |
| 5251 function navigateForward() | |
| 5252 { | |
| 5253 if ( nodeHistoryPosition < nodeHistory.length - 1 ) | |
| 5254 { | |
| 5255 nodeHistoryPosition++; | |
| 5256 var newNode = nodeHistory[nodeHistoryPosition]; | |
| 5257 | |
| 5258 if ( newNode.collapse ) | |
| 5259 { | |
| 5260 collapseCheckBox.checked = collapse = false; | |
| 5261 } | |
| 5262 | |
| 5263 if ( nodeHistoryPosition == nodeHistory.length - 1 ) | |
| 5264 { | |
| 5265 // this will ensure the forward button is disabled | |
| 5266 | |
| 5267 nodeHistory.length = nodeHistoryPosition; | |
| 5268 } | |
| 5269 | |
| 5270 setSelectedNode(newNode); | |
| 5271 updateDatasetButtons(); | |
| 5272 updateView(); | |
| 5273 } | |
| 5274 } | |
| 5275 | |
| 5276 function nextDataset() | |
| 5277 { | |
| 5278 var newDataset = currentDataset; | |
| 5279 | |
| 5280 do | |
| 5281 { | |
| 5282 if ( newDataset == datasets - 1 ) | |
| 5283 { | |
| 5284 newDataset = 0; | |
| 5285 } | |
| 5286 else | |
| 5287 { | |
| 5288 newDataset++; | |
| 5289 } | |
| 5290 } | |
| 5291 while ( datasetDropDown.options[newDataset].disabled ) | |
| 5292 | |
| 5293 selectDataset(newDataset); | |
| 5294 } | |
| 5295 | |
| 5296 function onDatasetChange() | |
| 5297 { | |
| 5298 selectDataset(datasetDropDown.selectedIndex); | |
| 5299 } | |
| 5300 | |
| 5301 function onKeyDown(event) | |
| 5302 { | |
| 5303 if | |
| 5304 ( | |
| 5305 event.keyCode == 37 && | |
| 5306 document.activeElement.id != 'search' && | |
| 5307 document.activeElement.id != 'linkText' | |
| 5308 ) | |
| 5309 { | |
| 5310 navigateBack(); | |
| 5311 event.preventDefault(); | |
| 5312 } | |
| 5313 else if | |
| 5314 ( | |
| 5315 event.keyCode == 39 && | |
| 5316 document.activeElement.id != 'search' && | |
| 5317 document.activeElement.id != 'linkText' | |
| 5318 ) | |
| 5319 { | |
| 5320 navigateForward(); | |
| 5321 event.preventDefault(); | |
| 5322 } | |
| 5323 else if ( event.keyCode == 38 && datasets > 1 ) | |
| 5324 { | |
| 5325 prevDataset(); | |
| 5326 | |
| 5327 //if ( document.activeElement.id == 'datasets' ) | |
| 5328 { | |
| 5329 event.preventDefault(); | |
| 5330 } | |
| 5331 } | |
| 5332 else if ( event.keyCode == 40 && datasets > 1 ) | |
| 5333 { | |
| 5334 nextDataset(); | |
| 5335 | |
| 5336 //if ( document.activeElement.id == 'datasets' ) | |
| 5337 { | |
| 5338 event.preventDefault(); | |
| 5339 } | |
| 5340 } | |
| 5341 else if ( event.keyCode == 9 && datasets > 1 ) | |
| 5342 { | |
| 5343 selectLastDataset(); | |
| 5344 event.preventDefault(); | |
| 5345 } | |
| 5346 else if ( event.keyCode == 83 ) | |
| 5347 { | |
| 5348 progress += .2; | |
| 5349 } | |
| 5350 else if ( event.keyCode == 66 ) | |
| 5351 { | |
| 5352 progress -= .2; | |
| 5353 } | |
| 5354 else if ( event.keyCode == 70 ) | |
| 5355 { | |
| 5356 progress = 1; | |
| 5357 } | |
| 5358 } | |
| 5359 | |
| 5360 function onKeyPress(event) | |
| 5361 { | |
| 5362 if ( event.keyCode == 38 && datasets > 1 ) | |
| 5363 { | |
| 5364 // prevDataset(); | |
| 5365 | |
| 5366 //if ( document.activeElement.id == 'datasets' ) | |
| 5367 { | |
| 5368 event.preventDefault(); | |
| 5369 } | |
| 5370 } | |
| 5371 else if ( event.keyCode == 40 && datasets > 1 ) | |
| 5372 { | |
| 5373 // nextDataset(); | |
| 5374 | |
| 5375 //if ( document.activeElement.id == 'datasets' ) | |
| 5376 { | |
| 5377 event.preventDefault(); | |
| 5378 } | |
| 5379 } | |
| 5380 } | |
| 5381 | |
| 5382 function onKeyUp(event) | |
| 5383 { | |
| 5384 if ( event.keyCode == 27 && document.activeElement.id == 'search' ) | |
| 5385 { | |
| 5386 search.value = ''; | |
| 5387 onSearchChange(); | |
| 5388 } | |
| 5389 else if ( event.keyCode == 38 && datasets > 1 ) | |
| 5390 { | |
| 5391 // prevDataset(); | |
| 5392 | |
| 5393 //if ( document.activeElement.id == 'datasets' ) | |
| 5394 { | |
| 5395 event.preventDefault(); | |
| 5396 } | |
| 5397 } | |
| 5398 else if ( event.keyCode == 40 && datasets > 1 ) | |
| 5399 { | |
| 5400 // nextDataset(); | |
| 5401 | |
| 5402 //if ( document.activeElement.id == 'datasets' ) | |
| 5403 { | |
| 5404 event.preventDefault(); | |
| 5405 } | |
| 5406 } | |
| 5407 } | |
| 5408 | |
| 5409 function onSearchChange() | |
| 5410 { | |
| 5411 nSearchResults = 0; | |
| 5412 head.search(); | |
| 5413 | |
| 5414 if ( search.value == '' ) | |
| 5415 { | |
| 5416 searchResults.innerHTML = ''; | |
| 5417 } | |
| 5418 else | |
| 5419 { | |
| 5420 searchResults.innerHTML = nSearchResults + ' results'; | |
| 5421 } | |
| 5422 | |
| 5423 setFocus(selectedNode); | |
| 5424 draw(); | |
| 5425 } | |
| 5426 | |
| 5427 function post(url, variable, value, postWindow) | |
| 5428 { | |
| 5429 var form = document.createElement('form'); | |
| 5430 var input = document.createElement('input'); | |
| 5431 var inputDataset = document.createElement('input'); | |
| 5432 | |
| 5433 form.appendChild(input); | |
| 5434 form.appendChild(inputDataset); | |
| 5435 | |
| 5436 form.method = "POST"; | |
| 5437 form.action = url; | |
| 5438 | |
| 5439 if ( postWindow == undefined ) | |
| 5440 { | |
| 5441 form.target = '_blank'; | |
| 5442 postWindow = window; | |
| 5443 } | |
| 5444 | |
| 5445 input.type = 'hidden'; | |
| 5446 input.name = variable; | |
| 5447 input.value = value; | |
| 5448 | |
| 5449 inputDataset.type = 'hidden'; | |
| 5450 inputDataset.name = 'dataset'; | |
| 5451 inputDataset.value = currentDataset; | |
| 5452 | |
| 5453 postWindow.document.body.appendChild(form); | |
| 5454 form.submit(); | |
| 5455 } | |
| 5456 | |
| 5457 function prevDataset() | |
| 5458 { | |
| 5459 var newDataset = currentDataset; | |
| 5460 | |
| 5461 do | |
| 5462 { | |
| 5463 if ( newDataset == 0 ) | |
| 5464 { | |
| 5465 newDataset = datasets - 1; | |
| 5466 } | |
| 5467 else | |
| 5468 { | |
| 5469 newDataset--; | |
| 5470 } | |
| 5471 } | |
| 5472 while ( datasetDropDown.options[newDataset].disabled ); | |
| 5473 | |
| 5474 selectDataset(newDataset); | |
| 5475 } | |
| 5476 | |
| 5477 function radiusDecrease() | |
| 5478 { | |
| 5479 if ( bufferFactor < .309 ) | |
| 5480 { | |
| 5481 bufferFactor += .03; | |
| 5482 updateViewNeeded = true; | |
| 5483 } | |
| 5484 } | |
| 5485 | |
| 5486 function radiusIncrease() | |
| 5487 { | |
| 5488 if ( bufferFactor > .041 ) | |
| 5489 { | |
| 5490 bufferFactor -= .03; | |
| 5491 updateViewNeeded = true; | |
| 5492 } | |
| 5493 } | |
| 5494 | |
| 5495 function resetKeyOffset() | |
| 5496 { | |
| 5497 currentKey = 1; | |
| 5498 keyMinTextLeft = centerX + gRadius + buffer - buffer / (keys + 1) / 2 + fontSize / 2; | |
| 5499 keyMinAngle = 0; | |
| 5500 } | |
| 5501 | |
| 5502 function rgbText(r, g, b) | |
| 5503 { | |
| 5504 var rgbArray = | |
| 5505 [ | |
| 5506 "rgb(", | |
| 5507 Math.floor(r), | |
| 5508 ",", | |
| 5509 Math.floor(g), | |
| 5510 ",", | |
| 5511 Math.floor(b), | |
| 5512 ")" | |
| 5513 ]; | |
| 5514 | |
| 5515 return rgbArray.join(''); | |
| 5516 } | |
| 5517 | |
| 5518 function round(number) | |
| 5519 { | |
| 5520 if ( number >= 1 || number <= -1 ) | |
| 5521 { | |
| 5522 return number.toFixed(0); | |
| 5523 } | |
| 5524 else | |
| 5525 { | |
| 5526 return number.toPrecision(1); | |
| 5527 } | |
| 5528 } | |
| 5529 | |
| 5530 function roundedRectangle(x, y, width, height, radius) | |
| 5531 { | |
| 5532 if ( radius * 2 > width ) | |
| 5533 { | |
| 5534 radius = width / 2; | |
| 5535 } | |
| 5536 | |
| 5537 if ( radius * 2 > height ) | |
| 5538 { | |
| 5539 radius = height / 2; | |
| 5540 } | |
| 5541 | |
| 5542 context.beginPath(); | |
| 5543 context.arc(x + radius, y + radius, radius, Math.PI, Math.PI * 3 / 2, false); | |
| 5544 context.lineTo(x + width - radius, y); | |
| 5545 context.arc(x + width - radius, y + radius, radius, Math.PI * 3 / 2, Math.PI * 2, false); | |
| 5546 context.lineTo(x + width, y + height - radius); | |
| 5547 context.arc(x + width - radius, y + height - radius, radius, 0, Math.PI / 2, false); | |
| 5548 context.lineTo(x + radius, y + height); | |
| 5549 context.arc(x + radius, y + height - radius, radius, Math.PI / 2, Math.PI, false); | |
| 5550 context.lineTo(x, y + radius); | |
| 5551 } | |
| 5552 | |
| 5553 function passClick(e) | |
| 5554 { | |
| 5555 mouseClick(e); | |
| 5556 } | |
| 5557 | |
| 5558 function searchResultString(results) | |
| 5559 { | |
| 5560 var searchResults = this.searchResults; | |
| 5561 | |
| 5562 if ( this.isSearchResult ) | |
| 5563 { | |
| 5564 // don't count ourselves | |
| 5565 searchResults--; | |
| 5566 } | |
| 5567 | |
| 5568 return ' - ' + results + (results > 1 ? ' results' : ' result'); | |
| 5569 } | |
| 5570 | |
| 5571 function setCallBacks() | |
| 5572 { | |
| 5573 canvas.onselectstart = function(){return false;} // prevent unwanted highlighting | |
| 5574 options.onselectstart = function(){return false;} // prevent unwanted highlighting | |
| 5575 document.onmousemove = mouseMove; | |
| 5576 window.onblur = focusLost; | |
| 5577 window.onmouseout = focusLost; | |
| 5578 document.onkeyup = onKeyUp; | |
| 5579 document.onkeydown = onKeyDown; | |
| 5580 canvas.onmousedown = mouseClick; | |
| 5581 document.onmouseup = mouseUp; | |
| 5582 keyControl.onclick = toggleKeys; | |
| 5583 collapseCheckBox = document.getElementById('collapse'); | |
| 5584 collapseCheckBox.checked = collapse; | |
| 5585 collapseCheckBox.onclick = handleResize; | |
| 5586 collapseCheckBox.onmousedown = suppressEvent; | |
| 5587 maxAbsoluteDepthText = document.getElementById('maxAbsoluteDepth'); | |
| 5588 maxAbsoluteDepthButtonDecrease = document.getElementById('maxAbsoluteDepthDecrease'); | |
| 5589 maxAbsoluteDepthButtonIncrease = document.getElementById('maxAbsoluteDepthIncrease'); | |
| 5590 maxAbsoluteDepthButtonDecrease.onclick = maxAbsoluteDepthDecrease; | |
| 5591 maxAbsoluteDepthButtonIncrease.onclick = maxAbsoluteDepthIncrease; | |
| 5592 maxAbsoluteDepthButtonDecrease.onmousedown = suppressEvent; | |
| 5593 maxAbsoluteDepthButtonIncrease.onmousedown = suppressEvent; | |
| 5594 fontSizeText = document.getElementById('fontSize'); | |
| 5595 fontSizeButtonDecrease = document.getElementById('fontSizeDecrease'); | |
| 5596 fontSizeButtonIncrease = document.getElementById('fontSizeIncrease'); | |
| 5597 fontSizeButtonDecrease.onclick = fontSizeDecrease; | |
| 5598 fontSizeButtonIncrease.onclick = fontSizeIncrease; | |
| 5599 fontSizeButtonDecrease.onmousedown = suppressEvent; | |
| 5600 fontSizeButtonIncrease.onmousedown = suppressEvent; | |
| 5601 radiusButtonDecrease = document.getElementById('radiusDecrease'); | |
| 5602 radiusButtonIncrease = document.getElementById('radiusIncrease'); | |
| 5603 radiusButtonDecrease.onclick = radiusDecrease; | |
| 5604 radiusButtonIncrease.onclick = radiusIncrease; | |
| 5605 radiusButtonDecrease.onmousedown = suppressEvent; | |
| 5606 radiusButtonIncrease.onmousedown = suppressEvent; | |
| 5607 maxAbsoluteDepth = 0; | |
| 5608 backButton = document.getElementById('back'); | |
| 5609 backButton.onclick = navigateBack; | |
| 5610 backButton.onmousedown = suppressEvent; | |
| 5611 forwardButton = document.getElementById('forward'); | |
| 5612 forwardButton.onclick = navigateForward; | |
| 5613 forwardButton.onmousedown = suppressEvent; | |
| 5614 snapshotButton = document.getElementById('snapshot'); | |
| 5615 snapshotButton.onclick = snapshot; | |
| 5616 snapshotButton.onmousedown = suppressEvent; | |
| 5617 detailsName = document.getElementById('detailsName'); | |
| 5618 detailsExpand = document.getElementById('detailsExpand'); | |
| 5619 detailsInfo = document.getElementById('detailsInfo'); | |
| 5620 search = document.getElementById('search'); | |
| 5621 search.onkeyup = onSearchChange; | |
| 5622 search.onmousedown = suppressEvent; | |
| 5623 searchResults = document.getElementById('searchResults'); | |
| 5624 useHueDiv = document.getElementById('useHueDiv'); | |
| 5625 linkButton = document.getElementById('linkButton'); | |
| 5626 linkButton.onclick = showLink; | |
| 5627 linkButton.onmousedown = suppressEvent; | |
| 5628 linkText = document.getElementById('linkText'); | |
| 5629 linkText.onblur = hideLink; | |
| 5630 linkText.onmousedown = suppressEvent; | |
| 5631 hide(linkText); | |
| 5632 var helpButton = document.getElementById('help'); | |
| 5633 helpButton.onmousedown = suppressEvent; | |
| 5634 var searchClear = document.getElementById('searchClear'); | |
| 5635 searchClear.onmousedown = suppressEvent; | |
| 5636 if ( datasets > 1 ) | |
| 5637 { | |
| 5638 datasetDropDown.onmousedown = suppressEvent; | |
| 5639 var prevDatasetButton = document.getElementById('prevDataset'); | |
| 5640 prevDatasetButton.onmousedown = suppressEvent; | |
| 5641 var nextDatasetButton = document.getElementById('nextDataset'); | |
| 5642 nextDatasetButton.onmousedown = suppressEvent; | |
| 5643 var lastDatasetButton = document.getElementById('lastDataset'); | |
| 5644 lastDatasetButton.onmousedown = suppressEvent; | |
| 5645 } | |
| 5646 | |
| 5647 image = document.getElementById('hiddenImage'); | |
| 5648 | |
| 5649 if ( image.complete ) | |
| 5650 { | |
| 5651 hiddenPattern = context.createPattern(image, 'repeat'); | |
| 5652 } | |
| 5653 else | |
| 5654 { | |
| 5655 image.onload = function() | |
| 5656 { | |
| 5657 hiddenPattern = context.createPattern(image, 'repeat'); | |
| 5658 } | |
| 5659 } | |
| 5660 | |
| 5661 var loadingImageElement = document.getElementById('loadingImage'); | |
| 5662 | |
| 5663 if ( loadingImageElement ) | |
| 5664 { | |
| 5665 loadingImage = loadingImageElement.src; | |
| 5666 } | |
| 5667 } | |
| 5668 | |
| 5669 function selectDataset(newDataset) | |
| 5670 { | |
| 5671 lastDataset = currentDataset; | |
| 5672 currentDataset = newDataset | |
| 5673 if ( datasets > 1 ) | |
| 5674 { | |
| 5675 datasetDropDown.selectedIndex = currentDataset; | |
| 5676 updateDatasetButtons(); | |
| 5677 datasetAlpha.start = 1.5; | |
| 5678 datasetChanged = true; | |
| 5679 } | |
| 5680 head.setMagnitudes(0); | |
| 5681 head.setDepth(1, 1); | |
| 5682 head.setMaxDepths(); | |
| 5683 handleResize(); | |
| 5684 } | |
| 5685 | |
| 5686 function selectLastDataset() | |
| 5687 { | |
| 5688 selectDataset(lastDataset); | |
| 5689 handleResize(); | |
| 5690 } | |
| 5691 | |
| 5692 function selectNode(newNode) | |
| 5693 { | |
| 5694 if ( selectedNode != newNode ) | |
| 5695 { | |
| 5696 // truncate history at current location to create a new branch | |
| 5697 // | |
| 5698 nodeHistory.length = nodeHistoryPosition; | |
| 5699 | |
| 5700 if ( selectedNode != 0 ) | |
| 5701 { | |
| 5702 nodeHistory.push(selectedNode); | |
| 5703 nodeHistoryPosition++; | |
| 5704 } | |
| 5705 | |
| 5706 setSelectedNode(newNode); | |
| 5707 //updateView(); | |
| 5708 } | |
| 5709 | |
| 5710 updateDatasetButtons(); | |
| 5711 } | |
| 5712 | |
| 5713 function setFocus(node) | |
| 5714 { | |
| 5715 if ( node == focusNode ) | |
| 5716 { | |
| 5717 // return; | |
| 5718 } | |
| 5719 | |
| 5720 focusNode = node; | |
| 5721 | |
| 5722 if ( node.href ) | |
| 5723 { | |
| 5724 detailsName.innerHTML = | |
| 5725 '<a target="_blank" href="' + node.href + '">' + node.name + '</a>'; | |
| 5726 } | |
| 5727 else | |
| 5728 { | |
| 5729 detailsName.innerHTML = node.name; | |
| 5730 } | |
| 5731 | |
| 5732 var table = '<table>'; | |
| 5733 | |
| 5734 table += '<tr><td></td></tr>'; | |
| 5735 | |
| 5736 for ( var i = 0; i < node.attributes.length; i++ ) | |
| 5737 { | |
| 5738 if ( attributes[i].displayName && node.attributes[i] != undefined ) | |
| 5739 { | |
| 5740 var index = node.attributes[i].length == 1 && attributes[i].mono ? 0 : currentDataset; | |
| 5741 | |
| 5742 if ( typeof node.attributes[i][currentDataset] == 'number' || node.attributes[i][index] != undefined && node.attributes[i][currentDataset] != '' ) | |
| 5743 { | |
| 5744 var value = node.attributes[i][index]; | |
| 5745 | |
| 5746 if ( attributes[i].listNode != undefined ) | |
| 5747 { | |
| 5748 value = | |
| 5749 '<a href="" onclick="showList(' + | |
| 5750 attributeIndex(attributes[i].listNode) + ',' + i + | |
| 5751 ',false);return false;" title="Show list">' + | |
| 5752 value + '</a>'; | |
| 5753 } | |
| 5754 else if ( attributes[i].listAll != undefined ) | |
| 5755 { | |
| 5756 value = | |
| 5757 '<a href="" onclick="showList(' + | |
| 5758 attributeIndex(attributes[i].listAll) + ',' + i + | |
| 5759 ',true);return false;" title="Show list">' + | |
| 5760 value + '</a>'; | |
| 5761 } | |
| 5762 else if ( attributes[i].dataNode != undefined && dataEnabled ) | |
| 5763 { | |
| 5764 value = | |
| 5765 '<a href="" onclick="showData(' + | |
| 5766 attributeIndex(attributes[i].dataNode) + ',' + i + | |
| 5767 ',false);return false;" title="Show data">' + | |
| 5768 value + '</a>'; | |
| 5769 } | |
| 5770 else if ( attributes[i].dataAll != undefined && dataEnabled ) | |
| 5771 { | |
| 5772 value = | |
| 5773 '<a href="" onclick="showData(' + | |
| 5774 attributeIndex(attributes[i].dataAll) + ',' + i + | |
| 5775 ',true);return false;" title="Show data">' + | |
| 5776 value + '</a>'; | |
| 5777 } | |
| 5778 | |
| 5779 table += | |
| 5780 '<tr><td><strong>' + attributes[i].displayName + ':</strong></td><td>' + | |
| 5781 value + '</td></tr>'; | |
| 5782 } | |
| 5783 } | |
| 5784 } | |
| 5785 | |
| 5786 table += '</table>'; | |
| 5787 detailsInfo.innerHTML = table; | |
| 5788 | |
| 5789 detailsExpand.disabled = !focusNode.hasChildren() || focusNode == selectedNode; | |
| 5790 } | |
| 5791 | |
| 5792 function setSelectedNode(newNode) | |
| 5793 { | |
| 5794 if ( selectedNode && selectedNode.hasParent(newNode) ) | |
| 5795 { | |
| 5796 zoomOut = true; | |
| 5797 } | |
| 5798 else | |
| 5799 { | |
| 5800 zoomOut = false; | |
| 5801 } | |
| 5802 | |
| 5803 selectedNodeLast = selectedNode; | |
| 5804 selectedNode = newNode; | |
| 5805 | |
| 5806 //if ( focusNode != selectedNode ) | |
| 5807 { | |
| 5808 setFocus(selectedNode); | |
| 5809 } | |
| 5810 } | |
| 5811 | |
| 5812 function waitForData(dataWindow, target, title, time, postUrl, postVar) | |
| 5813 { | |
| 5814 if ( nodeData.length == target ) | |
| 5815 { | |
| 5816 if ( postUrl != undefined ) | |
| 5817 { | |
| 5818 for ( var i = 0; i < nodeData.length; i++ ) | |
| 5819 { | |
| 5820 nodeData[i] = nodeData[i].replace(/\n/g, ','); | |
| 5821 } | |
| 5822 | |
| 5823 var postString = nodeData.join(''); | |
| 5824 postString = postString.slice(0, -1); | |
| 5825 | |
| 5826 dataWindow.document.body.removeChild(dataWindow.document.getElementById('loading')); | |
| 5827 document.body.removeChild(document.getElementById('data')); | |
| 5828 | |
| 5829 post(postUrl, postVar, postString, dataWindow); | |
| 5830 } | |
| 5831 else | |
| 5832 { | |
| 5833 //dataWindow.document.body.removeChild(dataWindow.document.getElementById('loading')); | |
| 5834 //document.body.removeChild(document.getElementById('data')); | |
| 5835 | |
| 5836 dataWindow.document.open(); | |
| 5837 dataWindow.document.write('<pre>' + nodeData.join('') + '</pre>'); | |
| 5838 dataWindow.document.close(); | |
| 5839 } | |
| 5840 | |
| 5841 dataWindow.document.title = title; // replace after document.write() | |
| 5842 } | |
| 5843 else | |
| 5844 { | |
| 5845 var date = new Date(); | |
| 5846 | |
| 5847 if ( date.getTime() - time > 10000 ) | |
| 5848 { | |
| 5849 dataWindow.document.body.removeChild(dataWindow.document.getElementById('loading')); | |
| 5850 document.body.removeChild(document.getElementById('data')); | |
| 5851 dataWindow.document.body.innerHTML = | |
| 5852 'Timed out loading supplemental files for:<br/>' + document.location; | |
| 5853 } | |
| 5854 else | |
| 5855 { | |
| 5856 setTimeout(function() {waitForData(dataWindow, target, title, time, postUrl, postVar);}, 100); | |
| 5857 } | |
| 5858 } | |
| 5859 } | |
| 5860 | |
| 5861 function data(newData) | |
| 5862 { | |
| 5863 nodeData.push(newData); | |
| 5864 } | |
| 5865 | |
| 5866 function enableData() | |
| 5867 { | |
| 5868 dataEnabled = true; | |
| 5869 } | |
| 5870 | |
| 5871 function showData(indexData, indexAttribute, summary) | |
| 5872 { | |
| 5873 var dataWindow = window.open('', '_blank'); | |
| 5874 var title = 'Krona - ' + attributes[indexAttribute].displayName + ' - ' + focusNode.name; | |
| 5875 dataWindow.document.title = title; | |
| 5876 | |
| 5877 nodeData = new Array(); | |
| 5878 | |
| 5879 if ( dataWindow && dataWindow.document && dataWindow.document.body != null ) | |
| 5880 { | |
| 5881 //var loadImage = document.createElement('img'); | |
| 5882 //loadImage.src = "file://localhost/Users/ondovb/Krona/KronaTools/img/loading.gif"; | |
| 5883 //loadImage.id = "loading"; | |
| 5884 //loadImage.alt = "Loading..."; | |
| 5885 //dataWindow.document.body.appendChild(loadImage); | |
| 5886 dataWindow.document.body.innerHTML = | |
| 5887 '<img id="loading" src="' + loadingImage + '" alt="Loading..."></img>'; | |
| 5888 } | |
| 5889 | |
| 5890 var scripts = document.createElement('div'); | |
| 5891 scripts.id = 'data'; | |
| 5892 document.body.appendChild(scripts); | |
| 5893 | |
| 5894 var files = focusNode.getData(indexData, summary); | |
| 5895 | |
| 5896 var date = new Date(); | |
| 5897 var time = date.getTime(); | |
| 5898 | |
| 5899 for ( var i = 0; i < files.length; i++ ) | |
| 5900 { | |
| 5901 var script = document.createElement('script'); | |
| 5902 script.src = files[i] + '?' + time; | |
| 5903 scripts.appendChild(script); | |
| 5904 } | |
| 5905 | |
| 5906 waitForData(dataWindow, files.length, title, time, attributes[indexAttribute].postUrl, attributes[indexAttribute].postVar); | |
| 5907 | |
| 5908 return false; | |
| 5909 } | |
| 5910 | |
| 5911 function showList(indexList, indexAttribute, summary) | |
| 5912 { | |
| 5913 var list = focusNode.getList(indexList, summary); | |
| 5914 | |
| 5915 if ( attributes[indexAttribute].postUrl != undefined ) | |
| 5916 { | |
| 5917 post(attributes[indexAttribute].postUrl, attributes[indexAttribute].postVar, list.join(',')); | |
| 5918 } | |
| 5919 else | |
| 5920 { | |
| 5921 var dataWindow = window.open('', '_blank'); | |
| 5922 | |
| 5923 if ( true || navigator.appName == 'Microsoft Internet Explorer' ) // :( | |
| 5924 { | |
| 5925 dataWindow.document.open(); | |
| 5926 dataWindow.document.write('<pre>' + list.join('\n') + '</pre>'); | |
| 5927 dataWindow.document.close(); | |
| 5928 } | |
| 5929 else | |
| 5930 { | |
| 5931 var pre = document.createElement('pre'); | |
| 5932 dataWindow.document.body.appendChild(pre); | |
| 5933 pre.innerHTML = list; | |
| 5934 } | |
| 5935 | |
| 5936 dataWindow.document.title = 'Krona - ' + attributes[indexAttribute].displayName + ' - ' + focusNode.name; | |
| 5937 } | |
| 5938 } | |
| 5939 | |
| 5940 function snapshot() | |
| 5941 { | |
| 5942 svg = svgHeader(); | |
| 5943 | |
| 5944 resetKeyOffset(); | |
| 5945 | |
| 5946 snapshotMode = true; | |
| 5947 | |
| 5948 selectedNode.draw(false, true); | |
| 5949 selectedNode.draw(true, true); | |
| 5950 | |
| 5951 if ( focusNode != 0 && focusNode != selectedNode ) | |
| 5952 { | |
| 5953 context.globalAlpha = 1; | |
| 5954 focusNode.drawHighlight(true); | |
| 5955 } | |
| 5956 | |
| 5957 if ( hueDisplayName && useHue() ) | |
| 5958 { | |
| 5959 drawLegendSVG(); | |
| 5960 } | |
| 5961 | |
| 5962 snapshotMode = false; | |
| 5963 | |
| 5964 svg += svgFooter(); | |
| 5965 | |
| 5966 snapshotWindow = window.open | |
| 5967 ( | |
| 5968 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svg), | |
| 5969 '_blank' | |
| 5970 ); | |
| 5971 /* var data = window.open('data:text/plain;charset=utf-8,hello', '_blank'); | |
| 5972 var data = window.open('', '_blank'); | |
| 5973 data.document.open('text/plain'); | |
| 5974 data.document.write('hello'); | |
| 5975 data.document.close(); | |
| 5976 var button = document.createElement('input'); | |
| 5977 button.type = 'button'; | |
| 5978 button.value = 'save'; | |
| 5979 button.onclick = save; | |
| 5980 data.document.body.appendChild(button); | |
| 5981 // snapshotWindow.document.write(svg); | |
| 5982 // snapshotWindow.document.close(); | |
| 5983 */ | |
| 5984 } | |
| 5985 | |
| 5986 function save() | |
| 5987 { | |
| 5988 alert(document.body.innerHTML); | |
| 5989 } | |
| 5990 | |
| 5991 function spacer() | |
| 5992 { | |
| 5993 if ( snapshotMode ) | |
| 5994 { | |
| 5995 return '   '; | |
| 5996 } | |
| 5997 else | |
| 5998 { | |
| 5999 return ' '; | |
| 6000 } | |
| 6001 } | |
| 6002 | |
| 6003 function suppressEvent(e) | |
| 6004 { | |
| 6005 e.cancelBubble = true; | |
| 6006 if (e.stopPropagation) e.stopPropagation(); | |
| 6007 } | |
| 6008 | |
| 6009 function svgFooter() | |
| 6010 { | |
| 6011 return '</svg>'; | |
| 6012 } | |
| 6013 | |
| 6014 function svgHeader() | |
| 6015 { | |
| 6016 var patternWidth = fontSize * .6;//radius / 50; | |
| 6017 | |
| 6018 return '\ | |
| 6019 <?xml version="1.0" standalone="no"?>\ | |
| 6020 <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" \ | |
| 6021 "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\ | |
| 6022 <svg width="' + imageWidth + '" height="' + imageHeight + '" version="1.1"\ | |
| 6023 xmlns="http://www.w3.org/2000/svg">\ | |
| 6024 <title>Krona (snapshot) - ' + | |
| 6025 (datasets > 1 ? datasetNames[currentDataset] + ' - ' : '') + selectedNode.name + | |
| 6026 '</title>\ | |
| 6027 <defs>\ | |
| 6028 <style type="text/css">\ | |
| 6029 text {font-size: ' + fontSize + 'px; font-family: ' + fontFamily + '; dominant-baseline:central}\ | |
| 6030 path {stroke-width:' + thinLineWidth * fontSize / 12 + ';}\ | |
| 6031 path.wedge {stroke:none}\ | |
| 6032 path.line {fill:none;stroke:black;}\ | |
| 6033 line {stroke:black;stroke-width:' + thinLineWidth * fontSize / 12 + ';}\ | |
| 6034 line.tick {stroke-width:' + thinLineWidth * fontSize / 6 + ';}\ | |
| 6035 line.pattern {stroke-width:' + thinLineWidth * fontSize / 18 + ';}\ | |
| 6036 circle {fill:none;stroke:black;stroke-width:' + thinLineWidth * fontSize / 12 + ';}\ | |
| 6037 rect {stroke:black;stroke-width:' + thinLineWidth * fontSize / 12 + ';}\ | |
| 6038 .highlight {stroke:black;stroke-width:'+ highlightLineWidth * fontSize / 12 + ';}\ | |
| 6039 .searchHighlight {fill:rgb(255, 255, 100);stroke:none;}\ | |
| 6040 </style>\ | |
| 6041 <pattern id="hiddenPattern" patternUnits="userSpaceOnUse" \ | |
| 6042 x="0" y="0" width="' + patternWidth + '" height="' + patternWidth + '">\ | |
| 6043 <line class="pattern" x1="0" y1="0" x2="' + patternWidth / 2 + '" y2="' + patternWidth / 2 + '"/>\ | |
| 6044 <line class="pattern" x1="' + patternWidth / 2 + '" y1="' + patternWidth + | |
| 6045 '" x2="' + patternWidth + '" y2="' + patternWidth / 2 + '"/>\ | |
| 6046 </pattern>\ | |
| 6047 </defs>\ | |
| 6048 '; | |
| 6049 } | |
| 6050 | |
| 6051 function svgText(text, x, y, anchor, bold, color) | |
| 6052 { | |
| 6053 if ( typeof(anchor) == 'undefined' ) | |
| 6054 { | |
| 6055 anchor = 'start'; | |
| 6056 } | |
| 6057 | |
| 6058 if ( color == undefined ) | |
| 6059 { | |
| 6060 color = 'black'; | |
| 6061 } | |
| 6062 | |
| 6063 return '<text x="' + x + '" y="' + y + | |
| 6064 '" style="font-color:' + color + ';font-weight:' + (bold ? 'bold' : 'normal') + | |
| 6065 '" text-anchor="' + anchor + '">' + text + '</text>'; | |
| 6066 } | |
| 6067 | |
| 6068 function toggleKeys() | |
| 6069 { | |
| 6070 if ( showKeys ) | |
| 6071 { | |
| 6072 keyControl.value = '…'; | |
| 6073 showKeys = false; | |
| 6074 } | |
| 6075 else | |
| 6076 { | |
| 6077 keyControl.value = 'x'; | |
| 6078 showKeys = true; | |
| 6079 } | |
| 6080 | |
| 6081 updateKeyControl(); | |
| 6082 | |
| 6083 if ( progress == 1 ) | |
| 6084 { | |
| 6085 draw(); | |
| 6086 } | |
| 6087 } | |
| 6088 | |
| 6089 function update() | |
| 6090 { | |
| 6091 if ( ! head ) | |
| 6092 { | |
| 6093 return; | |
| 6094 } | |
| 6095 | |
| 6096 if ( mouseDown && focusNode != selectedNode ) | |
| 6097 { | |
| 6098 var date = new Date(); | |
| 6099 | |
| 6100 if ( date.getTime() - mouseDownTime > quickLookHoldLength ) | |
| 6101 { | |
| 6102 if ( focusNode.hasChildren() ) | |
| 6103 { | |
| 6104 expand(focusNode); | |
| 6105 quickLook = true; | |
| 6106 } | |
| 6107 } | |
| 6108 } | |
| 6109 | |
| 6110 if ( updateViewNeeded ) | |
| 6111 { | |
| 6112 resize(); | |
| 6113 mouseX = -1; | |
| 6114 mouseY = -1; | |
| 6115 | |
| 6116 collapse = collapseCheckBox.checked; | |
| 6117 compress = true;//compressCheckBox.checked; | |
| 6118 shorten = true;//shortenCheckBox.checked; | |
| 6119 | |
| 6120 checkSelectedCollapse(); | |
| 6121 updateMaxAbsoluteDepth(); | |
| 6122 | |
| 6123 if ( focusNode.getCollapse() || focusNode.depth > maxAbsoluteDepth ) | |
| 6124 { | |
| 6125 setFocus(selectedNode); | |
| 6126 } | |
| 6127 else | |
| 6128 { | |
| 6129 setFocus(focusNode); | |
| 6130 } | |
| 6131 | |
| 6132 updateView(); | |
| 6133 | |
| 6134 updateViewNeeded = false; | |
| 6135 } | |
| 6136 | |
| 6137 var date = new Date(); | |
| 6138 progress = (date.getTime() - tweenStartTime) / tweenLength; | |
| 6139 // progress += .01; | |
| 6140 | |
| 6141 if ( progress >= 1 ) | |
| 6142 { | |
| 6143 progress = 1; | |
| 6144 } | |
| 6145 | |
| 6146 if ( progress != progressLast ) | |
| 6147 { | |
| 6148 tweenFactor =// progress; | |
| 6149 (1 / (1 + Math.exp(-tweenCurvature * (progress - .5))) - .5) / | |
| 6150 (tweenMax - .5) / 2 + .5; | |
| 6151 | |
| 6152 if ( progress == 1 ) | |
| 6153 { | |
| 6154 snapshotButton.disabled = false; | |
| 6155 zoomOut = false; | |
| 6156 | |
| 6157 //updateKeyControl(); | |
| 6158 | |
| 6159 if ( ! quickLook ) | |
| 6160 { | |
| 6161 //checkHighlight(); | |
| 6162 } | |
| 6163 | |
| 6164 | |
| 6165 if ( fpsDisplay ) | |
| 6166 { | |
| 6167 fpsDisplay.innerHTML = 'fps: ' + Math.round(tweenFrames * 1000 / tweenLength); | |
| 6168 } | |
| 6169 } | |
| 6170 | |
| 6171 draw(); | |
| 6172 } | |
| 6173 | |
| 6174 progressLast = progress; | |
| 6175 } | |
| 6176 | |
| 6177 function updateDatasetButtons() | |
| 6178 { | |
| 6179 if ( datasets == 1 ) | |
| 6180 { | |
| 6181 return; | |
| 6182 } | |
| 6183 | |
| 6184 var node = selectedNode ? selectedNode : head; | |
| 6185 | |
| 6186 datasetButtonLast.disabled = | |
| 6187 node.attributes[magnitudeIndex][lastDataset] == 0; | |
| 6188 | |
| 6189 datasetButtonPrev.disabled = true; | |
| 6190 datasetButtonNext.disabled = true; | |
| 6191 | |
| 6192 for ( var i = 0; i < datasets; i++ ) | |
| 6193 { | |
| 6194 var disable = node.attributes[magnitudeIndex][i] == 0; | |
| 6195 | |
| 6196 datasetDropDown.options[i].disabled = disable; | |
| 6197 | |
| 6198 if ( ! disable ) | |
| 6199 { | |
| 6200 if ( i != currentDataset ) | |
| 6201 { | |
| 6202 datasetButtonPrev.disabled = false; | |
| 6203 datasetButtonNext.disabled = false; | |
| 6204 } | |
| 6205 } | |
| 6206 } | |
| 6207 } | |
| 6208 | |
| 6209 function updateDatasetWidths() | |
| 6210 { | |
| 6211 if ( datasets > 1 ) | |
| 6212 { | |
| 6213 for ( var i = 0; i < datasets; i++ ) | |
| 6214 { | |
| 6215 context.font = fontBold; | |
| 6216 var dim = context.measureText(datasetNames[i]); | |
| 6217 datasetWidths[i] = dim.width; | |
| 6218 } | |
| 6219 } | |
| 6220 } | |
| 6221 | |
| 6222 function updateKeyControl() | |
| 6223 { | |
| 6224 if ( keys == 0 )//|| progress != 1 ) | |
| 6225 { | |
| 6226 keyControl.style.visibility = 'hidden'; | |
| 6227 } | |
| 6228 else | |
| 6229 { | |
| 6230 keyControl.style.visibility = 'visible'; | |
| 6231 keyControl.style.right = margin + 'px'; | |
| 6232 | |
| 6233 if ( showKeys ) | |
| 6234 { | |
| 6235 keyControl.style.top = | |
| 6236 imageHeight - | |
| 6237 ( | |
| 6238 keys * (keySize + keyBuffer) - | |
| 6239 keyBuffer + | |
| 6240 margin + | |
| 6241 keyControl.clientHeight * 1.5 | |
| 6242 ) + 'px'; | |
| 6243 } | |
| 6244 else | |
| 6245 { | |
| 6246 keyControl.style.top = | |
| 6247 (imageHeight - margin - keyControl.clientHeight) + 'px'; | |
| 6248 } | |
| 6249 } | |
| 6250 } | |
| 6251 | |
| 6252 function updateView() | |
| 6253 { | |
| 6254 if ( selectedNode.depth > maxAbsoluteDepth - 1 ) | |
| 6255 { | |
| 6256 maxAbsoluteDepth = selectedNode.depth + 1; | |
| 6257 } | |
| 6258 | |
| 6259 highlightedNode = selectedNode; | |
| 6260 | |
| 6261 angleFactor = 2 * Math.PI / (selectedNode.magnitude); | |
| 6262 | |
| 6263 maxPossibleDepth = Math.floor(gRadius / (fontSize * minRingWidthFactor)); | |
| 6264 | |
| 6265 if ( maxPossibleDepth < 4 ) | |
| 6266 { | |
| 6267 maxPossibleDepth = 4; | |
| 6268 } | |
| 6269 | |
| 6270 var minRadiusInner = fontSize * 8 / gRadius; | |
| 6271 var minRadiusFirst = fontSize * 6 / gRadius; | |
| 6272 var minRadiusOuter = fontSize * 5 / gRadius; | |
| 6273 | |
| 6274 if ( .25 < minRadiusInner ) | |
| 6275 { | |
| 6276 minRadiusInner = .25; | |
| 6277 } | |
| 6278 | |
| 6279 if ( .15 < minRadiusFirst ) | |
| 6280 { | |
| 6281 minRadiusFirst = .15; | |
| 6282 } | |
| 6283 | |
| 6284 if ( .15 < minRadiusOuter ) | |
| 6285 { | |
| 6286 minRadiusOuter = .15; | |
| 6287 } | |
| 6288 | |
| 6289 // visibility of nodes depends on the depth they are displayed at, | |
| 6290 // so we need to set the max depth assuming they can all be displayed | |
| 6291 // and iterate it down based on the deepest child node we can display | |
| 6292 // | |
| 6293 var maxDepth; | |
| 6294 var newMaxDepth = selectedNode.getMaxDepth() - selectedNode.getDepth() + 1; | |
| 6295 // | |
| 6296 do | |
| 6297 { | |
| 6298 maxDepth = newMaxDepth; | |
| 6299 | |
| 6300 if ( ! compress && maxDepth > maxPossibleDepth ) | |
| 6301 { | |
| 6302 maxDepth = maxPossibleDepth; | |
| 6303 } | |
| 6304 | |
| 6305 if ( compress ) | |
| 6306 { | |
| 6307 compressedRadii = new Array(maxDepth); | |
| 6308 | |
| 6309 compressedRadii[0] = minRadiusInner; | |
| 6310 | |
| 6311 var offset = 0; | |
| 6312 | |
| 6313 while | |
| 6314 ( | |
| 6315 lerp | |
| 6316 ( | |
| 6317 Math.atan(offset + 2), | |
| 6318 Math.atan(offset + 1), | |
| 6319 Math.atan(maxDepth + offset - 1), | |
| 6320 minRadiusInner, | |
| 6321 1 - minRadiusOuter | |
| 6322 ) - minRadiusInner > minRadiusFirst && | |
| 6323 offset < 10 | |
| 6324 ) | |
| 6325 { | |
| 6326 offset++; | |
| 6327 } | |
| 6328 | |
| 6329 offset--; | |
| 6330 | |
| 6331 for ( var i = 1; i < maxDepth; i++ ) | |
| 6332 { | |
| 6333 compressedRadii[i] = lerp | |
| 6334 ( | |
| 6335 Math.atan(i + offset), | |
| 6336 Math.atan(offset), | |
| 6337 Math.atan(maxDepth + offset - 1), | |
| 6338 minRadiusInner, | |
| 6339 1 - minRadiusOuter | |
| 6340 ) | |
| 6341 } | |
| 6342 } | |
| 6343 else | |
| 6344 { | |
| 6345 nodeRadius = 1 / maxDepth; | |
| 6346 } | |
| 6347 | |
| 6348 newMaxDepth = selectedNode.maxVisibleDepth(maxDepth); | |
| 6349 | |
| 6350 if ( compress ) | |
| 6351 { | |
| 6352 if ( newMaxDepth <= maxPossibleDepth ) | |
| 6353 { | |
| 6354 // compress | |
| 6355 } | |
| 6356 } | |
| 6357 else | |
| 6358 { | |
| 6359 if ( newMaxDepth > maxPossibleDepth ) | |
| 6360 { | |
| 6361 newMaxDepth = maxPossibleDepth; | |
| 6362 } | |
| 6363 } | |
| 6364 } | |
| 6365 while ( newMaxDepth < maxDepth ); | |
| 6366 | |
| 6367 maxDisplayDepth = maxDepth; | |
| 6368 | |
| 6369 lightnessFactor = (lightnessMax - lightnessBase) / (maxDepth > 8 ? 8 : maxDepth); | |
| 6370 keys = 0; | |
| 6371 | |
| 6372 nLabelOffsets = new Array(maxDisplayDepth - 1); | |
| 6373 labelOffsets = new Array(maxDisplayDepth - 1); | |
| 6374 labelLastNodes = new Array(maxDisplayDepth - 1); | |
| 6375 labelFirstNodes = new Array(maxDisplayDepth - 1); | |
| 6376 | |
| 6377 for ( var i = 0; i < maxDisplayDepth - 1; i++ ) | |
| 6378 { | |
| 6379 if ( compress ) | |
| 6380 { | |
| 6381 if ( i == maxDisplayDepth - 1 ) | |
| 6382 { | |
| 6383 nLabelOffsets[i] = 0; | |
| 6384 } | |
| 6385 else | |
| 6386 { | |
| 6387 var width = | |
| 6388 (compressedRadii[i + 1] - compressedRadii[i]) * | |
| 6389 gRadius; | |
| 6390 | |
| 6391 nLabelOffsets[i] = Math.floor(width / fontSize / 1.2); | |
| 6392 | |
| 6393 if ( nLabelOffsets[i] > 2 ) | |
| 6394 { | |
| 6395 nLabelOffsets[i] = min | |
| 6396 ( | |
| 6397 Math.floor(width / fontSize / 1.75), | |
| 6398 5 | |
| 6399 ); | |
| 6400 } | |
| 6401 } | |
| 6402 } | |
| 6403 else | |
| 6404 { | |
| 6405 nLabelOffsets[i] = Math.max | |
| 6406 ( | |
| 6407 Math.floor(Math.sqrt((nodeRadius * gRadius / fontSize)) * 1.5), | |
| 6408 3 | |
| 6409 ); | |
| 6410 } | |
| 6411 | |
| 6412 labelOffsets[i] = Math.floor((nLabelOffsets[i] - 1) / 2); | |
| 6413 labelLastNodes[i] = new Array(nLabelOffsets[i] + 1); | |
| 6414 labelFirstNodes[i] = new Array(nLabelOffsets[i] + 1); | |
| 6415 | |
| 6416 for ( var j = 0; j <= nLabelOffsets[i]; j++ ) | |
| 6417 { | |
| 6418 // these arrays will allow nodes with neighboring labels to link to | |
| 6419 // each other to determine max label length | |
| 6420 | |
| 6421 labelLastNodes[i][j] = 0; | |
| 6422 labelFirstNodes[i][j] = 0; | |
| 6423 } | |
| 6424 } | |
| 6425 | |
| 6426 fontSizeText.innerHTML = fontSize; | |
| 6427 fontNormal = fontSize + 'px ' + fontFamily; | |
| 6428 context.font = fontNormal; | |
| 6429 fontBold = 'bold ' + fontSize + 'px ' + fontFamily; | |
| 6430 tickLength = fontSize * .7; | |
| 6431 | |
| 6432 head.setTargets(0); | |
| 6433 | |
| 6434 keySize = ((imageHeight - margin * 3) * 1 / 2) / keys * 3 / 4; | |
| 6435 | |
| 6436 if ( keySize > fontSize * maxKeySizeFactor ) | |
| 6437 { | |
| 6438 keySize = fontSize * maxKeySizeFactor; | |
| 6439 } | |
| 6440 | |
| 6441 keyBuffer = keySize / 3; | |
| 6442 | |
| 6443 fontSizeLast = fontSize; | |
| 6444 | |
| 6445 if ( datasetChanged ) | |
| 6446 { | |
| 6447 datasetChanged = false; | |
| 6448 } | |
| 6449 else | |
| 6450 { | |
| 6451 datasetAlpha.start = 0; | |
| 6452 } | |
| 6453 | |
| 6454 var date = new Date(); | |
| 6455 tweenStartTime = date.getTime(); | |
| 6456 progress = 0; | |
| 6457 tweenFrames = 0; | |
| 6458 | |
| 6459 updateKeyControl(); | |
| 6460 updateDatasetWidths(); | |
| 6461 | |
| 6462 document.title = 'Krona - ' + selectedNode.name; | |
| 6463 updateNavigationButtons(); | |
| 6464 snapshotButton.disabled = true; | |
| 6465 | |
| 6466 maxAbsoluteDepthText.innerHTML = maxAbsoluteDepth - 1; | |
| 6467 | |
| 6468 maxAbsoluteDepthButtonDecrease.disabled = (maxAbsoluteDepth == 2); | |
| 6469 maxAbsoluteDepthButtonIncrease.disabled = (maxAbsoluteDepth == head.maxDepth); | |
| 6470 | |
| 6471 if ( collapse != collapseLast && search.value != '' ) | |
| 6472 { | |
| 6473 onSearchChange(); | |
| 6474 collapseLast = collapse; | |
| 6475 } | |
| 6476 } | |
| 6477 | |
| 6478 function updateMaxAbsoluteDepth() | |
| 6479 { | |
| 6480 while ( selectedNode.depth > maxAbsoluteDepth - 1 ) | |
| 6481 { | |
| 6482 selectedNode = selectedNode.getParent(); | |
| 6483 } | |
| 6484 } | |
| 6485 | |
| 6486 function updateNavigationButtons() | |
| 6487 { | |
| 6488 backButton.disabled = (nodeHistoryPosition == 0); | |
| 6489 // upButton.disabled = (selectedNode.getParent() == 0); | |
| 6490 forwardButton.disabled = (nodeHistoryPosition == nodeHistory.length); | |
| 6491 } | |
| 6492 | |
| 6493 function useHue() | |
| 6494 { | |
| 6495 return useHueCheckBox && useHueCheckBox.checked; | |
| 6496 } | |
| 6497 /* | |
| 6498 function zoomOut() | |
| 6499 { | |
| 6500 return ( | |
| 6501 selectedNodeLast != 0 && | |
| 6502 selectedNodeLast.getDepth() < selectedNode.getDepth()); | |
| 6503 } | |
| 6504 */ | |
| 6505 </script> | |
| 6506 </head> | |
| 6507 <body> | |
| 6508 <img id="hiddenImage" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9oLCBQhNQwWVnsAAAAidEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVAgb24gYSBNYWOHqHdDAAABE0lEQVQYGQEIAff+AwAAABkAAAAAAAAA+gAAAAAAAAAAAAAAAAAAAAAAAAAMAwAAAAAAAAANAAAAAAAAAPoAAAAAAAAADAAAAAYAAAD0AwAAAPoAAAAAAAAAAAAAAPoAAAAMAAAADQAAAPoAAAD6AAAAAAAAAAAAAAAAAAAAAAwAAAAZAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAABkAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAGQAAAAwAAAAAAAAADAAAAAwAAAAABAAAAAAAAAAAAAAA8wAAAPQAAAAAAAAAAAAAAA0AAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAZRssKC5OpXwYAAAAASUVORK5CYII=" style="display:none"/> | |
| 6509 <img id="loadingImage" src="data:image/gif;base64,R0lGODlhEAAQAPIAAP///wAAAMLCwkJCQgAAAGJiYoKCgpKSkiH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAADMwi63P4wyklrE2MIOggZnAdOmGYJRbExwroUmcG2LmDEwnHQLVsYOd2mBzkYDAdKa+dIAAAh+QQJCgAAACwAAAAAEAAQAAADNAi63P5OjCEgG4QMu7DmikRxQlFUYDEZIGBMRVsaqHwctXXf7WEYB4Ag1xjihkMZsiUkKhIAIfkECQoAAAAsAAAAABAAEAAAAzYIujIjK8pByJDMlFYvBoVjHA70GU7xSUJhmKtwHPAKzLO9HMaoKwJZ7Rf8AYPDDzKpZBqfvwQAIfkECQoAAAAsAAAAABAAEAAAAzMIumIlK8oyhpHsnFZfhYumCYUhDAQxRIdhHBGqRoKw0R8DYlJd8z0fMDgsGo/IpHI5TAAAIfkECQoAAAAsAAAAABAAEAAAAzIIunInK0rnZBTwGPNMgQwmdsNgXGJUlIWEuR5oWUIpz8pAEAMe6TwfwyYsGo/IpFKSAAAh+QQJCgAAACwAAAAAEAAQAAADMwi6IMKQORfjdOe82p4wGccc4CEuQradylesojEMBgsUc2G7sDX3lQGBMLAJibufbSlKAAAh+QQJCgAAACwAAAAAEAAQAAADMgi63P7wCRHZnFVdmgHu2nFwlWCI3WGc3TSWhUFGxTAUkGCbtgENBMJAEJsxgMLWzpEAACH5BAkKAAAALAAAAAAQABAAAAMyCLrc/jDKSatlQtScKdceCAjDII7HcQ4EMTCpyrCuUBjCYRgHVtqlAiB1YhiCnlsRkAAAOwAAAAAAAAAAAA==" style="display:none"/> | |
| 6510 <img id="logo" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAKRGlDQ1BJQ0MgUHJvZmlsZQAAeAGdlndUFNcXx9/MbC+0XZYiZem9twWkLr1IlSYKy+4CS1nWZRewN0QFIoqICFYkKGLAaCgSK6JYCAgW7AEJIkoMRhEVlczGHPX3Oyf5/U7eH3c+8333nnfn3vvOGQAoASECYQ6sAEC2UCKO9PdmxsUnMPG9AAZEgAM2AHC4uaLQKL9ogK5AXzYzF3WS8V8LAuD1LYBaAK5bBIQzmX/p/+9DkSsSSwCAwtEAOx4/l4tyIcpZ+RKRTJ9EmZ6SKWMYI2MxmiDKqjJO+8Tmf/p8Yk8Z87KFPNRHlrOIl82TcRfKG/OkfJSREJSL8gT8fJRvoKyfJc0WoPwGZXo2n5MLAIYi0yV8bjrK1ihTxNGRbJTnAkCgpH3FKV+xhF+A5gkAO0e0RCxIS5cwjbkmTBtnZxYzgJ+fxZdILMI53EyOmMdk52SLOMIlAHz6ZlkUUJLVlokW2dHG2dHRwtYSLf/n9Y+bn73+GWS9/eTxMuLPnkGMni/al9gvWk4tAKwptDZbvmgpOwFoWw+A6t0vmv4+AOQLAWjt++p7GLJ5SZdIRC5WVvn5+ZYCPtdSVtDP6386fPb8e/jqPEvZeZ9rx/Thp3KkWRKmrKjcnKwcqZiZK+Jw+UyL/x7ifx34VVpf5WEeyU/li/lC9KgYdMoEwjS03UKeQCLIETIFwr/r8L8M+yoHGX6aaxRodR8BPckSKPTRAfJrD8DQyABJ3IPuQJ/7FkKMAbKbF6s99mnuUUb3/7T/YeAy9BXOFaQxZTI7MprJlYrzZIzeCZnBAhKQB3SgBrSAHjAGFsAWOAFX4Al8QRAIA9EgHiwCXJAOsoEY5IPlYA0oAiVgC9gOqsFeUAcaQBM4BtrASXAOXARXwTVwE9wDQ2AUPAOT4DWYgSAID1EhGqQGaUMGkBlkC7Egd8gXCoEioXgoGUqDhJAUWg6tg0qgcqga2g81QN9DJ6Bz0GWoH7oDDUPj0O/QOxiBKTAd1oQNYSuYBXvBwXA0vBBOgxfDS+FCeDNcBdfCR+BW+Bx8Fb4JD8HP4CkEIGSEgeggFggLYSNhSAKSioiRlUgxUonUIk1IB9KNXEeGkAnkLQaHoWGYGAuMKyYAMx/DxSzGrMSUYqoxhzCtmC7MdcwwZhLzEUvFamDNsC7YQGwcNg2bjy3CVmLrsS3YC9ib2FHsaxwOx8AZ4ZxwAbh4XAZuGa4UtxvXjDuL68eN4KbweLwa3gzvhg/Dc/ASfBF+J/4I/gx+AD+Kf0MgE7QJtgQ/QgJBSFhLqCQcJpwmDBDGCDNEBaIB0YUYRuQRlxDLiHXEDmIfcZQ4Q1IkGZHcSNGkDNIaUhWpiXSBdJ/0kkwm65KdyRFkAXk1uYp8lHyJPEx+S1GimFLYlESKlLKZcpBylnKH8pJKpRpSPakJVAl1M7WBep76kPpGjiZnKRcox5NbJVcj1yo3IPdcnihvIO8lv0h+qXyl/HH5PvkJBaKCoQJbgaOwUqFG4YTCoMKUIk3RRjFMMVuxVPGw4mXFJ0p4JUMlXyWeUqHSAaXzSiM0hKZHY9O4tHW0OtoF2igdRzeiB9Iz6CX07+i99EllJWV75RjlAuUa5VPKQwyEYcgIZGQxyhjHGLcY71Q0VbxU+CqbVJpUBlSmVeeoeqryVYtVm1Vvqr5TY6r5qmWqbVVrU3ugjlE3VY9Qz1ffo35BfWIOfY7rHO6c4jnH5tzVgDVMNSI1lmkc0OjRmNLU0vTXFGnu1DyvOaHF0PLUytCq0DqtNa5N03bXFmhXaJ/RfspUZnoxs5hVzC7mpI6GToCOVGe/Tq/OjK6R7nzdtbrNug/0SHosvVS9Cr1OvUl9bf1Q/eX6jfp3DYgGLIN0gx0G3QbThkaGsYYbDNsMnxipGgUaLTVqNLpvTDX2MF5sXGt8wwRnwjLJNNltcs0UNnUwTTetMe0zg80czQRmu836zbHmzuZC81rzQQuKhZdFnkWjxbAlwzLEcq1lm+VzK32rBKutVt1WH60drLOs66zv2SjZBNmstemw+d3W1JZrW2N7w45q52e3yq7d7oW9mT3ffo/9bQeaQ6jDBodOhw+OTo5ixybHcSd9p2SnXU6DLDornFXKuuSMdfZ2XuV80vmti6OLxOWYy2+uFq6Zroddn8w1msufWzd3xE3XjeO2323Ineme7L7PfchDx4PjUevxyFPPk+dZ7znmZeKV4XXE67m3tbfYu8V7mu3CXsE+64P4+PsU+/T6KvnO9632fein65fm1+g36e/gv8z/bAA2IDhga8BgoGYgN7AhcDLIKWhFUFcwJTgquDr4UYhpiDikIxQODQrdFnp/nsE84by2MBAWGLYt7EG4Ufji8B8jcBHhETURjyNtIpdHdkfRopKiDke9jvaOLou+N994vnR+Z4x8TGJMQ8x0rE9seexQnFXcirir8erxgvj2BHxCTEJ9wtQC3wXbF4wmOiQWJd5aaLSwYOHlReqLshadSpJP4iQdT8YmxyYfTn7PCePUcqZSAlN2pUxy2dwd3Gc8T14Fb5zvxi/nj6W6pZanPklzS9uWNp7ukV6ZPiFgC6oFLzICMvZmTGeGZR7MnM2KzWrOJmQnZ58QKgkzhV05WjkFOf0iM1GRaGixy+LtiyfFweL6XCh3YW67hI7+TPVIjaXrpcN57nk1eW/yY/KPFygWCAt6lpgu2bRkbKnf0m+XYZZxl3Uu11m+ZvnwCq8V+1dCK1NWdq7SW1W4anS1/+pDa0hrMtf8tNZ6bfnaV+ti13UUahauLhxZ77++sUiuSFw0uMF1w96NmI2Cjb2b7Dbt3PSxmFd8pcS6pLLkfSm39Mo3Nt9UfTO7OXVzb5lj2Z4tuC3CLbe2emw9VK5YvrR8ZFvottYKZkVxxavtSdsvV9pX7t1B2iHdMVQVUtW+U3/nlp3vq9Orb9Z41zTv0ti1adf0bt7ugT2ee5r2au4t2ftun2Df7f3++1trDWsrD+AO5B14XBdT1/0t69uGevX6kvoPB4UHhw5FHupqcGpoOKxxuKwRbpQ2jh9JPHLtO5/v2pssmvY3M5pLjoKj0qNPv0/+/tax4GOdx1nHm34w+GFXC62luBVqXdI62ZbeNtQe395/IuhEZ4drR8uPlj8ePKlzsuaU8qmy06TThadnzyw9M3VWdHbiXNq5kc6kznvn487f6Iro6r0QfOHSRb+L57u9us9ccrt08rLL5RNXWFfarjpebe1x6Gn5yeGnll7H3tY+p772a87XOvrn9p8e8Bg4d93n+sUbgTeu3px3s//W/Fu3BxMHh27zbj+5k3Xnxd28uzP3Vt/H3i9+oPCg8qHGw9qfTX5uHnIcOjXsM9zzKOrRvRHuyLNfcn95P1r4mPq4ckx7rOGJ7ZOT437j154ueDr6TPRsZqLoV8Vfdz03fv7Db56/9UzGTY6+EL+Y/b30pdrLg6/sX3VOhU89fJ39ema6+I3am0NvWW+738W+G5vJf49/X/XB5EPHx+CP92ezZ2f/AAOY8/xJsCmYAAAACXBIWXMAAAsTAAALEwEAmpwYAAAG40lEQVRIDXVWDWxT1xU+993n//g/tpOYJDQNPylkawmturZbK1SaULExaRvQqVuRNmnrxkgZfxGUIcqI6DaRUqjo1nZCqBNDa7UytVMZE1W3ZaCMpu2AJQXCsB2SOSROnNjPfr935zwnqNrPs6x73/XV993zne+cayaEgE89DB/ANVp0v3vmzIOKZa0ez008lM+ONWeHhwPV4Vg5HqlNxWvifS4Pf2dlx8qzuHdiFkPC0Zqd2wP7FAHDFZvtL+fPP9LX378tPzzc0TCZl5oCAVANAW9PpGHB2nngccuQvTEF2s0AVHvq+5e0Ljr0aPujxwlx79690p49e26TzBHMgfNfHDu28/rlyzseEMzXGgpBtCpg+V0OKZsvwpH0JWjbsRgSNX4oFQxRmC7ByNUJlu61IBlqen3j5qe3IMcYqiDhwW0SCuk2+AtHj/aMfdD/3FNVQd/KhkazLhgSTFiSompQMnQwLQuBS0DAhqWzQMTD7nqgwbzvyShMuq49eWDfz04iXoLAKRKc2+A2W/ehQ9vgytWffCNZDwm/3yzpOp+TT5YkGCso8MrwILR8vwFiMR+SCZBwXZZlcHtcQi2bVt9bN7hIxU8+u69rPYHjUwnl8LFj908NDHY9UTcP4lVVZlFVucDTYraBRssy8WtR8sHQddApGsOw10zThJKiMqeLS/eubgSlamTdwecPf8dGJ61wIg0ODm7+cnUsUuPzmQqCE3AF0LJlIR6O7pAYLqM4pqELAwkMIjKJiEjKzBNwmG1fTEIme/2HmUymjqTin2jafS3TMwceicVlGRkNy8KcICL6CVUAPIGQQLCRksL6pnLMe4eDeYMyA7SHZQo0Ne3AfciOHykQ9QqlVK5++7Xe1ONrHuuToz5fe5slXAGn01J0HbcRMBEI4Khg0dDZ2eyoekXX3i2CnB76oxIaTRTaFzwYjodrPJgIwbhTAhJD01BGUzPdVWVZKUytQKijsnd8/OGwP4ihmphs0tk+v32uMoKfyqTUfzQku3dteualpN9PBeV+4bmXv/Lh7849f+9XE0mHbAlDLbDJzCQM/XUcVB7nlj8GctJa+uPduxtlRy7XEozGQMdkCdIE47VwdHIuBgrT7EOf9/hru3bvxx9M/NJTfuZH3/1V1/ZM6NyJoSNaXmUlaZ7wL/0M83++DkJ1NeAJB+Hm6TONvZs2RyQ3sJgb7UYeJ1kI3MJI6D1XVuGXo6PkbXP58uUOTBFbu3Yt5hvg+k+7TxT5/Hzjt7dC68anxMIvPQbzPtcGocYkc/q8gvt8DgzXK9OZCRQrqjLa+iNZpScR1v98Argqx8MQakiCrmlgmWjnogLAuV0f2NFApj1lYd3CKq11YMHQyW8nGSMIOmT4Vm3tOtz33oULF/RZJluqui1d62ei0aBaKFKtoJnIQxIOTJiqyvTJvB4EUCQjEhmYVFVKaqWo8CSU6bJhska3B5bkp7+5af/+Z2/OzERnCVw9PXu//vd/DewMNt9JbhVYFAw1BWGY9A4GSutVSunH9+3LyVos9v7EeG5Fk8+HSpHlyUaUB8EcOFsVT7j+lM7sPLJly7IxXU+75Uiwmt/Z0SQtjw+9d1osWrWGyT4vyovgEqYHqfSZgsxvjV/8wYsvpuRcsXj6o5Kyc4Hqd3kkSegCw0UCSriGcze+tUerXSNlZc0btyagPrgCmkNLweSGOD/wJruonYDWNU9AhQTrvKzxwsVLIGnaWYzYkI53d/9tNBQ89c9iAbjE0KsVcCKgCtVMixnY2UKcW1UgW1WuaovLXDi5i33hs+tgYXo+XHzr16DPFMHp8ZjFkVFmfnLtSueuXW+SpCS91bJ4cc8fCjO50UJBdjKGDq1YttLw0LIYiYaFiG1EQhUkWeZ2F3PJXnj4nvWwaLgRLv/+DTGdGea53nOwKFF3sL6+foR6HSadSU9v2HA+uuSuA6fyU5BVitwlSSZVNbnKbhtkY3IYWgzBASOw2zRgwG6HF9rv3yDuzi2xPn6pB+Y7PSe/t2Prz+n0lH+6C6gUoKuz82Ckbdnh3ypFuJ7PcywLU8ZeRJJRRAZVOu7kXEICCRxoYY/bB06n0yyUphnzCH730nve3759WyfhzV44WF4VfLI/EfFX8cq8eunSjpapvK8ZayOM7nJj6RR1BX6TU2BZ63ZoCDdB2VTEjDIFN3PX2JD6Z0jM97/euXHrf12ZcwREOkcCdOl/8PFH2yZupDqCqbRUIzuQwoLeog7Nd3wNQq4IZPMZKHpTEE46+xcvbD20qn3VcQL5f5c+/UYPtRtbO5zbf1s0IVZP5HIP5bLZ5tFMJlAdC5djtZFUIl7TJ3PPOx0rO8iO9t+W/wQnwH8DU6LCKd+DXAkAAAAASUVORK5CYII=" style="display:none"/> | |
| 6511 <noscript>Javascript must be enabled to view this page.</noscript> | |
| 6512 <div style="display:none"> | |
| 6513 <krona collapse="true" key="true"> | |
| 6514 <attributes magnitude="count"> | |
| 6515 <list>members</list> | |
| 6516 <attribute display="Reads" listAll="members">count</attribute> | |
| 6517 <attribute display="Unassigned" listNode="members">unassigned</attribute> | |
| 6518 <attribute display="Rank" mono="true">rank</attribute> | |
| 6519 </attributes> | |
| 6520 <datasets> | |
| 6521 <dataset>0</dataset> | |
| 6522 </datasets> | |
| 6523 <node name="Root"> | |
| 6524 <count><val>100</val></count> | |
| 6525 <node name="Eukaryota"> | |
| 6526 <count><val>94</val></count> | |
| 6527 <rank><val>superkingdom</val></rank> | |
| 6528 <node name="Metazoa"> | |
| 6529 <rank><val>kingdom</val></rank> | |
| 6530 <count><val>94</val></count> | |
| 6531 <node name="Chordata"> | |
| 6532 <rank><val>phylum</val></rank> | |
| 6533 <count><val>94</val></count> | |
| 6534 <node name="Craniata"> | |
| 6535 <count><val>94</val></count> | |
| 6536 <rank><val>subphylum</val></rank> | |
| 6537 <node name="Gnathostomata"> | |
| 6538 <count><val>94</val></count> | |
| 6539 <rank><val>superclass</val></rank> | |
| 6540 <node name="Mammalia"> | |
| 6541 <rank><val>class</val></rank> | |
| 6542 <count><val>94</val></count> | |
| 6543 <node name="Euarchontoglires"> | |
| 6544 <count><val>94</val></count> | |
| 6545 <rank><val>superorder</val></rank> | |
| 6546 <node name="Rodentia"> | |
| 6547 <count><val>94</val></count> | |
| 6548 <rank><val>order</val></rank> | |
| 6549 <node name="Sciurognathi"> | |
| 6550 <count><val>94</val></count> | |
| 6551 <rank><val>suborder</val></rank> | |
| 6552 <node name="Muridae"> | |
| 6553 <count><val>94</val></count> | |
| 6554 <rank><val>family</val></rank> | |
| 6555 <node name="Murinae"> | |
| 6556 <rank><val>subfamily</val></rank> | |
| 6557 <count><val>94</val></count> | |
| 6558 <node name="Rattus"> | |
| 6559 <rank><val>genus</val></rank> | |
| 6560 <count><val>94</val></count> | |
| 6561 <members> | |
| 6562 <vals><val>IA_1-296315</val><val>IA_1-322295</val></vals> | |
| 6563 </members> | |
| 6564 <unassigned><val>2</val></unassigned> | |
| 6565 <node name="Rattus norvegicus"> | |
| 6566 <rank><val>species</val></rank> | |
| 6567 <count><val>92</val></count> | |
| 6568 <members> | |
| 6569 <vals><val>IA_1-144417</val><val>IA_1-278966</val><val>IA_1-314709</val><val>IA_1-324951</val><val>IA_1-27817</val><val>IA_1-95255</val><val>IA_1-104173</val><val>IA_1-135979</val><val>IA_1-139090</val><val>IA_1-139090</val><val>IA_1-139090</val><val>IA_1-144996</val><val>IA_1-160446</val><val>IA_1-160446</val><val>IA_1-160446</val><val>IA_1-160446</val><val>IA_1-160446</val><val>IA_1-160446</val><val>IA_1-160446</val><val>IA_1-160446</val><val>IA_1-161439</val><val>IA_1-190855</val><val>IA_1-190855</val><val>IA_1-190855</val><val>IA_1-190855</val><val>IA_1-190855</val><val>IA_1-190855</val><val>IA_1-190855</val><val>IA_1-190855</val><val>IA_1-190855</val><val>IA_1-190855</val><val>IA_1-190855</val><val>IA_1-190855</val><val>IA_1-190855</val><val>IA_1-190855</val><val>IA_1-190855</val><val>IA_1-190855</val><val>IA_1-190855</val><val>IA_1-190855</val><val>IA_1-190855</val><val>IA_1-190855</val><val>IA_1-190855</val><val>IA_1-205154</val><val>IA_1-205154</val><val>IA_1-205154</val><val>IA_1-205154</val><val>IA_1-205154</val><val>IA_1-205154</val><val>IA_1-205154</val><val>IA_1-205154</val><val>IA_1-205154</val><val>IA_1-205154</val><val>IA_1-216231</val><val>IA_1-236286</val><val>IA_1-236286</val><val>IA_1-236286</val><val>IA_1-236286</val><val>IA_1-236286</val><val>IA_1-236286</val><val>IA_1-236286</val><val>IA_1-236286</val><val>IA_1-236286</val><val>IA_1-236286</val><val>IA_1-236286</val><val>IA_1-236286</val><val>IA_1-237681</val><val>IA_1-250166</val><val>IA_1-254274</val><val>IA_1-254274</val><val>IA_1-27817</val><val>IA_1-29000</val><val>IA_1-291427</val><val>IA_1-291427</val><val>IA_1-293054</val><val>IA_1-293054</val><val>IA_1-296315</val><val>IA_1-310974</val><val>IA_1-310974</val><val>IA_1-311282</val><val>IA_1-311282</val><val>IA_1-42600</val><val>IA_1-45102</val><val>IA_1-45102</val><val>IA_1-48105</val><val>IA_1-48105</val><val>IA_1-57254</val><val>IA_1-61975</val><val>IA_1-61975</val><val>IA_1-66943</val><val>IA_1-68288</val><val>IA_1-82334</val><val>IA_1-95526</val></vals> | |
| 6570 </members> | |
| 6571 </node> | |
| 6572 </node> | |
| 6573 </node> | |
| 6574 </node> | |
| 6575 </node> | |
| 6576 </node> | |
| 6577 </node> | |
| 6578 </node> | |
| 6579 </node> | |
| 6580 </node> | |
| 6581 </node> | |
| 6582 </node> | |
| 6583 </node> | |
| 6584 <node name="Bacteria"> | |
| 6585 <rank><val>superkingdom</val></rank> | |
| 6586 <count><val>6</val></count> | |
| 6587 <node name="Proteobacteria"> | |
| 6588 <count><val>6</val></count> | |
| 6589 <rank><val>phylum</val></rank> | |
| 6590 <node name="Gammaproteobacteria"> | |
| 6591 <count><val>6</val></count> | |
| 6592 <rank><val>class</val></rank> | |
| 6593 <node name="Enterobacteriales"> | |
| 6594 <count><val>6</val></count> | |
| 6595 <rank><val>order</val></rank> | |
| 6596 <node name="Enterobacteriaceae"> | |
| 6597 <count><val>6</val></count> | |
| 6598 <rank><val>family</val></rank> | |
| 6599 <node name="Shigella"> | |
| 6600 <rank><val>genus</val></rank> | |
| 6601 <count><val>6</val></count> | |
| 6602 <node name="Shigella flexneri"> | |
| 6603 <count><val>6</val></count> | |
| 6604 <rank><val>species</val></rank> | |
| 6605 <members> | |
| 6606 <vals><val>IA_1-79371</val><val>IA_1-84488</val><val>IA_1-270826</val><val>IA_1-285361</val><val>IA_1-93958</val><val>IA_1-99821</val></vals> | |
| 6607 </members> | |
| 6608 </node> | |
| 6609 </node> | |
| 6610 </node> | |
| 6611 </node> | |
| 6612 </node> | |
| 6613 </node> | |
| 6614 </node> | |
| 6615 </node> | |
| 6616 </krona> | |
| 6617 </div></body></html> |
