/Tutorial/example_html/src/krona-2.0.js
JavaScript | 6290 lines | 4980 code | 851 blank | 459 comment | 843 complexity | 11f3a823b037886b4ed607b63ffa4f00 MD5 | raw file
Possible License(s): GPL-2.0
Large files files are truncated, but you can click here to view the full file
- {//-----------------------------------------------------------------------------
- //
- // PURPOSE
- //
- // Krona is a flexible tool for exploring the relative proportions of
- // hierarchical data, such as metagenomic classifications, using a
- // radial, space-filling display. It is implemented using HTML5 and
- // JavaScript, allowing charts to be explored locally or served over the
- // Internet, requiring only a current version of any major web
- // browser. Krona charts can be created using an Excel template or from
- // common bioinformatic formats using the provided conversion scripts.
- //
- //
- // COPYRIGHT LICENSE
- //
- // Copyright (c) 2011, Battelle National Biodefense Institute (BNBI);
- // all rights reserved. Authored by: Brian Ondov, Nicholas Bergman, and
- // Adam Phillippy
- //
- // This Software was prepared for the Department of Homeland Security
- // (DHS) by the Battelle National Biodefense Institute, LLC (BNBI) as
- // part of contract HSHQDC-07-C-00020 to manage and operate the National
- // Biodefense Analysis and Countermeasures Center (NBACC), a Federally
- // Funded Research and Development Center.
- //
- // Redistribution and use in source and binary forms, with or without
- // modification, are permitted provided that the following conditions are
- // met:
- //
- // * Redistributions of source code must retain the above copyright
- // notice, this list of conditions and the following disclaimer.
- //
- // * Redistributions in binary form must reproduce the above copyright
- // notice, this list of conditions and the following disclaimer in the
- // documentation and/or other materials provided with the distribution.
- //
- // * Neither the name of the Battelle National Biodefense Institute nor
- // the names of its contributors may be used to endorse or promote
- // products derived from this software without specific prior written
- // permission.
- //
- // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- // HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- //
- //
- // TRADEMARK LICENSE
- //
- // KRONA(TM) is a trademark of the Department of Homeland Security, and use
- // of the trademark is subject to the following conditions:
- //
- // * Distribution of the unchanged, official code/software using the
- // KRONA(TM) mark is hereby permitted by the Department of Homeland
- // Security, provided that the software is distributed without charge
- // and modification.
- //
- // * Distribution of altered source code/software using the KRONA(TM) mark
- // is not permitted unless written permission has been granted by the
- // Department of Homeland Security.
- //
- //
- // FOR MORE INFORMATION VISIT
- //
- // http://krona.sourceforge.net
- //
- //-----------------------------------------------------------------------------
- }
- var canvas;
- var context;
- var svg; // for snapshot mode
- var collapse = true;
- var collapseCheckBox;
- var collapseLast;
- var compress;
- var compressCheckBox;
- var maxAbsoluteDepthText;
- var maxAbsoluteDepthButtonDecrease;
- var maxAbsoluteDepthButtonIncrease;
- var fontSize = 11;
- var fontSizeText;
- var fontSizeButtonDecrease;
- var fontSizeButtonIncrease;
- var fontSizeLast;
- var shorten;
- var shortenCheckBox;
- var maxAbsoluteDepth;
- var backButton;
- var upButton;
- var forwardButton;
- var snapshotButton;
- var snapshotMode = false;
- var details;
- var detailsName;
- var search;
- var searchResults;
- var nSearchResults;
- var useHueCheckBox;
- var useHueDiv;
- var datasetDropDown;
- var datasetButtonLast;
- var datasetButtonPrev;
- var datasetButtonNext;
- var keyControl;
- var showKeys = true;
- var linkButton;
- var linkText;
- var frame;
- // Node references. Note that the meanings of 'selected' and 'focused' are
- // swapped in the docs.
- //
- var head; // the root of the entire tree
- var selectedNode = 0; // the root of the current view
- var focusNode = 0; // a node chosen for more info (single-click)
- var highlightedNode = 0; // mouse hover node
- var highlightingHidden = false;
- var nodes = new Array();
- var currentNodeID = 0; // to iterate while loading
- var nodeHistory = new Array();
- var nodeHistoryPosition = 0;
- var dataEnabled = false; // true when supplemental files are present
- // store non-Krona GET variables so they can be passed on to links
- //
- var getVariables = new Array();
- // selectedNodeLast is separate from the history, since we need to check
- // properties of the last node viewed when browsing through the history
- //
- var selectedNodeLast = 0;
- var zoomOut = false;
- // temporary zoom-in while holding the mouse button on a wedge
- //
- var quickLook = false; // true when in quick look state
- var mouseDown = false;
- var mouseDownTime; // to detect mouse button hold
- var quickLookHoldLength = 200;
- var imageWidth;
- var imageHeight;
- var centerX;
- var centerY;
- var gRadius;
- var updateViewNeeded = false;
- // Determines the angle that the pie chart starts at. 90 degrees makes the
- // center label consistent with the children.
- //
- var rotationOffset = Math.PI / 2;
- var buffer = 100;
- // The maps are the small pie charts showing the current slice being viewed.
- //
- var mapBuffer = 10;
- var mapRadius = 0;
- var maxMapRadius = 25;
- var mapWidth = 150;
- var maxLabelOverhang = Math.PI * 4.18;
- // Keys are the labeled boxes for slices in the highest level that are too thin
- // to label.
- //
- var maxKeySizeFactor = 2; // will be multiplied by font size
- var keySize;
- var keys;
- var keyBuffer = 10;
- var currentKey;
- var keyMinTextLeft;
- var keyMinAngle;
- var minRingWidthFactor = 5; // will be multiplied by font size
- var maxPossibleDepth; // the theoretical max that can be displayed
- var maxDisplayDepth; // the actual depth that will be displayed
- var headerHeight = 0;//document.getElementById('options').clientHeight;
- var historySpacingFactor = 1.6; // will be multiplied by font size
- var historyAlphaDelta = .25;
- // appearance
- //
- var lineOpacity = 0.3;
- var saturation = 0.5;
- var lightnessBase = 0.6;
- var lightnessMax = .8;
- var thinLineWidth = .3;
- var highlightLineWidth = 1.5;
- var labelBoxBuffer = 6;
- var labelBoxRounding = 15;
- var labelWidthFudge = 1.05; // The width of unshortened labels are set slightly
- // longer than the name width so the animation
- // finishes faster.
- var fontNormal;
- var fontBold;
- var fontFamily = 'sans-serif';
- //var fontFaceBold = 'bold Arial';
- var nodeRadius;
- var angleFactor;
- var tickLength;
- var compressedRadii;
- // colors
- //
- var highlightFill = 'rgba(255, 255, 255, .3)';
- var colorUnclassified = 'rgb(220,220,220)';
- // label staggering
- //
- var labelOffsets; // will store the current offset at each depth
- //
- // This will store pointers to the last node that had a label in each offset (or "track") of a
- // each depth. These will be used to shorten neighboring labels that would overlap.
- // The [nLabelNodes] index will store the last node with a radial label.
- // labelFirstNodes is the same, but to check for going all the way around and
- // overlapping the first labels.
- //
- var labelLastNodes;
- var labelFirstNodes;
- //
- var nLabelOffsets = 3; // the number of offsets to use
- var mouseX = -1;
- var mouseY = -1;
- // tweening
- //
- var progress = 0; // for tweening; goes from 0 to 1.
- var progressLast = 0;
- var tweenFactor = 0; // progress converted by a curve for a smoother effect.
- var tweenLength = 850; // in ms
- var tweenCurvature = 13;
- //
- // tweenMax is used to scale the sigmoid function so its range is [0,1] for the
- // domain [0,1]
- //
- var tweenMax = 1 / (1 + Math.exp(-tweenCurvature / 2));
- //
- var tweenStartTime;
- // for framerate debug
- //
- var tweenFrames = 0;
- var fpsDisplay = document.getElementById('frameRate');
- // Arrays to translate xml attribute names into displayable attribute names
- //
- var attributes = new Array();
- //
- var magnitudeIndex; // the index of attribute arrays used for magnitude
- var membersAssignedIndex;
- var membersSummaryIndex;
- // For defining gradients
- //
- var hueDisplayName;
- var hueStopPositions;
- var hueStopHues;
- var hueStopText;
- // multiple datasets
- //
- var currentDataset = 0;
- var lastDataset = 0;
- var datasets = 1;
- var datasetNames;
- var datasetSelectSize = 30;
- var datasetAlpha = new Tween(0, 0);
- var datasetWidths = new Array();
- var datasetChanged;
- var datasetSelectWidth = 50;
- window.onload = load;
- var image;
- var hiddenPattern;
- var loadingImage;
- function resize()
- {
- imageWidth = window.innerWidth;
- imageHeight = window.innerHeight;
-
- if ( ! snapshotMode )
- {
- context.canvas.width = imageWidth;
- context.canvas.height = imageHeight;
- }
-
- if ( datasetDropDown )
- {
- var ratio =
- (datasetDropDown.offsetTop + datasetDropDown.clientHeight) * 2 /
- imageHeight;
-
- if ( ratio > 1 )
- {
- ratio = 1;
- }
-
- ratio = Math.sqrt(ratio);
-
- datasetSelectWidth =
- (datasetDropDown.offsetLeft + datasetDropDown.clientWidth) * ratio;
- }
- var leftMargin = datasets > 1 ? datasetSelectWidth + 30 : 0;
- var minDimension = imageWidth - mapWidth - leftMargin > imageHeight ?
- imageHeight :
- imageWidth - mapWidth - leftMargin;
-
- maxMapRadius = minDimension * .03;
- buffer = minDimension * .1;
- margin = minDimension * .015;
- centerX = (imageWidth - mapWidth - leftMargin) / 2 + leftMargin;
- centerY = imageHeight / 2;
- gRadius = minDimension / 2 - buffer;
- //context.font = '11px sans-serif';
- }
- function handleResize()
- {
- updateViewNeeded = true;
- }
- function Attribute()
- {
- }
- function Tween(start, end)
- {
- this.start = start;
- this.end = end;
- this.current = this.start;
-
- this.current = function()
- {
- if ( progress == 1 || this.start == this.end )
- {
- return this.end;
- }
- else
- {
- return this.start + tweenFactor * (this.end - this.start);
- }
- };
-
- this.setTarget = function(target)
- {
- this.start = this.current();
- this.end = target;
- }
- }
- function Node()
- {
- this.id = currentNodeID;
- currentNodeID++;
- nodes[this.id] = this;
-
- this.angleStart = new Tween(Math.PI, 0);
- this.angleEnd = new Tween(Math.PI, 0);
- this.radiusInner = new Tween(1, 1);
- this.labelRadius = new Tween(1, 1);
- this.labelWidth = new Tween(0, 0);
- this.scale = new Tween(1, 1); // TEMP
- this.radiusOuter = new Tween(1, 1);
-
- this.r = new Tween(255, 255);
- this.g = new Tween(255, 255);
- this.b = new Tween(255, 255);
-
- this.alphaLabel = new Tween(0, 1);
- this.alphaLine = new Tween(0, 1);
- this.alphaArc = new Tween(0, 0);
- this.alphaWedge = new Tween(0, 1);
- this.alphaOther = new Tween(0, 1);
- this.alphaPattern = new Tween(0, 0);
- this.children = Array();
- this.parent = 0;
-
- this.attributes = new Array(attributes.length);
-
- this.addChild = function(child)
- {
- this.children.push(child);
- };
-
- this.addLabelNode = function(depth, labelOffset)
- {
- if ( labelHeadNodes[depth][labelOffset] == 0 )
- {
- // this will become the head node for this list
-
- labelHeadNodes[depth][labelOffset] = this;
- this.labelPrev = this;
- }
-
- var head = labelHeadNodes[depth][labelOffset];
-
- this.labelNext = head;
- this.labelPrev = head.labelPrev;
- head.labelPrev.labelNext = this;
- head.labelPrev = this;
- }
-
- this.canDisplayDepth = function()
- {
- // whether this node is at a depth that can be displayed, according
- // to the max absolute depth
-
- return this.depth <= maxAbsoluteDepth;
- }
-
- this.canDisplayHistory = function()
- {
- var radiusInner;
-
- if ( compress )
- {
- radiusInner = compressedRadii[0];
- }
- else
- {
- radiusInner = nodeRadius;
- }
-
- return (
- -this.labelRadius.end * gRadius +
- historySpacingFactor * fontSize / 2 <
- radiusInner * gRadius
- );
- }
-
- this.canDisplayLabelCurrent = function()
- {
- return (
- (this.angleEnd.current() - this.angleStart.current()) *
- (this.radiusInner.current() * gRadius + gRadius) >=
- minWidth());
- }
-
- this.checkHighlight = function()
- {
- if ( this.children.length == 0 && this == focusNode )
- {
- //return false;
- }
-
- if ( this.hide )
- {
- return false;
- }
-
- if ( this.radiusInner.end == 1 )
- {
- // compressed to the outside; don't check
-
- return false;
- }
-
- var highlighted = false;
-
- var angleStartCurrent = this.angleStart.current() + rotationOffset;
- var angleEndCurrent = this.angleEnd.current() + rotationOffset;
- var radiusInner = this.radiusInner.current() * gRadius;
-
- for ( var i = 0; i < this.children.length; i++ )
- {
- highlighted = this.children[i].checkHighlight();
-
- if ( highlighted )
- {
- return true;
- }
- }
-
- if ( this != selectedNode && ! this.getCollapse() )
- {
- context.beginPath();
- context.arc(0, 0, radiusInner, angleStartCurrent, angleEndCurrent, false);
- context.arc(0, 0, gRadius, angleEndCurrent, angleStartCurrent, true);
- context.closePath();
-
- if ( context.isPointInPath(mouseX - centerX, mouseY - centerY) )
- {
- highlighted = true;
- }
-
- if
- (
- ! highlighted &&
- (angleEndCurrent - angleStartCurrent) *
- (radiusInner + gRadius) <
- minWidth() &&
- this.getDepth() == selectedNode.getDepth() + 1
- )
- {
- if ( showKeys && this.checkHighlightKey() )
- {
- highlighted = true;
- }
- }
- }
-
- if ( highlighted )
- {
- if ( this != highlightedNode )
- {
- // document.body.style.cursor='pointer';
- }
-
- highlightedNode = this;
- }
-
- return highlighted;
- }
-
- this.checkHighlightCenter = function()
- {
- if ( ! this.canDisplayHistory() )
- {
- return;
- }
-
- var cx = centerX;
- var cy = centerY - this.labelRadius.end * gRadius;
- //var dim = context.measureText(this.name);
-
- var width = this.nameWidth;
-
- if ( this.searchResultChildren() )
- {
- var results = searchResultString(this.searchResultChildren());
- var dim = context.measureText(results);
- width += dim.width;
- }
-
- if
- (
- mouseX > cx - width / 2 &&
- mouseX < cx + width / 2 &&
- mouseY > cy - historySpacingFactor * fontSize / 2 &&
- mouseY < cy + historySpacingFactor * fontSize / 2
- )
- {
- highlightedNode = this;
- return;
- }
-
- if ( this.getParent() )
- {
- this.getParent().checkHighlightCenter();
- }
- }
-
- this.checkHighlightKey = function()
- {
- var offset = keyOffset();
-
- var xMin = imageWidth - keySize - margin - this.keyNameWidth - keyBuffer;
- var xMax = imageWidth - margin;
- var yMin = offset;
- var yMax = offset + keySize;
-
- currentKey++;
-
- return (
- mouseX > xMin &&
- mouseX < xMax &&
- mouseY > yMin &&
- mouseY < yMax);
- }
-
- this.checkHighlightMap = function()
- {
- if ( this.parent )
- {
- this.parent.checkHighlightMap();
- }
-
- if ( this.getCollapse() || this == focusNode )
- {
- return;
- }
-
- var box = this.getMapPosition();
-
- if
- (
- mouseX > box.x - mapRadius &&
- mouseX < box.x + mapRadius &&
- mouseY > box.y - mapRadius &&
- mouseY < box.y + mapRadius
- )
- {
- highlightedNode = this;
- }
- }
-
- /* this.collapse = function()
- {
- for (var i = 0; i < this.children.length; i++ )
- {
- this.children[i] = this.children[i].collapse();
- }
-
- if
- (
- this.children.length == 1 &&
- this.children[0].magnitude == this.magnitude
- )
- {
- this.children[0].parent = this.parent;
- this.children[0].getDepth() = this.parent.getDepth() + 1;
- return this.children[0];
- }
- else
- {
- return this;
- }
- }
- */
- this.draw = function(labelMode, selected, searchHighlighted)
- {
- var depth = this.getDepth() - selectedNode.getDepth() + 1;
- // var hidden = false;
-
- if ( selectedNode == this )
- {
- selected = true;
- }
-
- var angleStartCurrent = this.angleStart.current() + rotationOffset;
- var angleEndCurrent = this.angleEnd.current() + rotationOffset;
- var radiusInner = this.radiusInner.current() * gRadius;
- var canDisplayLabelCurrent = this.canDisplayLabelCurrent();
- var hiddenSearchResults = false;
-
- /* if ( ! this.hide )
- {
- for ( var i = 0; i < this.children.length; i++ )
- {
- if ( this.children[i].hide && this.children[i].searchResults )
- {
- hiddenSearchResults = true;
- }
- }
- }
- */
- var drawChildren =
- ( ! this.hide || ! this.hidePrev && progress < 1 ) &&
- ( ! this.hideAlone || ! this.hideAlonePrev && progress < 1 );
-
- // if ( this.alphaWedge.current() > 0 || this.alphaLabel.current() > 0 )
- {
- var lastChildAngleEnd;
-
- if ( this.hasChildren() )//canDisplayChildren )
- {
- lastChildAngleEnd =
- this.children[this.children.length - 1].angleEnd.current()
- + rotationOffset;
- }
-
- if ( labelMode )
- {
- var drawRadial =
- !(
- this.parent &&
- this.parent != selectedNode &&
- angleEndCurrent == this.parent.angleEnd.current() + rotationOffset
- );
-
- if ( angleStartCurrent != angleEndCurrent )
- {
- this.drawLines(angleStartCurrent, angleEndCurrent, radiusInner, drawRadial, selected);
- }
-
- var alphaOtherCurrent = this.alphaOther.current();
- var childRadiusInner;
-
- if ( this == selectedNode || alphaOtherCurrent )
- {
- childRadiusInner =
- this.children[this.children.length - 1].radiusInner.current() * gRadius;
- }
-
- if ( this == selectedNode )
- {
- this.drawReferenceRings(childRadiusInner);
- }
-
- if
- (
- selected &&
- ! searchHighlighted &&
- this != selectedNode &&
- (
- this.isSearchResult ||
- this.hideAlone && this.searchResultChildren() ||
- false
- // this.hide &&
- // this.containsSearchResult
- )
- )
- {
- context.globalAlpha = this.alphaWedge.current();
-
- drawWedge
- (
- angleStartCurrent,
- angleEndCurrent,
- radiusInner,
- gRadius,
- highlightFill,
- 0,
- true
- );
-
- if
- (
- this.keyed &&
- ! showKeys &&
- this.searchResults &&
- ! searchHighlighted &&
- this != highlightedNode &&
- this != focusNode
- )
- {
- var angle = (angleEndCurrent + angleStartCurrent) / 2;
- this.drawLabel(angle, true, false, true, true);
- }
-
- //this.drawHighlight(false);
- searchHighlighted = true;
- }
-
- if
- (
- this == selectedNode ||
- // true
- //(canDisplayLabelCurrent) &&
- this != highlightedNode &&
- this != focusNode
- )
- {
- if ( this.radial != this.radialPrev && this.alphaLabel.end == 1 )
- {
- context.globalAlpha = tweenFactor;
- }
- else
- {
- context.globalAlpha = this.alphaLabel.current();
- }
-
- this.drawLabel
- (
- (angleStartCurrent + angleEndCurrent) / 2,
- this.hideAlone && this.searchResultChildren() ||
- (this.isSearchResult || hiddenSearchResults) && selected,
- this == selectedNode && ! this.radial,
- selected,
- this.radial
- );
-
- if ( this.radial != this.radialPrev && this.alphaLabel.start == 1 && progress < 1 )
- {
- context.globalAlpha = 1 - tweenFactor;
-
- this.drawLabel
- (
- (angleStartCurrent + angleEndCurrent) / 2,
- (this.isSearchResult || hiddenSearchResults) && selected,
- this == selectedNodeLast && ! this.radialPrev,
- selected,
- this.radialPrev
- );
- }
- }
-
- if
- (
- alphaOtherCurrent &&
- lastChildAngleEnd != null
- )
- {
- if
- (
- (angleEndCurrent - lastChildAngleEnd) *
- (childRadiusInner + gRadius) >=
- minWidth()
- )
- {
- //context.font = fontNormal;
- context.globalAlpha = this.alphaOther.current();
-
- drawTextPolar
- (
- this.getUnclassifiedText(),
- this.getUnclassifiedPercentage(),
- (lastChildAngleEnd + angleEndCurrent) / 2,
- (childRadiusInner + gRadius) / 2,
- true,
- false,
- false,
- 0,
- 0
- );
- }
- }
-
- if ( this == selectedNode && this.keyUnclassified && showKeys )
- {
- this.drawKey
- (
- (lastChildAngleEnd + angleEndCurrent) / 2,
- false,
- false
- );
- }
- }
- else
- {
- var alphaWedgeCurrent = this.alphaWedge.current();
-
- if ( alphaWedgeCurrent || this.alphaOther.current() )
- {
- var currentR = this.r.current();
- var currentG = this.g.current();
- var currentB = this.b.current();
-
- var fill = rgbText(currentR, currentG, currentB);
-
- var radiusOuter;
- var lastChildAngle;
- var truncateWedge =
- (
- this.hasChildren() &&
- ! this.keyed &&
- (compress || depth < maxDisplayDepth) &&
- drawChildren
- );
-
- if ( truncateWedge )
- {
- radiusOuter = this.children[0].radiusInner.current() * gRadius;
- }
- else
- {
- radiusOuter = gRadius;
- }
- /*
- if ( this.hasChildren() )
- {
- radiusOuter = this.children[0].getUncollapsed().radiusInner.current() * gRadius + 1;
- }
- else
- { // TEMP
- radiusOuter = radiusInner + nodeRadius * gRadius;
-
- if ( radiusOuter > gRadius )
- {
- radiusOuter = gRadius;
- }
- }
- */
- context.globalAlpha = alphaWedgeCurrent;
-
- if ( radiusInner != radiusOuter )
- {
- drawWedge
- (
- angleStartCurrent,
- angleEndCurrent,
- radiusInner,
- radiusOuter,//this.radiusOuter.current() * gRadius,
- //'rgba(0, 200, 0, .1)',
- fill,
- this.alphaPattern.current()
- );
-
- if ( truncateWedge )
- {
- // fill in the extra space if the sum of our childrens'
- // magnitudes is less than ours
-
- if ( lastChildAngleEnd < angleEndCurrent )//&& false) // TEMP
- {
- if ( radiusOuter > 1 )
- {
- // overlap slightly to hide the seam
-
- // radiusOuter -= 1;
- }
-
- if ( alphaWedgeCurrent < 1 )
- {
- context.globalAlpha = this.alphaOther.current();
- drawWedge
- (
- lastChildAngleEnd,
- angleEndCurrent,
- radiusOuter,
- gRadius,
- colorUnclassified,
- 0
- );
- context.globalAlpha = alphaWedgeCurrent;
- }
-
- drawWedge
- (
- lastChildAngleEnd,
- angleEndCurrent,
- radiusOuter,
- gRadius,//this.radiusOuter.current() * gRadius,
- //'rgba(200, 0, 0, .1)',
- fill,
- this.alphaPattern.current()
- );
- }
- }
-
- if ( radiusOuter < gRadius )
- {
- // patch up the seam
- //
- context.beginPath();
- context.arc(0, 0, radiusOuter, angleStartCurrent/*lastChildAngleEnd*/, angleEndCurrent, false);
- context.strokeStyle = fill;
- context.lineWidth = 1;
- context.stroke();
- }
- }
-
- if ( this.keyed && selected && showKeys )//&& progress == 1 )
- {
- this.drawKey
- (
- (angleStartCurrent + angleEndCurrent) / 2,
- (
- this == highlightedNode ||
- this == focusNode ||
- this.searchResults
- ),
- this == highlightedNode || this == focusNode
- );
- }
- }
- }
- }
-
- if ( drawChildren )
- {
- // draw children
- //
- for ( var i = 0; i < this.children.length; i++ )
- {
- if ( this.drawHiddenChildren(i, selected, labelMode, searchHighlighted) )
- {
- i = this.children[i].hiddenEnd;
- }
- else
- {
- this.children[i].draw(labelMode, selected, searchHighlighted);
- }
- }
- }
- };
-
- this.drawHiddenChildren = function
- (
- firstHiddenChild,
- selected,
- labelMode,
- searchHighlighted
- )
- {
- var firstChild = this.children[firstHiddenChild];
-
- if ( firstChild.hiddenEnd == null || firstChild.radiusInner.current() == 1 )
- {
- return false;
- }
-
- for ( var i = firstHiddenChild; i < firstChild.hiddenEnd; i++ )
- {
- if ( ! this.children[i].hide || ! this.children[i].hidePrev && progress < 1 )
- {
- return false;
- }
- }
-
- var angleStart = firstChild.angleStart.current() + rotationOffset;
- var lastChild = this.children[firstChild.hiddenEnd];
- var angleEnd = lastChild.angleEnd.current() + rotationOffset;
- var radiusInner = gRadius * firstChild.radiusInner.current();
- var hiddenChildren = firstChild.hiddenEnd - firstHiddenChild + 1;
-
- if ( labelMode )
- {
- var hiddenSearchResults = 0;
-
- for ( var i = firstHiddenChild; i <= firstChild.hiddenEnd; i++ )
- {
- hiddenSearchResults += this.children[i].searchResults;
- }
-
- if
- (
- selected &&
- (angleEnd - angleStart) *
- (gRadius + gRadius) >=
- minWidth() ||
- hiddenSearchResults
- )
- {
- context.globalAlpha = this.alphaWedge.current();
-
- this.drawHiddenLabel
- (
- angleStart,
- angleEnd,
- hiddenChildren,
- hiddenSearchResults
- );
- }
- }
-
- var drawWedges = true;
-
- for ( var i = firstHiddenChild; i <= firstChild.hiddenEnd; i++ )
- {
- // all hidden children must be completely hidden to draw together
-
- if ( this.children[i].alphaPattern.current() != this.children[i].alphaWedge.current() )
- {
- drawWedges = false;
- break;
- }
- }
-
- if ( labelMode )
- {
- if ( drawWedges )
- {
- var drawRadial = (angleEnd < this.angleEnd.current() + rotationOffset);
- this.drawLines(angleStart, angleEnd, radiusInner, drawRadial);
- }
-
- if ( hiddenSearchResults && ! searchHighlighted )
- {
- drawWedge
- (
- angleStart,
- angleEnd,
- radiusInner,
- gRadius,//this.radiusOuter.current() * gRadius,
- highlightFill,
- 0,
- true
- );
- }
- }
- else if ( drawWedges )
- {
- context.globalAlpha = this.alphaWedge.current();
-
- var fill = rgbText
- (
- firstChild.r.current(),
- firstChild.g.current(),
- firstChild.b.current()
- );
-
- drawWedge
- (
- angleStart,
- angleEnd,
- radiusInner,
- gRadius,//this.radiusOuter.current() * gRadius,
- fill,
- context.globalAlpha,
- false
- );
- }
-
- return drawWedges;
- }
-
- this.drawHiddenLabel = function(angleStart, angleEnd, value, hiddenSearchResults)
- {
- var textAngle = (angleStart + angleEnd) / 2;
- var labelRadius = gRadius + fontSize;//(radiusInner + radius) / 2;
-
- drawTick(gRadius - fontSize * .75, fontSize * 1.5, textAngle);
- drawTextPolar
- (
- value.toString() + ' more',
- 0, // inner text
- textAngle,
- labelRadius,
- true, // radial
- hiddenSearchResults, // bubble
- this == highlightedNode || this == focusNode, // bold
- false,
- hiddenSearchResults
- );
- }
-
- this.drawHighlight = function(bold)
- {
- var angleStartCurrent = this.angleStart.current() + rotationOffset;
- var angleEndCurrent = this.angleEnd.current() + rotationOffset;
- var radiusInner = this.radiusInner.current() * gRadius;
-
- //this.setHighlightStyle();
-
- if ( this == focusNode && this == highlightedNode && this.hasChildren() )
- {
- // context.fillStyle = "rgba(255, 255, 255, .3)";
- arrow
- (
- angleStartCurrent,
- angleEndCurrent,
- radiusInner
- );
- }
- else
- {
- drawWedge
- (
- angleStartCurrent,
- angleEndCurrent,
- radiusInner,
- gRadius,
- highlightFill,
- 0,
- true
- );
- }
-
- // check if hidden children should be highlighted
- //
- for ( var i = 0; i < this.children.length; i++ )
- {
- if
- (
- this.children[i].getDepth() - selectedNode.getDepth() + 1 <=
- maxDisplayDepth &&
- this.children[i].hiddenEnd != null
- )
- {
- var firstChild = this.children[i];
- var lastChild = this.children[firstChild.hiddenEnd];
- var hiddenAngleStart = firstChild.angleStart.current() + rotationOffset;
- var hiddenAngleEnd = lastChild.angleEnd.current() + rotationOffset;
- var hiddenRadiusInner = gRadius * firstChild.radiusInner.current();
-
- drawWedge
- (
- hiddenAngleStart,
- hiddenAngleEnd,
- hiddenRadiusInner,
- gRadius,
- 'rgba(255, 255, 255, .3)',
- 0,
- true
- );
-
- if ( ! this.searchResults )
- {
- this.drawHiddenLabel
- (
- hiddenAngleStart,
- hiddenAngleEnd,
- firstChild.hiddenEnd - i + 1
- );
- }
-
- i = firstChild.hiddenEnd;
- }
- }
-
- // context.strokeStyle = 'black';
- context.fillStyle = 'black';
-
- var highlight = ! ( progress < 1 && zoomOut && this == selectedNodeLast );
-
- var angle = (angleEndCurrent + angleStartCurrent) / 2;
-
- if ( ! (this.keyed && showKeys) )
- {
- this.drawLabel(angle, true, bold, true, this.radial);
- }
- }
-
- this.drawHighlightCenter = function()
- {
- if ( ! this.canDisplayHistory() )
- {
- return;
- }
-
- context.lineWidth = highlightLineWidth;
- context.strokeStyle = 'black';
- context.fillStyle = "rgba(255, 255, 255, .6)";
-
- context.fillStyle = 'black';
- this.drawLabel(3 * Math.PI / 2, true, true, false);
- context.font = fontNormal;
- }
-
- this.drawKey = function(angle, highlight, bold)
- {
- var offset = keyOffset();
- var color;
- var patternAlpha = this.alphaPattern.end;
- var boxLeft = imageWidth - keySize - margin;
- var textY = offset + keySize / 2;
-
- var label;
- var keyNameWidth;
-
- if ( this == selectedNode )
- {
- color = colorUnclassified;
- label =
- this.getUnclassifiedText() +
- ' ' +
- this.getUnclassifiedPercentage();
- keyNameWidth = measureText(label, false);
- }
- else
- {
- label = this.keyLabel;
- color = rgbText(this.r.end, this.g.end, this.b.end);
-
- if ( highlight )
- {
- if ( this.searchResultChildren() )
- {
- label = label + searchResultString(this.searchResultChildren());
- }
-
- keyNameWidth = measureText(label, bold);
- }
- else
- {
- keyNameWidth = this.keyNameWidth;
- }
- }
-
- var textLeft = boxLeft - keyBuffer - keyNameWidth - fontSize / 2;
- var labelLeft = textLeft;
-
- if ( labelLeft > keyMinTextLeft - fontSize / 2 )
- {
- keyMinTextLeft -= fontSize / 2;
-
- if ( keyMinTextLeft < centerX - gRadius + fontSize / 2 )
- {
- keyMinTextLeft = centerX - gRadius + fontSize / 2;
- }
-
- labelLeft = keyMinTextLeft;
- }
-
- var lineX = new Array();
- var lineY = new Array();
-
- var bendRadius;
- var keyAngle = Math.atan((textY - centerY) / (labelLeft - centerX));
- var arcAngle;
-
- if ( keyAngle < 0 )
- {
- keyAngle += Math.PI;
- }
-
- if ( keyMinAngle == 0 || angle < keyMinAngle )
- {
- keyMinAngle = angle;
- }
-
- if ( angle > Math.PI && keyMinAngle > Math.PI )
- {
- // allow lines to come underneath the chart
-
- angle -= Math.PI * 2;
- }
-
- lineX.push(Math.cos(angle) * gRadius);
- lineY.push(Math.sin(angle) * gRadius);
-
- if ( angle < keyAngle && textY > centerY + Math.sin(angle) * (gRadius + buffer * (currentKey - 1) / (keys + 1) / 2 + buffer / 2) )
- {
- bendRadius = gRadius + buffer - buffer * currentKey / (keys + 1) / 2;
- }
- else
- {
- bendRadius = gRadius + buffer * currentKey / (keys + 1) / 2 + buffer / 2;
- }
-
- var outside =
- Math.sqrt
- (
- Math.pow(labelLeft - centerX, 2) +
- Math.pow(textY - centerY, 2)
- ) > bendRadius;
-
- if ( ! outside )
- {
- arcAngle = Math.asin((textY - centerY) / bendRadius);
-
- keyMinTextLeft = min(keyMinTextLeft, centerX + bendRadius * Math.cos(arcAngle) - fontSize / 2);
-
- if ( labelLeft < textLeft && textLeft > centerX + bendRadius * Math.cos(arcAngle) )
- {
- lineX.push(textLeft - centerX);
- lineY.push(textY - centerY);
- }
- }
- else
- {
- keyMinTextLeft = min(keyMinTextLeft, labelLeft - fontSize / 2);
-
- if ( angle < keyAngle )
- {
- // flip everything over y = x
- //
- arcAngle = Math.PI / 2 - keyLineAngle
- (
- Math.PI / 2 - angle,
- Math.PI / 2 - keyAngle,
- bendRadius,
- textY - centerY,
- labelLeft - centerX,
- lineY,
- lineX
- );
-
- }
- else
- {
- arcAngle = keyLineAngle
- (
- angle,
- keyAngle,
- bendRadius,
- labelLeft - centerX,
- textY - centerY,
- lineX,
- lineY
- );
- }
- }
-
- if ( labelLeft > centerX + bendRadius * Math.cos(arcAngle) ||
- textY > centerY + bendRadius * Math.sin(arcAngle) + .01)
- // if ( outside || )
- {
- lineX.push(labelLeft - centerX);
- lineY.push(textY - centerY);
-
- if ( textLeft != labelLeft )
- {
- lineX.push(textLeft - centerX);
- lineY.push(textY - centerY);
- }
- }
-
- context.globalAlpha = this.alphaWedge.current();
-
- if ( snapshotMode )
- {
- var labelSVG;
-
- if ( this == selectedNode )
- {
- labelSVG =
- this.getUnclassifiedText() +
- spacer() +
- this.getUnclassifiedPercentage();
- }
- else
- {
- labelSVG = this.name + spacer() + this.getPercentage() + '%';
- }
-
- svg +=
- '<rect fill="' + color + '" ' +
- 'x="' + boxLeft + '" y="' + offset +
- '" width="' + keySize + '" height="' + keySize + '"/>';
-
- if ( patternAlpha )
- {
- svg +=
- '<rect fill="url(#hiddenPattern)" style="stroke:none" ' +
- 'x="' + boxLeft + '" y="' + offset +
- '" width="' + keySize + '" height="' + keySize + '"/>';
- }
-
- svg +=
- '<path class="line' +
- (highlight ? ' highlight' : '') +
- '" d="M ' + (lineX[0] + centerX) + ',' +
- (lineY[0] + centerY);
-
- if ( angle != arcAngle )
- {
- svg +=
- ' L ' + (centerX + bendRadius * Math.cos(angle)) + ',' +
- (centerY + bendRadius * Math.sin(angle)) +
- ' A ' + bendRadius + ',' + bendRadius + ' 0 ' +
- '0,' + (angle > arcAngle ? '0' : '1') + ' ' +
- (centerX + bendRadius * Math.cos(arcAngle)) + ',' +
- (centerY + bendRadius * Math.sin(arcAngle));
- }
-
- for ( var i = 1; i < lineX.length; i++ )
- {
- svg +=
- ' L ' + (centerX + lineX[i]) + ',' +
- (centerY + lineY[i]);
- }
-
- svg += '"/>';
-
- if ( highlight )
- {
- if ( this.searchResultChildren() )
- {
- labelSVG = labelSVG + searchResultString(this.searchResultChildren());
- }
-
- drawBubbleSVG
- (
- boxLeft - keyBuffer - keyNameWidth - fontSize / 2,
- textY - fontSize,
- keyNameWidth + fontSize,
- fontSize * 2,
- fontSize,
- 0
- );
-
- if ( this.isSearchResult )
- {
- drawSearchHighlights
- (
- label,
- boxLeft - keyBuffer - keyNameWidth,
- textY,
- 0
- )
- }
- }
-
- svg += svgText(labelSVG, boxLeft - keyBuffer, textY, 'end', bold);
- }
- else
- {
- context.fillStyle = color;
- context.translate(-centerX, -centerY);
- context.strokeStyle = 'black';
- context.globalAlpha = 1;//this.alphaWedge.current();
-
- context.fillRect(boxLeft, offset, keySize, keySize);
-
- if ( patternAlpha )
- {
- context.globalAlpha = patternAlpha;
- context.fillStyle = hiddenPattern;
-
- // make clipping box for Firefox performance
- context.beginPath();
- context.moveTo(boxLeft, offset);
- context.lineTo(boxLeft + keySize, offset);
- context.lineTo(boxLeft + keySize, offset + keySize);
- context.lineTo(boxLeft, offset + keySize);
- context.closePath();
- context.save();
- context.clip();
-
- context.fillRect(boxLeft, offset, keySize, keySize);
- context.fillRect(boxLeft, offset, keySize, keySize);
-
- context.restore(); // remove clipping region
- }
-
- if ( highlight )
- {
- this.setHighlightStyle();
- context.fillRect(boxLeft, offset, keySize, keySize);
- }
- else
- {
- context.lineWidth = thinLineWidth;
- }
-
- context.strokeRect(boxLeft, offset, keySize, keySize);
-
- if ( lineX.length )
- {
- context.beginPath();
- context.moveTo(lineX[0] + centerX, lineY[0] + centerY);
-
- context.arc(centerX, centerY, bendRadius, angle, arcAngle, angle > arcAngle);
-
- for ( var i = 1; i < lineX.length; i++ )
- {
- context.lineTo(lineX[i] + centerX, lineY[i] + centerY);
- }
-
- context.globalAlpha = this == selectedNode ?
- this.children[0].alphaWedge.current() :
- this.alphaWedge.current();
- context.lineWidth = highlight ? highlightLineWidth : thinLineWidth;
- context.stroke();
- context.globalAlpha = 1;
- }
-
- if ( highlight )
- {
- drawBubbleCanvas
- (
- boxLeft - keyBuffer - keyNameWidth - fontSize / 2,
- textY - fontSize,
- keyNameWidth + fontSize,
- fontSize * 2,
- fontSize,
- 0
- );
-
- if ( this.isSearchResult )
- {
- drawSearchHighlights
- (
- label,
- boxLeft - keyBuffer - keyNameWidth,
- textY,
- 0
- )
- }
- }
-
- drawText(label, boxLeft - keyBuffer, offset + keySize / 2, 0, 'end', bold);
-
- context.translate(centerX, centerY);
- }
-
- currentKey++;
- }
-
- this.drawLabel = function(angle, bubble, bold, selected, radial)
- {
- if ( context.globalAlpha == 0 )
- {
- return;
- }
-
- var innerText;
- var label;
- var radius;
-
- if ( radial )
- {
- radius = (this.radiusInner.current() + 1) * gRadius / 2;
- }
- else
- {
- radius = this.labelRadius.current() * gRadius;
- }
-
- if ( radial && (selected || bubble ) )
- {
- var percentage = this.getPercentage();
- innerText = percentage + '%';
- }
-
- if
- (
- ! radial &&
- this != selectedNode &&
- ! bubble &&
- ( !zoomOut || this != selectedNodeLast)
- )
- {
- label = this.shortenLabel();
- }
- else
- {
- label = this.name;
- }
-
- var flipped = drawTextPolar
- (
- label,
- innerText,
- angle,
- radius,
- radial,
- bubble,
- bold,
- // this.isSearchResult && this.shouldAddSearchResultsString() && (!selected || this == selectedNode || highlight),
- this.isSearchResult && (!selected || this == selectedNode || bubble),
- (this.hideAlone || !selected || this == selectedNode ) ? this.searchResultChildren() : 0
- );
-
- var depth = this.getDepth() - selectedNode.getDepth() + 1;
-
- if
- (
- ! radial &&
- ! bubble &&
- this != selectedNode &&
- this.angleEnd.end != this.angleStart.end &&
- nLabelOffsets[depth - 2] > 2 &&
- this.labelWidth.current() > (this.angleEnd.end - this.angleStart.end) * Math.abs(radius) &&
- ! ( zoomOut && this == selectedNodeLast ) &&
- this.labelRadius.end > 0
- )
- {
- // name extends beyond wedge; draw tick mark towards the central
- // radius for easier identification
-
- var radiusCenter = compress ?
- (compressedRadii[depth - 1] + compressedRadii[depth - 2]) / 2 :
- (depth - .5) * nodeRadius;
-
- if ( this.labelRadius.end > radiusCenter )
- {
- if ( flipped )
- {
- drawTick(radius - tickLength * 1.4 , tickLength, angle);
- }
- else
- {
- drawTick(radius - tickLength * 1.7, tickLength, angle);
- }
- }
- else
- {
- if ( flipped )
- {
- drawTick(radius + tickLength * .7, tickLength, angle);
- }
- else
- {
- drawTick(radius + tickLength * .4, tickLength, angle);
- }
- }
- }
- }
-
- this.drawLines = function(angleStart, angleEnd, radiusInner, drawRadial, selected)
- {
- if ( snapshotMode )
- {
- if ( this != selectedNode)
- {
- if ( angleEnd == angleStart + Math.PI * 2 )
- {
- // fudge to prevent overlap, which causes arc ambiguity
- //
- angleEnd -= .1 / gRadius;
- }
-
- var longArc = angleEnd - angleStart > Math.PI ? 1 : 0;
-
- var x1 = centerX + radiusInner * Math.cos(angleStart);
- var y1 = centerY + radiusInner * Math.sin(angleStart);
-
- var x2 = centerX + gRadius * Math.cos(angleStart);
- var y2 = centerY + gRadius * Math.sin(angleStart);
-
- var x3 = centerX + gRadius * Math.cos(angleEnd);
- var y3 = centerY + gRadius * Math.sin(angleEnd);
-
- var x4 = centerX + radiusInner * Math.cos(angleEnd);
- var y4 = centerY + radiusInner * Math.sin(angleEnd);
-
- if ( this.alphaArc.end )
- {
- var dArray =
- [
- " M ", x4, ",", y4,
- " A ", radiusInner, ",", radiusInner, " 0 ", longArc,
- " 0 ", x1, ",", y1
- ];
-
- svg += '<path class="line" d="' + dArray.join('') + '"/>';
- }
-
- if ( drawRadial && this.alphaLine.end )
- {
- svg += '<line x1="' + x3 + '" y1="' + y3 + '" x2="' + x4 + '" y2="' + y4 + '"/>';
- }
- }
- }
- else
- {
- context.lineWidth = thinLineWidth;
- context.strokeStyle = 'black';
- context.beginPath();
- context.arc(0, 0, radiusInner, angleStart, angleEnd, false);
- context.globalAlpha = this.alphaArc.current();
- context.stroke();
-
- if ( drawRadial )
- {
- var x1 = radiusInner * Math.cos(angleEnd);
- var y1 = radiusInner * Math.sin(angleEnd);
- var x2 = gRadius * Math.cos(angleEnd);
- var y2 = gRadius * Math.sin(angleEnd);
-
- context.beginPath();
- context.moveTo(x1, y1);
- context.lineTo(x2, y2);
-
- // if ( this.getCollapse() )//( selected && this != selectedNode )
- {
- context.globalAlpha = this.alphaLine.current();
- }
-
- context.stroke();
- }
- }
- }
-
- this.drawMap = function(child)
- {
- if ( this.parent )
- {
- this.parent.drawMap(child);
- }
-
- if ( this.getCollapse() && this != child || this == focusNode )
- {
- return;
- }
-
- var angleStart =
- (child.baseMagnitude - this.baseMagnitude) / this.magnitude * Math.PI * 2 +
- rotationOffset;
- var angleEnd =
- (child.baseMagnitude - this.baseMagnitude + child.magnitude) /
- this.magnitude * Math.PI * 2 +
- rotationOffset;
-
- var box = this.getMapPosition();
-
- context.save();
- context.fillStyle = 'black';
- context.textAlign = 'end';
- context.textBaseline = 'middle';
-
- var textX = box.x - mapRadius - mapBuffer;
- var percentage = getPercentage(child.magnitude / this.magnitude);
-
- var highlight = this == selectedNode || this == highlightedNode;
-
- if ( highlight )
- {
- context.font = fontBold;
- }
- else
- {
- context.font = fontNormal;
- }
-
- context.fillText(percentage + '% of', textX, box.y - mapRadius / 3);
- context.fillText(this.name, textX, box.y + mapRadius / 3);
-
- if ( highlight )
- {
- context.font = fontNormal;
- }
-
- if ( this == highlightedNode && this != selectedNode )
- {
- context.fillStyle = 'rgb(245, 245, 245)';
- // context.fillStyle = 'rgb(200, 200, 200)';
- }
- else
- {
- context.fillStyle = 'rgb(255, 255, 255)';
- }
-
- context.beginPath();
- context.arc(box.x, box.y, mapRadius, 0, Math.PI * 2, true);
- context.closePath();
- context.fill();
-
- if ( this == selectedNode )
- {
- context.lineWidth = 1;
- context.fillStyle = 'rgb(100, 100, 100)';
- }
- else
- {
- if ( this == highlightedNode )
- {
- context.lineWidth = .2;
- context.fillStyle = 'rgb(190, 190, 190)';
- }
- else
- {
- context.lineWidth = .2;
- context.fillStyle = 'rgb(200, 200, 200)';
- }
- }
-
- var maxDepth = this.getMaxDepth();
-
- if ( ! compress && maxDepth > maxPossibleDepth + this.getDepth() - 1 )
- {
- maxDepth = maxPossibleDepth + this.getDepth() - 1;
- }
-
- if ( this.getDepth() < selectedNode.getDepth() )
- {
- if ( child.getDepth() - 1 >= maxDepth )
- {
- maxDepth = child.getDepth();
- }
- }
-
- var radiusInner;
-
- if ( compress )
- {
- radiusInner = 0;
- // Math.atan(child.getDepth() - this.getDepth()) /
- // Math.PI * 2 * .9;
- }
- else
- {
- radiusInner =
- (child.getDepth() - this.getDepth()) /
- (maxDepth - this.getDepth() + 1);
- }
-
- context.stroke();
- context.beginPath();
-
- if ( radiusInner == 0 )
- {
- context.moveTo(box.x, box.y);
- }
- else
- {
- context.arc(box.x, box.y, mapRadius * radiusInner, angleEnd, angleStart, true);
- }
-
- context.arc(box.x, box.y, mapRadius, angleStart, angleEnd, false);
- context.closePath();
- context.fill();
-
- if ( this == highlightedNode && this != selectedNode )
- {
- context.lineWidth = 1;
- context.stroke();
- }
-
- context.restore();
- }
-
- this.drawReferenceRings = function(childRadiusInner)
- {
- if ( snapshotMode )
- {
- svg +=
- '<circle cx="' + centerX + '" cy="' + centerY +
- '" r="' + childRadiusInner + '"/>';
- svg +=
- '<circle cx="' + centerX + '" cy="' + centerY +
- '" r="' + gRadius + '"/>';
- }
- else
- {
- context.globalAlpha = 1 - this.alphaLine.current();//this.getUncollapsed().alphaLine.current();
- context.beginPath();
- context.arc(0, 0, childRadiusInner, 0, Math.PI * 2, false);
- context.stroke();
- context.beginPath();
- context.arc(0, 0, gRadius, 0, Math.PI * 2, false);
- context.stroke();
- }
- }
-
- this.getCollapse = function()
- {
- return (
- collapse &&
- this.collapse &&
- this.depth != maxAbsoluteDepth
- );
- }
-
- this.getDepth = function()
- {
- if ( collapse )
- {
- return this.depthCollapsed;
- }
- else
- {
- return this.depth;
- }
- }
-
- this.getMagnitude = function()
- {
- return this.attributes[magnitudeIndex][currentDataset];
- }
-
- this.getMapPosition = function()
- {
- return {
- x : (details.offsetLeft + details.clientWidth - mapRadius),
- y : ((focusNode.getDepth() - this.getDepth()) *
- (mapBuffer + mapRadius * 2) - mapRadius) +
- details.clientHeight + details.offsetTop
- };
- }
-
- this.getMaxDepth = function(limit)
- {
- var max;
-
- if ( collapse )
- {
- return this.maxDepthCollapsed;
- }
- else
- {
- if ( this.maxDepth > maxAbsoluteDepth )
- {
- return maxAbsoluteDepth;
- }
- else
- {
- return this.maxDepth;
- }
- }
- }
-
- this.getData = function(index, summary)
- {
- var files = new Array();
-
- if
- (
- this.attributes[index] != null &&
- this.attributes[index][currentDataset] != null &&
- this.attributes[index][currentDataset] != ''
- )
- {
- files.push
- (
- document.location +
- '.files/' +
- this.attributes[index][currentDataset]
- );
- }
-
- if ( summary )
- {
- for ( var i = 0; i < this.children.length; i++ )
- {
- files = files.concat(this.children[i].getData(index, true));
- }
- }
-
- return files;
- }
-
- this.getList = function(index, summary)
- {
- var list;
-
- if
- (
- this.attributes[index] != null &&
- this.attributes[index][currentDataset] != null
- )
- {
- list = this.attributes[index][currentDataset];
- }
- else
- {
- list = new Array();
- }
-
- if ( summary )
- {
- for ( var i = 0; i < this.children.length; i++ )
- {
- list = list.concat(this.children[i].getList(index, true));
- }
- }
-
- return list;
- }
-
- this.getParent = function()
- {
- // returns parent, accounting for collapsing or 0 if doesn't exist
-
- var parent = this.parent;
-
- while ( parent != 0 && parent.getCollapse() )
- {
- parent = parent.parent;
- }
-
- return parent;
- }
-
- this.getPercentage = function()
- {
- return getPercentage(this.magnitude / selectedNode.magnitude);
- }
-
- this.getUnclassifiedPercentage = function()
- {
- var lastChild = this.children[this.children.length - 1];
-
- return getPercentage
- (
- (
- this.baseMagnitude +
- this.magnitude -
- lastChild.magnitude -
- lastChild.baseMagnitude
- ) / this.magnitude
- ) + '%';
- }
-
- this.getUnclassifiedText = function()
- {
- return '[unassigned '+ this.name + ']';
- }
-
- this.getUncollapsed = function()
- {
- // recurse through collapsed children until uncollapsed node is found
-
- if ( this.getCollapse() )
- {
- return this.children[0].getUncollapsed();
- }
- else
- {
- return this;
- }
- }
-
- this.hasChildren = function()
- {
- return this.children.length && this.depth < maxAbsoluteDepth && this.magnitude;
- }
-
- this.hasParent = function(parent)
- {
- if ( this.parent )
- {
- if ( this.parent == parent )
- {
- return true;
- }
- else
- {
- return this.parent.hasParent(parent);
- }
- }
- else
- {
- return false;
- }
- }
-
- this.maxVisibleDepth = function(maxDepth)
- {
- var childInnerRadius;
- var depth = this.getDepth() - selectedNode.getDepth() + 1;
- var currentMaxDepth = depth;
-
- if ( this.hasChildren() && depth < maxDepth)
- {
- var lastChild = this.children[this.children.length - 1];
-
- if ( this.name == 'Pseudomonadaceae' )
- {
- var x = 3;
- }
-
- if
- (
- lastChild.baseMagnitude + lastChild.magnitude <
- this.baseMagnitude + this.magnitude
- )
- {
- currentMaxDepth++;
- }
-
- if ( compress )
- {
- childInnerRadius = compressedRadii[depth - 1];
- }
- else
- {
- childInnerRadius = (depth) / maxDepth;
- }
-
- for ( var i = 0; i < this.children.length; i++ )
- {
- if
- (//true ||
- this.children[i].magnitude *
- angleFactor *
- (childInnerRadius + 1) *
- gRadius >=
- minWidth()
- )
- {
- var childMaxDepth = this.children[i].maxVisibleDepth(maxDepth);
-
- if ( childMaxDepth > currentMaxDepth )
- {
- currentMaxDepth = childMaxDepth;
- }
- }
- }
- }
-
- return currentMaxDepth;
- }
-
- this.resetLabelWidth = function()
- {
- var nameWidthOld = this.nameWidth;
-
- if ( ! this.radial )//&& fontSize != fontSizeLast )
- {
- var dim = context.measureText(this.name);
- this.nameWidth = dim.width;
- }
-
- if ( fontSize != fontSizeLast && this.labelWidth.end == nameWidthOld * labelWidthFudge )
- {
- // font size changed; adjust start of tween to match
-
- this.labelWidth.start = this.nameWidth * labelWidthFudge;
- }
- else
- {
- this.labelWidth.start = this.labelWidth.current();
- }
-
- this.labelWidth.end = this.nameWidth * labelWidthFudge;
- }
-
- this.restrictLabelWidth = function(width)
- {
- if ( width < this.labelWidth.end )
- {
- this.labelWidth.end = width;
- }
- }
-
- this.search = function()
- {
- this.isSearchResult = false;
- this.searchResults = 0;
-
- if
- (
- ! this.getCollapse() &&
- search.value != '' &&
- this.name.toLowerCase().indexOf(search.value.toLowerCase()) != -1
- )
- {
- this.isSearchResult = true;
- this.searchResults = 1;
- nSearchResults++;
- }
-
- for ( var i = 0; i < this.children.length; i++ )
- {
- this.searchResults += this.children[i].search();
- }
-
- return this.searchResults;
- }
-
- this.searchResultChildren = function()
- {
- if ( this.isSearchResult )
- {
- return this.searchResults - 1;
- }
- else
- {
- return this.searchResults;
- }
- }
-
- this.setDepth = function(depth, depthCollapsed)
- {
- this.depth = depth;
- this.depthCollapsed = depthCollapsed;
-
- if
- (
- this.children.length == 1 &&
- // this.magnitude > 0 &&
- this.children[0].magnitude == this.magnitude &&
- ( head.children.length > 1 || this.children[0].children.length )
- )
- {
- this.collapse = true;
- }
- else
- {
- this.collapse = false;
- depthCollapsed++;
- }
-
- for ( var i = 0; i < this.children.length; i++ )
- {
- this.children[i].setDepth(depth + 1, depthCollapsed);
- }
- }
-
- this.setHighlightStyle = function()
- {
- context.lineWidth = highlightLineWidth;
-
- if ( this.hasChi…
Large files files are truncated, but you can click here to view the full file